Compare commits

...
301 Commits
Author SHA1 Message Date
Wilson Snyder 935032366f Version bump 2021-12-05 11:10:19 -05:00
Yutetsu TAKATSUKASA 1160dddc6d Fix bit op tree optimization (#3221)
* Add a test to reproduce #3197

* Fix #3197. Optimize correctly even if a variable is >32

* Quick exit instead of continue. No functional change is intended.

* Delete comment-out line.

* update per review comment
2021-11-30 23:20:00 +09:00
Wilson Snyder 293a5f402b Fix timescale portability on Arm64 (#3222). 2021-11-28 15:47:19 -05:00
Wilson Snyder ca09f73446 Tests: Fix open test on some automount cloud providers 2021-11-28 15:40:35 -05:00
Wilson Snyder e814855357 Tests: Skip gdb tests if no gdb 2021-11-28 15:33:33 -05:00
Wilson Snyder 692306ef44 Optimize $random concatenates/selects (#3114). 2021-11-28 14:17:28 -05:00
Wilson Snyder ffdc0afe5f Disable unintended debug message 2021-11-28 14:10:50 -05:00
Wilson Snyder 98037cad56 Internals: Optimize VL_RANDOM to have unclean output 2021-11-28 14:00:19 -05:00
Wilson Snyder 61e3536163 Internals: Remove some unused arguments. 2021-11-28 13:44:16 -05:00
Wilson Snyder a1a186a86c Internals: Remove some unused arguments. 2021-11-28 13:07:37 -05:00
Wilson Snyder 04e0c7e4f1 Support tracing through --hierarchical/--lib-create libraries (#3200). 2021-11-27 17:07:27 -05:00
Wilson Snyder 833686446c Commentary, ChangeLog up to date 2021-11-27 09:05:51 -05:00
Wilson Snyder c6dae40cf6 Support task name in $display %m (#3211). 2021-11-26 20:38:48 -05:00
Wilson Snyder cd737065f2 Internals: More const. No functional change intended. 2021-11-26 17:55:36 -05:00
Wilson Snyder 010084201a Internals: Remove dead code. 2021-11-26 16:15:08 -05:00
Wilson Snyder 60f7eff701 Remove dead statistic. 2021-11-26 16:10:16 -05:00
Wilson Snyder d1862e25eb Internals: Cleanup some constructor-constructor assigns. No functional change intended. 2021-11-26 16:05:07 -05:00
Wilson Snyder b0018fd680 Internals: Remove dead output variable. No functional change. 2021-11-26 15:13:36 -05:00
Wilson Snyder 2742a8c813 Internals: No need for variable name on immediate visitors. No functional change intended. 2021-11-26 10:52:45 -05:00
Wilson Snyder 05e12ab60e Internals: More const. No functional change intended. 2021-11-26 10:52:45 -05:00
Michaël Lefebvre 9bda2cb4ad Fix some SliceSels not being constants (#3186) (#3218). 2021-11-26 10:51:11 -05:00
Wilson Snyder 393b9e435d Internals: Revert previous commit const for clang. 2021-11-25 09:47:06 -05:00
Wilson Snyder e87a726989 Internals: More const. No functional change intended. 2021-11-25 09:05:50 -05:00
Wilson Snyder 62387a0e32 Fix display of empty string constant (#3207) (#3215). 2021-11-25 08:03:27 -05:00
Wilson Snyder 31079ca8b5 Fix $size on dynamic strings (#3216). 2021-11-25 07:50:47 -05:00
Wilson Snyder 4184ad1921 Commentary 2021-11-23 18:27:40 -05:00
Wilson Snyder e7ebe0e280 Fix $fopen etc on integer arrays (#3214). 2021-11-23 18:22:16 -05:00
Wilson Snyder c14bbb9421 Fix incorrect width after and-or optimization (#3208). 2021-11-23 18:15:21 -05:00
Wilson Snyder b1b92b7dd4 Fix hang on recursive definition error (#3199). 2021-11-23 07:27:41 -05:00
Julie Schwartz a14394dbb5 Commentary: remove duplicate/wrong change-log entry (#3212) 2021-11-18 05:15:02 -05:00
Wilson Snyder 96b7831243 Commentary: C++14 moves to 2023. 2021-11-17 18:56:11 -05:00
Wilson Snyder 1e440765c0 Commentary, about previous commit: Fix display of signed without format (#3204). 2021-11-17 18:52:53 -05:00
Wilson Snyder 2ccf49031b Fix $display of signed/unsigned without format (#3207). 2021-11-17 18:50:52 -05:00
Wilson Snyder 0abc856be9 Fix %0 format on $value$plusargs. 2021-11-17 17:54:07 -05:00
Wilson Snyder d2a8fa7440 Fix display of empty string constant (#3207). 2021-11-17 17:46:08 -05:00
Wilson Snyder 106d635d0f Fix vluint64 portability (#3150). 2021-11-17 07:10:48 -05:00
Wilson Snyder cff9f8143e Commentary 2021-11-17 07:06:43 -05:00
Wilson Snyder cfa401e01d Internals: Add lib-create const and improve comments. 2021-11-14 12:01:02 -05:00
Wilson Snyder d6195e354f Add some missing COLD attributes. 2021-11-14 11:05:20 -05:00
Wilson Snyder c0892dd11c When using --lib-create, disable tracing hash/internals. Partial (#3200). 2021-11-14 10:16:59 -05:00
Wilson Snyder 899de9a282 Add --lib-create, similar to --protect-lib but without protections (#3200). 2021-11-14 09:39:31 -05:00
Wilson Snyder 4b593f8eb3 Commentary 2021-11-14 09:01:03 -05:00
Wilson Snyder 28b6e79cf7 Tests: Rename t_lib_prot tests in prep for new options. 2021-11-14 08:58:04 -05:00
Wilson Snyder fe5822ba54 Commentary 2021-11-13 15:01:27 -05:00
Wilson Snyder c496649c49 Internals: Remove unused DfaGraph code. 2021-11-13 14:48:21 -05:00
Wilson Snyder 37e3c6da70 Internals: Add more const. No functional change intended. 2021-11-13 13:50:44 -05:00
Wilson Snyder 4cb5c1e1db Internals: More const. No functional change. 2021-11-13 11:38:12 -05:00
Wilson Snyder b7a7504ba5 Internals: More obvious debug filename. 2021-11-13 11:35:03 -05:00
Wilson Snyder 3a5cbd5b67 Internals: Untabify some embedded tabs. 2021-11-13 10:46:25 -05:00
Wilson Snyder 27883b52d6 Internals: Add const 2021-11-13 10:42:26 -05:00
Wilson Snyder b95ee84343 Commentary 2021-11-13 10:41:00 -05:00
Geza Lore 185e5d8f42 Make 'bit', 'logic' and 'time' types unsigned by default
IEEE 1800-2017 6.11.3 says these types are unsigned. Until now these
types were treated as not having a signedness (NOSIGN), and nodes having
these types were later resolved by V3Width to be unsigned. This is a bit
problematic when creating nodes of these types after V3Width. Treating
these types as unsigned from the get go is fine, and actually improves
generated code slightly.
2021-11-09 21:54:21 +00:00
Geza Lore 987ce927eb Remove unused code. No functional change. 2021-11-09 19:46:19 +00:00
Yutetsu TAKATSUKASA b08c694cd6 Fix wrong bit op tree optimization (#3185)
* Add a test to reproduce bug3182. Run the same HDL with -Oo to confirm the result is same.

* Hopefully fix #3182. The result can be 0 only when polarity is true (no AstNot is found during traversal).
2021-11-06 12:31:50 +09:00
Geza Lore e69a8e838d Improve memory usage of V3Partition. Only performance change intended. (#3192) 2021-11-05 22:08:54 -04:00
Wilson Snyder 61612582e6 Improve memory usage of V3Partition. Only performance change intended. 2021-11-04 07:39:28 -04:00
Wilson Snyder 8b00939f0c Improve performance of V3Scoreboard. Only performance change intended. 2021-11-03 22:16:18 -04:00
Wilson Snyder da5644211f Improve memory usage of V3Partition. Only performance change intended. 2021-11-03 22:01:40 -04:00
Wilson Snyder c1d7bfa617 Internals: Skip some asserts in fastpath partitioning. 2021-11-03 19:19:23 -04:00
Wilson Snyder c26ce25cea Internals: Add more const. No functional change. 2021-11-03 17:49:19 -04:00
Wilson Snyder 758264dc77 Fix nested generate if genblk naming (#3189). 2021-11-01 08:59:00 -04:00
Wilson Snyder 304697d133 Commentary 2021-11-01 08:57:43 -04:00
Teng Huang fdbf465eda Fix array method names with parens (#3181) (#3183) 2021-10-26 12:20:45 -04:00
Wilson Snyder 90102d9867 Commentary 2021-10-25 21:02:02 -04:00
Wilson Snyder a1bb471aca Internals: Iterate sentree in clocker to avoid confusing iterateChildren. No functional change intended. 2021-10-25 20:19:49 -04:00
Wilson Snyder 4ef37d9411 Commentary 2021-10-25 19:53:41 -04:00
Yutetsu TAKATSUKASA f3b10df454 Skip merging assign statements if a variable is marked split_var to fix #3177 (#3179)
* add tests to reproduce #3177.

Any random test circuits can be added to t_split_var_4.v later because it uses CRC to check the result while
t_split_var_0.v has just barrel shifters.

* Fix #3177. Don't merge assign statements if a variable is marked split_var.
2021-10-25 20:56:59 +09:00
Geza Lore f5a226a183 Partial clean up of V3Order. No functional change intended.
This is a partial cleanup of V3Order with the aim of increasing clarity:
- Split the initial OrderGraph building and the actual ordering process
into separate classes (OrderVisitor -> OrderBuildVisitor + OrderProcess)
- Remove all the historical cruft from the graph building phase (now in
OrderBuildVisitor), and add more assertions for assumptions.
- Change the dot styling of OrderGraph to use shapes and more easily
distinguishable colors.
- Expand vague comments, remove incorrect comments, and add more.
- Replace some old code with cleaner C++11 constructs.
- Move code about a bit so logically connected sections are closer to
each other, scope some definitions where they are used rather than file
scope.
- The actual ordering process (now in OrderProcess) is still largely
unchanged.

The generated code is identical to before (within the limits of the
exiting non-determinism).
2021-10-24 16:49:05 +01:00
Geza Lore 5515eed0b6 Make AstTopScope accessible from AstNetlist
AstTopScope is a singleton node and making it accessible without a
traversal simplifies some code and decouple some other.
2021-10-24 13:00:29 +01:00
Geza Lore 381c87a1eb Remove VN_CAST_CONST and VN_AS_CONST.
The _CONST suffix on these macros is only lexical notation, pointer
constness can be preserved by overloading the underlying
implementations appropriately. Given that the compiler will catch
invalid const usage (trying to assign a non-const pointer to a const
pointer variable, etc.), and that the declarations of symbols should
make their constness obvious, I see no reason to keep the _CONST
flavours.
2021-10-24 11:43:48 +01:00
github action 20e896ec21 Apply 'make format' 2021-10-22 18:51:57 +00:00
Geza Lore 70603bb752 Add static assertions for unnecessary VN_IS/VN_AS/VN_CAST
Fail at compile time if the result of these macros can be statically
determined (i.e.: they aways succeed or always fail). Remove unnecessary
casts discovered. No functional change.
2021-10-22 19:39:24 +01:00
Geza Lore 7cb6dc664e Generate YYSTYPE members with astgen
In preparation for strengthening Ast type system. No functional change.
2021-10-22 19:27:32 +01:00
Geza Lore dae9fa5053 Use VN_AS wherever possible and obvious. No functional change. 2021-10-22 14:06:00 +01:00
Wilson Snyder aa1a0b0f13 Tests: Fix numactl on WSL2 2021-10-19 21:24:22 -04:00
Wilson Snyder 371bf01957 Tests: Fix numactl on WSL2 2021-10-19 21:21:52 -04:00
Wilson Snyder ef7759a522 Commentary re: WSL2 2021-10-19 20:34:13 -04:00
Wilson Snyder 0ef9087f89 Commentary 2021-10-17 14:51:50 -04:00
Wilson Snyder 00059987a1 devel release 2021-10-17 14:47:19 -04:00
Wilson Snyder 8f090176a6 Version bump 2021-10-17 14:36:23 -04:00
Wilson Snyder 43e8263d1f Commentary 2021-10-17 14:35:34 -04:00
Geza Lore ac622f9db8 Introduce VN_AS for downcasting to known AstNode subtype.
VN_AS should be used over VN_CAST in code where the author knows up
front (i.e.: statically) what the true type of the node is. This has
multiple benefits over VN_CAST:
- In the debug build: Asserts node type is as expected
- In the optimized build: It is faster as no superfluous type test
- And (I would argue most importantly) it documents intent in the code

No current instances of VN_CAST changed in this patch
2021-10-16 23:52:31 +01:00
Wilson Snyder 749a66b7a2 Add assertion on mix of combo/posedge (#3170) 2021-10-16 18:14:32 -04:00
Geza Lore fc80ace6e4 Reduce size of astgen generated implementation boilerplate.
Rely on C++ templates instead for simplicity. No functional change.
2021-10-16 20:15:51 +01:00
Wilson Snyder b8f6b262e4 Fix false EOFNEWLINE warning when DOS carriage returns present (#3171). 2021-10-13 21:21:51 -04:00
Wilson Snyder b64e89f7ed Fix calling new with arguments in same class (#3166). 2021-10-12 21:22:59 -04:00
Wilson Snyder c3bec344a4 Fix missing install of vl_file_copy/vl_hier_graph (#3165). 2021-10-12 20:20:45 -04:00
Iru Cai 9731ec76e3 Fix $urandom_range when the range is 0 ... UINT_MAX (#3161) 2021-10-11 06:45:19 -06:00
Matthew Ballance a9646cf45c Fix constructor-parameter argument comma-separation in C++ (#3162) 2021-10-09 17:19:31 -06:00
Wilson Snyder 5f597dd9fc Tests: Improve internal coverage holes 2021-10-05 22:02:37 -04:00
Wilson Snyder 7d57c3ca78 Internals: Remove dead code. 2021-10-05 22:02:23 -04:00
Wilson Snyder 4739956cfe Internals: Add missing const. No functional change. 2021-10-05 21:20:22 -04:00
Wilson Snyder e202a6324a Tests: Improve internal coverage holes 2021-10-05 21:09:57 -04:00
Wilson Snyder 66d70c8b37 Internal coverage: Fix some test runs having conflicting sources. 2021-10-05 20:22:29 -04:00
Wilson Snyder 02aa639425 Internals: Add some const etc. No functional change. 2021-10-05 19:42:31 -04:00
Wilson Snyder 55da66164b Fix verilator_gantt time on Arm. 2021-10-04 22:13:34 -04:00
Wilson Snyder 2560fc867f verilator_gantt: Fix reading broken /cpu/procinfo reports 2021-10-02 11:10:43 -04:00
Wilson Snyder f5c9deecb8 Tests: Add verilator_gantt no-proc test. 2021-10-02 10:05:59 -04:00
github action 20af8a36a7 Apply 'make format' 2021-09-28 11:29:21 +00:00
Zhanglei Wang 1c1c805b07 Fix internal error on bad widths (#3140) (#3145) 2021-09-28 07:28:02 -04:00
Wilson Snyder 9029da5ab8 Add profile-guided optmization of mtasks (#3150). 2021-09-26 22:51:11 -04:00
Wilson Snyder def7c8fe4d Commentary 2021-09-26 16:21:58 -04:00
Wilson Snyder 9184c4f501 Fix cygwin compile error due to missing -std=gnu++14 (#3149) 2021-09-26 16:19:46 -04:00
Wilson Snyder 44809f1843 Remove unused CFG_CXXFLAGS_STD_OLDEST 2021-09-26 16:01:25 -04:00
Wilson Snyder 87ced0cc3e Fix crash on clang 12/13 (#3148). 2021-09-25 17:10:57 -04:00
Wilson Snyder 741bb5328e verilator_gantt: Fix argument report omitting last digits 2021-09-24 21:11:15 -04:00
Wilson Snyder 33650336f5 Commentary 2021-09-24 08:49:16 -04:00
Wilson Snyder bbce1dc256 Commentary 2021-09-24 08:48:20 -04:00
Wilson Snyder 8ab51dbf22 Verilator_gantt: remove ASCII graphics 2021-09-24 08:48:20 -04:00
Wilson Snyder fd4595d6b4 verilator_gantt: Add eval count to report 2021-09-24 08:48:20 -04:00
github action 204804ae52 Apply 'make format' 2021-09-24 03:00:42 +00:00
Wilson Snyder c2819923c5 Verilator_gantt now shows the predicted mtask times, eval times, and additional statistics. 2021-09-23 22:59:36 -04:00
Wilson Snyder 68f1432a68 Gantt: Subtract common start in slowpath to reduce collection measurement error. 2021-09-23 19:43:20 -04:00
Keith Colbert 0defb61a18 Fix --waiver-output for multiline warnings (#2429) (#3141) 2021-09-22 07:42:59 -04:00
Wilson Snyder 0fc805202c Commentary (#3139) 2021-09-21 18:15:56 -04:00
Wilson Snyder a7374e84a2 Tests: Some coverage improvements 2021-09-20 18:20:48 -04:00
Wilson Snyder 76681fd931 Commentary 2021-09-17 20:18:47 -04:00
Wilson Snyder 2418df7bb2 Commentary 2021-09-17 20:03:45 -04:00
Wilson Snyder 97d8d32049 Commentary 2021-09-17 18:52:12 -04:00
Wilson Snyder 08c8b0d7d6 Fix removing if statement with side effect in condition (#3131). 2021-09-13 15:52:53 -04:00
Wilson Snyder 81fd3e4732 Commentary 2021-09-13 15:47:03 -04:00
Wilson Snyder d384a69877 Fix display has no time units on class function (#3116). 2021-09-08 19:31:46 -04:00
github action a8b480559d Apply 'make format' 2021-09-08 22:46:27 +00:00
Wilson Snyder f1b8b1d99b Format: perltidy spacing cleanup. No functional change. 2021-09-08 18:45:25 -04:00
Wilson Snyder f67637144f Internals: Some verilog.y format cleanups. No functional change. 2021-09-08 08:34:53 -04:00
Wilson Snyder 4b274a8d4d Convert verilator_gantt to python 2021-09-08 08:16:31 -04:00
Wilson Snyder c678e7ec3e Format: perltidy spacing cleanup. No functional change. 2021-09-07 23:50:28 -04:00
Wilson Snyder 2a79e46c46 Tests: Fix t_dist_fixme to detect later files. 2021-09-07 19:27:46 -04:00
Wilson Snyder d09b6a7d2c Include processor information in verilator_gantt data file. 2021-09-05 11:56:28 -04:00
Wilson Snyder 35eac0c457 Commentary: Fix profcfunc naming. 2021-09-05 11:33:20 -04:00
Wilson Snyder cac0b0f316 Tests: Fix t_gantt_io 2021-09-04 16:51:55 -04:00
Wilson Snyder 8519dc1383 Tests: Skip unstability 2021-09-04 13:48:16 -04:00
Wilson Snyder 72f198d79e Tests: Add t_gantt_io test 2021-09-04 12:44:13 -04:00
Wilson Snyder 1cb8091125 verilator_profcfunc: Also allow eval_step. 2021-09-04 12:28:26 -04:00
Wilson Snyder 18c543f2f2 Convert verilator_difftree to Python 2021-09-04 09:31:22 -04:00
Wilson Snyder 496b9f9c63 Tests: Add difftree test. 2021-09-04 08:27:59 -04:00
Wilson Snyder 1ee46ac5e1 Convert verilator_profcfunc to python. 2021-09-04 08:21:09 -04:00
Wilson Snyder f37c875be8 Internal: Remove fixme 2021-09-04 08:07:48 -04:00
Wilson Snyder 56dc66d842 Fix verilator_profcfunc profile accounting (#3115). 2021-09-03 19:59:10 -04:00
Wilson Snyder 7ae54ef973 Commentary 2021-09-02 09:00:01 -04:00
Iru Cai 4ed00c563c Support displaying x and z in $display task (#3107) (#3109) 2021-09-02 08:10:52 -04:00
Daniel Bates a49bfe871c Docs: Fix typo in coverage instructions (#3111) 2021-09-02 07:41:10 -04:00
Wilson Snyder 6d97fea2f5 Internals: Cleanup some const, etc. No functional change. 2021-09-01 22:15:38 -04:00
Wilson Snyder da833d55fe devel release 2021-09-01 21:08:17 -04:00
Wilson Snyder 960813cb0f Version bump 2021-09-01 20:58:03 -04:00
Wilson Snyder cd56dfee1c Update GTKwave from upstream. 2021-09-01 20:45:41 -04:00
Wilson Snyder 3e03cd5a4d Commentary 2021-09-01 20:39:59 -04:00
Krzysztof Bieganski cd9e9da4b5 Support assignment patterns as children of pins (#3041)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2021-08-30 10:44:03 -04:00
Wilson Snyder f64d9d87f6 Clang compile fix 2021-08-28 17:44:19 -04:00
Wilson Snyder 43aa3229fb Support empty queue assignment to delete queue 2021-08-28 17:23:35 -04:00
Wilson Snyder 27d53691bd Add header guards on Dpi.h generated files (#2979). 2021-08-23 21:43:54 -04:00
Wilson Snyder 959793cde3 Internals: Cleanup VL_VALUE_STRING_MAX widths (#3050). 2021-08-23 21:13:33 -04:00
Wilson Snyder c3d64d9743 Fix internal error on wide -x-initial unique (#3106). 2021-08-23 20:13:09 -04:00
Wilson Snyder f9806595f2 Add error when constant function under a generate (#3103). 2021-08-21 10:33:20 -04:00
Geza Lore 34a0bb448e Improve V3MergeCond
- Merge AstNodeIf nodes as well (not just assignment from AstCond)
- Merge merged results recursively (optimizes nested conditionals/ifs)
- Only checking mergeability once per node.
- Don't add redundant masking
- Duplicate cheap statements in both branches, if doing so yields a
  larger merge
- Include reduced nodes before the starting conditional in the merge
2021-08-19 18:55:39 +01:00
Geza Lore 8681861be9 Improve bitop tree optimization
- Remove redundant casting
- Cheaper final XOR parity flip (~/^1 instead of != 0)
- Support XOR reduction of ~XOR nodes
- Don't add redundant masking of terms
- Support unmasked terms
- Add cheaper implementation for single bit only terms
- Ensure result is clean under all circumstances (this fixes current bugs)
2021-08-18 22:00:13 +01:00
Geza Lore fffc63970d Use narrowest RedXor 2021-08-18 22:00:09 +01:00
Ivan Vnučec 8f527a0c6e Commentary (#3102)
Actually you can see `https://github.com/verilator/verilator` URL in your browser.
2021-08-18 15:19:02 -04:00
Geza Lore c69ddc46f8 Fix bitop tree optimization dropping necessary cleaning AND (#3097)
Fixes #3096.
2021-08-14 21:09:01 +01:00
Yutetsu TAKATSUKASA 4ec8bc3589 Fix op count and wrong optimization in bitop tree (#3099)
- Fix op calculation.
- Fix wrong optimization revealed by changing the op calculation:
  Not(CCast(And(a, b))) must be frozen.
2021-08-14 12:28:47 +01:00
Geza Lore 536bdf506e Fix re-evaluation of logic dependent on state set in DPI exports (#3091).
Verilator should now correctly re-evaluate any logic that depends on
state set in a DPI exported function, including if the DPI export is
called outside eval, or if the DPI export is called from a DPI import.

Whenever the design contains a DPI exported function that sets a
non-local variable, we create a global __Vdpi_export_trigger flag, that
is set in the body of the DPI export, and make all variables set in any
DPI exported functions dependent on this flag (this ensures correct
ordering and change detection on state set in DPI exports when needed).
The DPI export trigger flag is cleared at the end of eval, which ensured
calls to DPI exports outside of eval are detected. Additionally the
ordering is modifies to assume that any call to a 'context' DPI import
might call DPI exports by adding an edge to the ordering graph from the
logic vertex containing the call to the DPI import to the DPI export
trigger variable vertex (note the standard does not allow calls to DPI
exports from DPI imports that were not imported with 'context', so we
do not enforce ordering on those).
2021-08-12 21:43:32 +01:00
Pieter Kapsenberg d1836b7b6f Traces show array instances using brackets instead of parens (#3092) (#3095) 2021-08-12 20:40:44 +03:00
Geza Lore 00fe36f44c Name temporary variables based on hash of related node.
This improves output stability by removing sequence numbers and hence
can improve ccache hit rate. No functional change intended.
2021-08-11 17:29:22 +01:00
Geza Lore 5adc856950 Tests: ignore all hashes in files_identical
Also add 'h' prefix to all printed hashes, to reduce ambiguity. No
functional change.
2021-08-11 16:55:11 +01:00
Geza Lore bc3e24c8cd Use c++XX instead of gnu++XX language standards.
The GNU extensions to C/C++ are not required to build Verilator. Note
this change does not affect __attribute__, __builtin_* and intrinsics,
which are still available when using GCC or Clang with the standard
language options.
2021-08-04 11:48:32 +01:00
Geza Lore 2c8456ea75 Internals: Cleanup V3SplitVar
- Use C++11 initialization syntax
- Use C++11 for loops
- Add const
- Factor out repeated _->fileline() sub-expressions
- Factor out issuing warning message

No functional change.
2021-08-02 15:48:26 +01:00
Geza Lore 519cc7d61c Internals: Remove code duplication from V3Order
No functional change
2021-08-02 14:06:50 +01:00
Wilson Snyder 0f4daa12e4 Ignore sv-test's `pragma protect end 2021-07-29 09:00:10 -04:00
Wilson Snyder 6bad0e14ce Support timeunit/timeprecision in $unit. 2021-07-29 08:40:41 -04:00
Geza Lore 3ec3c2c268 Internals: Cleanup V3Expand
- Use C++ initialization syntax
- Add const
- Make members static where appropriate
- Factor out repeated _->fileline() sub-expressions

No functional change.
2021-07-27 13:18:57 +01:00
Wilson Snyder b8e804f05b Internals: Some clang-tidy cleanups. No functional change intended. 2021-07-25 13:38:27 -04:00
Geza Lore cdeb6e792f Add --instr-count-dpi option, change default to 200
This replaces the former static AstNode::INSTR_COUNT_DPI, and makes it
user adjustable to fit the design.

Fixes #3068.
2021-07-25 16:40:12 +01:00
Wilson Snyder deebfa3239 Tests: Remove Ubuntu 16.04 as github removes in a month. 2021-07-25 11:22:25 -04:00
Wilson Snyder f937e3282f Tests: Ignore DepSet hash numbers. (#3083) 2021-07-25 11:20:19 -04:00
Geza Lore b46664c70a Internals: Use C++11 constexpr for instruction count costs
No functional change.
2021-07-25 13:32:40 +01:00
github action 33af14eba8 Apply 'make format' 2021-07-25 01:07:07 +00:00
Steven Hugg 18b0f6387d Add XML ccall, constpool, initarray, and if/while begins (#3080)
* EmitXml: Added <ccall>, <constpool>, <initarray>/<inititem>, wrapped children of <if> and <while> with <begin> elements to prevent ambiguity
* EmitXml: added signed="true" to signed basicdtypes
2021-07-24 21:06:06 -04:00
Wilson Snyder 43ecaec9a0 Internal C++11 flex cleanups. No functional change intended. 2021-07-24 12:49:07 -04:00
Wilson Snyder 275885a797 Internals: Fix some long UNSUP lines. No functional change. 2021-07-24 12:21:04 -04:00
Wilson Snyder 68aa731ce9 Fix missing null on blackbox parse. 2021-07-24 12:13:19 -04:00
Wilson Snyder b90fce55f4 Includes: Refactor verilated.h and deprecate verilated_heavy.h (#2701). 2021-07-24 10:00:33 -04:00
Wilson Snyder 44173b40a1 Internals: Remove dead code. No functional change. 2021-07-24 09:18:49 -04:00
Wilson Snyder ab13a2ebdc Internals: Use C++11 const and initializers. No functional change intended. 2021-07-24 08:36:11 -04:00
Wilson Snyder 19875d2e85 Increase split count of ChangeDet (#3072). 2021-07-23 19:24:25 -04:00
Geza Lore 1de33b9fb7 Support localparams in tasks/functions 2021-07-23 20:34:49 +01:00
Geza Lore 90c917ee83 Enable now supported tests for string array initializers
Fixes #2895.
2021-07-23 17:21:15 +01:00
Geza Lore 4ab4c0c8ba Emit parameter values as 'static constexpr' instead of enum
All parameters that are required in the output are now emitted as
'static constexpr, except for string or array of strings parameters,
which  are still emitted as 'static const' (required as std::string is
not a literal type, so cannot be constexpr).  This simplifies handling
of parameters and supports 'real' parameters.
2021-07-23 17:20:26 +01:00
Geza Lore 9907d211ff Make VlWide and VlUnpacked C++20 compatible aggregates.
C++20 requires that aggregate types do not have a user declared
constructor, not even an explicitly defaulted one. We need these types
to be aggregates for static initialization.

Fixes #3076.
2021-07-23 12:57:45 +01:00
Wilson Snyder 13933743ad Suppress creating change_request if not needed. 2021-07-22 20:50:03 -04:00
Wilson Snyder fc25721e55 Increase instruction count of ChangeDet (#3072) 2021-07-22 20:13:45 -04:00
Wilson Snyder fa64f8fb32 Internals: C++11 cleanup. No functional change intended. 2021-07-22 20:05:54 -04:00
Geza Lore ab4063f098 Emit implementations into separate files based on required headers.
This patch partitions AstCFuncs under an AstNodeModule based on which
header files they require for their implementation, and emits them
into separate files based on the distinct dependency sets. This helps
with incremental recompilation of the output C++.
2021-07-22 18:01:07 +01:00
Geza Lore 8bb77f86ec Emit: Factor out parent module access via user4
No functional change.
2021-07-22 16:31:46 +01:00
Geza Lore 42d918c163 Internals: Omit selfPointer from ConstPool references
AstVarRefs to constant pool values are special and they have no use for
the selfPointer. Removing it simplifies future downstream code.
2021-07-22 13:35:22 +01:00
Geza Lore e3dd70c373 Internals: Simplify V3CUse
No functional change intended.
2021-07-20 19:52:15 +01:00
Geza Lore 9273fafbbf Internals: Split parts of V3CUse into V3Common
Apart from adding required AstCUse, V3CUse also used to create some
standard methods for classes. This is now done in a separate pass
V3Common. Note that this is not a performance issue, as V3Common just
iterates through each module, which are stored in a simple linked list
under the netlist, and does not need to traverse the whole netlist.
2021-07-20 17:18:52 +01:00
Geza Lore d9c893af11 Internals: Add VString::startsWith predicate function 2021-07-20 14:29:43 +01:00
Geza Lore a9c4a96c0f Internals: const pointer argument 2021-07-20 14:29:36 +01:00
Geza Lore 32e99ba0d9 Emit: Attach VL_ATTR_COLD to definitions as well as declarations.
This is required by clang if the definition precedes the declaration
(which can happen when building via __ALL.cpp), otherwise we get a
warning.
2021-07-20 13:52:53 +01:00
Wilson Snyder da8930a57a Fix multitop to go to stderr (#3070) 2021-07-20 08:00:14 -04:00
Geza Lore 1166728a8d Tests: make t_trace_*two* tests less sensitive to file names
No functional change intended
2021-07-19 17:48:41 +01:00
Geza Lore 30fa5e41be Don't emit trace files with --lint-only 2021-07-19 17:48:38 +01:00
Geza Lore cf39331d1f Emit: Factor out visitor that can emit constant initializers
This is needed both by the V3EmitCConstPool emitter and the
V3EmitCHeaders emitter.
2021-07-14 13:56:04 +01:00
Geza Lore bac84726e1 Emit: Clean up some code coverage holes 2021-07-14 12:27:33 +01:00
Geza Lore ddef61d62e Internals: Move AstNodeCCall::m_selfPointer to AstCCall
Other sub-classes of AstNodeCCall do not need the self pointer. Moving
it into the specific sub-class that needs it clarifies V3Descope and
Emit. No functional change intended.
2021-07-13 17:47:42 +01:00
Geza Lore 4081a1a539 Internals: Separate emitting of C++ headers and implementation
Internal AstNodeModule headers (.h) and implementation (.cpp) files are
now emitted separately in V3EmitC::emitcHeaders() and
V3EmitC::emitcImp() respectively. No functional change intended
2021-07-13 17:43:44 +01:00
Geza Lore 1691bbfe25 Remove unnecesasry and incorrect trace class CUse. 2021-07-13 14:20:57 +01:00
Geza Lore 17cc452f79 Add V3VariableOrder pass
A separate V3VariableOrder pass is now used to order module variables
before Emit. All variables are now ordered together, without
consideration for whether they are ports, signals form the design, or
additional internal variables added by Verilator (which used to be
ordered and emitted as separate groups in Emit). For single threaded
models, this is performance neutral. For multi-threaded models, the
MTask affinity based sorting was slightly modified, so variables with no
MTask affinity are emitted last, otherwise the MTask affinity sets are
sorted using the TSP sorter as before, but again, ports, signals, and
internal variables are not differentiated. This yields a 2%+ speedup for
the multithreaded model on OpenTitan.
2021-07-12 14:53:40 +01:00
Wilson Snyder 8ecdc85cf7 Internals: C++11 style cleanups. No functional change. 2021-07-11 18:42:01 -04:00
Geza Lore 5ad3c4e499 Internals: Speed up and improve V3Broken
This patch makes OpenTitan verilation with --debug-check 22% faster, and
the same with --debug --no-dump-tree 91% faster. Functionality is the
same (including when VL_LEAK_CHECKS is defined), except V3Broken can now
always find duplicate references via child/next pointers if the target
node is not `maybePointedTo()` (previously this only happened when
compiled with VL_LEAK_CHECKS). The main change relates to storing the
v3Broken traversal state in the AstNode by stealing a byte from what
used to be unused flags. We retain an unordered_set only for marking
pointers as valid to be referenced via a non-child/non-next member
pointer.
2021-07-11 21:03:17 +01:00
Geza Lore 8073e8bb46 Fix typo 2021-07-11 16:52:02 +01:00
Geza Lore 8a9d6d225b Internals: Add source file/line info to BROKEN_RTN messages 2021-07-11 16:50:05 +01:00
Wilson Snyder c7499133b2 Internals: C++11 for bool. No functional change. 2021-07-11 10:42:32 -04:00
Wilson Snyder 687dd440a9 Removed deprecated Verilator tarball generation makefile rules. 2021-07-11 10:25:24 -04:00
Wilson Snyder f1bb0bb725 Tests: Whitespace check all git files 2021-07-11 10:25:13 -04:00
Wilson Snyder a8168d5d62 Commentary 2021-07-11 09:46:00 -04:00
Geza Lore 896b18e024 Emit function locals in the place they appear in the tree
Do not sort and hoist function local variables to the top of the
function definition. The stack layout of automatic variables is not
defined by C so the compilers can lay these out optimally. Simplifies
internals for follow on work. Effect on model performance is neutral to
very slight improvement, so we do not seem to be loosing anything.
2021-07-10 15:20:19 +01:00
Geza Lore 766ad14ae0 Check function locals are referenced only when in scope
V3Broken now checks that AstVar nodes referenced in an AstCFunc are
either external, or appear in the tree before the reference, and are in
scope.

Fix V3Begin to move lifted AstVars to beginning of FTask, rather than
end, which trips the above check.
2021-07-10 15:20:19 +01:00
Geza Lore add3811f46 Internals: Fix debug prints racing with option parsing.
debug() declared by VL_DEGUB_FUNC used to cache the result of the debug
level lookup (which depends on options) in a static. This meant that if
the debug() function was called before option parsing, the default debug
level of 0 would be used for the rest of the program, even if a --debug
option was given. Fixed by not caching the debug level until after
option parsing is complete.
2021-07-10 12:57:40 +01:00
Wilson Snyder f55177a49f Tests: Fix coverage holes. No functional change. 2021-07-10 07:11:50 -04:00
Wilson Snyder bdceb08963 Internals: Fix off-by-one #line. 2021-07-10 07:11:34 -04:00
Geza Lore 825d6b87a8 Internals: Put AstNode flags in padding gap, initialize spare bits
The bool AstNode flags fit in a padding gap after m_type. This reduces
memory consumption by about 2% on OpenTitan. Initializing the unused
bits in the flags then avoids a read-modify-write in the constructor
(replacing it with a store constant). Overall verilation speed is about
1% faster.
2021-07-10 08:56:05 +01:00
Wilson Snyder 61e2e55ba5 Internals: Fix coverage holes. No functional change. 2021-07-09 18:11:59 -04:00
Wilson Snyder e6b75f752b Commentary 2021-07-09 17:46:11 -04:00
Geza Lore a4f5d95648 Fix -G to treat simple integer literals as signed (#3060)
The -G option now correctly parses simple integer literals as signed
numbers, which is in line with the standard and is significant when
overriding parameters without a type specifier.

Fixes #3060
2021-07-08 13:42:25 +01:00
Geza Lore 686baaf2cf Internals: Streamline trace function generation
Remove magic code fragments form EmitCTrace, so Emit need not be aware
that a function is tracing related or not (apart from the purpose of
file name generation). All necessary code is now generated via text
nodes in V3TraceDecl and V3Trace. No functional change intended.
2021-07-08 02:08:09 +01:00
Geza Lore 76b3776fa3 Change generated tracing routines to use snake_case
For consistency with the rest of the generated code, generated methods
related to tracing now use snake_case instead of camelCase. No
functional change intended.
2021-07-08 02:08:09 +01:00
Wilson Snyder 3ed2af638b devel release 2021-07-07 20:50:11 -04:00
Wilson Snyder 8e2ba6a003 Version bump 2021-07-07 20:43:07 -04:00
Wilson Snyder 3aa6332f25 Fix Codacy warnings. No functional change. 2021-07-07 19:42:49 -04:00
Wilson Snyder 36599133bf Add --prof-c to pass profiling to compiler (#3059). 2021-07-07 19:12:52 -04:00
Wilson Snyder 941f96c913 Commentary 2021-07-07 17:59:22 -04:00
Wilson Snyder 93578d64d2 Internals: Remove old needHeavy code. 2021-07-07 17:56:34 -04:00
Dan Petrisko 8c705ee145 Support middle-of-design nested topmodules (#3026) 2021-07-07 15:00:29 -04:00
Dan Petrisko c5c5f11e16 Tests: Adding failing test case for source synchronous signals (#3038) 2021-07-07 14:00:17 -04:00
Morten Borup Petersen fd0446f481 Internals: Add .dot graph visualization of ThreadSchedule (#3048)
* Move MTaskState to ThreadSchedule

MTaskState does not concern itself with sandbagging, and thus solely contains information related to the finalized schedule, i.e., completion time, thread ID and next MTask on thread.

* Add .dot graph visualization of ThreadSchedule

Follow-up to #2779.

This commit adds the creation of .dot files - used by GraphViz - to visualize how mtasks are statically scheduled across the set of specified threads.
We visualize each thread as a row, with nodes of a row being the mtasks scheduled for the given thread. The width of the mtask nodes are proportional to their cost. MTask dependencies are shown using an edge between the source and sink mtasks.
2021-07-06 07:06:00 -04:00
Geza Lore 2ebed755e6 V3Simulate: Avoid copying while managing free list.
V3Simulate reuses allocated AstConst nodes for efficiency, however this
used to be implemented in a way that required a deep copy of a
std::unorderd_map<_, std::deque<_>>, which was quite inefficient when it
grew large. The free list is now managed without any copying. This takes
the V3Table pass from taking 12s to 0.2s on SweRV EH1.
2021-07-05 17:07:33 +01:00
Geza Lore 2a7aa28b20 V3Simulate/V3Table: change deques to vectors for performance
We can get away with only push_back and back on sequence containers, in
which case std::vector is significantly faster than std::deque.
2021-07-05 15:53:56 +01:00
Geza Lore fb56f4f880 Tests: make t_prot_lib*.pl name mangling deterministic
This is to facilitate diffing generated files in tests.
2021-07-02 00:21:24 +01:00
Geza Lore d4e73e215e Tests: fail test if vcddiff aborts, fix failing tests
Tests used to silently pass when vcddiff aborted. Now fixed. Updated
large array trace reference files for FST, added same reference files
for VCD.

Developers need to update their local vcddiff.
2021-07-01 23:22:25 +01:00
Geza Lore d5c9369103 Internals: Fix suspicious dtype clone relinking. (#3056) 2021-07-01 14:33:44 -04:00
Geza Lore af27702188 Fix type parameter resolution with hash collision.
We incorrectly treated two different struct types the same when passed
as an actual parameter to a `parameter type` parameter in an instance,
if the actual parameter expression both hash to the same value and the
structs have the same struct name. This is now corrected.

Fixes #3055.
2021-07-01 16:52:29 +01:00
Morten Borup Petersen 2c813488f4 Tests: Add simulator benchmarking data option for regression tests (#3054)
This commit adds the '--simbenchmark' option to the regression test compile command.
The option is not intended as a fully-fledged benchmarking infrastructure, but rather a
utility for easily generating cycle- and execution time information when executing a verilated test.

As an example use case, the included test file shows how optimization level is varied across
three different builds+simulations, with the statistics for each run output to the same file in
the output directory.

Future work:
- 'sim_time' in the generated top-level main file should be a parameter.
- Given the above, the test execution script from verilog-sim-benchmark can be integrated
to generate better estimates of cycles/second through varying 'sim_time' over multiple executions.
2021-07-01 11:17:55 -04:00
Geza Lore 708abe0dd1 Introduce model interface class, make $root part or Syms (#3036)
This patch implements #3032. Verilator creates a module representing the
SystemVerilog $root scope (V3LinkLevel::wrapTop). Until now, this was
called the "TOP" module, which also acted as the user instantiated model
class. Syms used to hold a pointer to this root module, but hold
instances of any submodule. This patch renames this root scope module
from "TOP" to "$root", and introduces a separate model class which is
now an interface class. As the root module is no longer the user
interface class, it can now be made an instance of Syms, just like any
other submodule. This allows absolute references into the root module to
avoid an additional pointer indirection resulting in a potential speedup
(about 1.5% on OpenTitan). The model class now also contains all non
design specific generated code (e.g.: eval loops, trace config, etc),
which additionally simplifies Verilator internals.

Please see the updated documentation for the model interface changes.
2021-06-30 16:35:40 +01:00
Yutetsu TAKATSUKASA 22e0f3edbe Introduce small object optimization to V3Number (#3034) 2021-06-30 21:20:56 +09:00
Felix Yan 2cdf0aae72 Commentary: Correct a typo in configure.ac (#3045) 2021-06-28 23:18:14 -04:00
Wilson Snyder ee4a601c9e Internals: Use interface initialization for cleaner code. No functional change. 2021-06-28 22:40:39 -04:00
Morten Borup Petersen e7b6a7ef6a Fix shadowing of vlSymsp member with argument (#3046).
This commit removes shadowing of the vlSymsp member of the emitted
modules, allowing models to compile when -Werror=shadow is set. This may
be useful when i.e., an external project which defines its own error
flags depends on the verilated model.
2021-06-28 20:37:09 +01:00
Geza Lore 2825940fad Fix dtype when simplifying masked shift.
Fixes #3044
2021-06-25 18:06:43 +01:00
Geza Lore c6f6dab413 Emit: Factor out parts needed for emitting an AstCFunc
Factored out bits from V3EmitC.cpp that is required to emit a whole
(non-trace) AstCFunc. This is mostly what used to be the EmitCStmts
class plus relevant bits from EmitCImp. These now live in EmitCFunc,
which is reusable by anything that needs to emit a regular AstCFunc
(differences in tracing to be addressed later). EmitCImp now extends
EmitCFunc instead of EmitCStmts. No functional change intended.
2021-06-24 19:39:47 +01:00
Geza Lore bbbee8b89e Make emitVarDecl and emitModCUse reusable via V3EmitCBase.
Moved these 2 function into V3EmitCBase so we can reuse them later.
emitVarDecl required minor alteration to move building of m_ctorVarsVec
back into V3EmitC (which is now done in V3EmitC::emitSortedVarList).
No functional change intended.
2021-06-24 18:15:47 +01:00
Geza Lore 5250408019 Add V3EmitCBase.cpp to hold implementations
No need to keep complex functions in the V3EmitCBase.h header (which is
included in a lot of compilation units). No functional change intended.
2021-06-24 18:04:54 +01:00
Geza Lore a198a3c1e1 Add extern "C" to function declarations in VPI tests.
These are necessary to link the executables. So far we have been saved
by one of the generated headers forward declaring these functions with
extern "C", but changing that header would break these tests.
2021-06-24 16:48:58 +01:00
Wilson Snyder f311a0a5a5 Internals: Add const. No functional change. 2021-06-22 08:43:54 -04:00
Geza Lore ec1c112791 Remove deprecated --inhibit-sim (#3035) 2021-06-21 12:38:42 -04:00
Geza Lore 7ecc2d4df7 V3Hash: Add missing include.
Fixes #3029
2021-06-21 15:32:58 +01:00
Wilson Snyder 512fe0a2d1 Internals: Add const. No functional change. 2021-06-20 18:33:13 -04:00
Yutetsu TAKATSUKASA ec4eb18846 Fiix incorrect result by bit tree opt (#3023) (#3030)
* Add a test to reproduce #3023. Also applied verilog-mode formatting.

* use unique_ptr. No functional change is intended.

* Introduce restorer that reverts changes during iterate() if failed.
2021-06-21 07:28:39 +09:00
Geza Lore f064a94f1d Configure time unit/time precision in the Sym constructor.
This used to be done in the constructor of the top module, but there is
no reason to do it there. Internals are cleaner with this in the Sym
constructor. No functional change intended.
2021-06-19 20:42:38 +01:00
Geza Lore 19398efc4c Remove no-op VL_CELL. No functional change intended. 2021-06-19 20:42:38 +01:00
Geza Lore fcb8bc22bd Internals: Remove m_classPrefix from AstNodeVarRef/AstNodeCCall
This is now redundant and can be reconstituted in V3EmitC without being
explicitly stored.
2021-06-19 20:42:38 +01:00
Wilson Snyder 2ee52222e2 Tests: Add test (#2912). 2021-06-19 14:39:34 -04:00
Wilson Snyder c11cd18491 In XML, show pinIndex information (#2877). 2021-06-19 13:41:41 -04:00
Geza Lore eebda248c7 Internals: Use AstUserAllocator in V3Order 2021-06-19 15:23:02 +01:00
Wilson Snyder 8d737271ca Allow configure override of AR program (#2999). 2021-06-19 10:00:31 -04:00
Wilson Snyder 52cde49a6f Internals: Add more const. No functional change. 2021-06-18 22:24:08 -04:00
Geza Lore 6c9c16c31d Simplify redundant masking of AstShiftR/AstShiftL
AND(CONST,SHIFTR(_,C)) appears often after V3Expand, with C a large
enough dense mask (i.e.: of the form  (1 << n) - 1) to make the masking
redundant. E.g.: 0xff & ((uint32_t)a >> 24). V3Const now replaces these
ANDs with the SHIFTR node.

Similarly, we also simplify the same with SHIFTL,
e.g.: 0xff000000 & ((uint32_t)a << 24)
2021-06-18 20:06:53 +01:00
Geza Lore 0c93c3844f Simplify AND(CONST,OR(_,_)) with redundant terms
V3Expand generates a lot of OR nodes that are under a clearing mask, and
have redundant terms, e.g.: 0xff & (a << 8 | b >> 24). The 'a << 8' term
in there is redundant as it's bottom bits are all zero where the mask is
non-zero. V3Const now removes these redundant terms.
2021-06-18 19:07:00 +01:00
Geza Lore e5e5bc0fa3 Localize variables used in multiple functions
Teach V3Localize how to localize variables that are used in multiple
functions, if in all functions where they are used, they are always
written in whole before being consumed. This allows a lot more variables
to be localized (+20k variables on OpenTitan - when building without
--trace), and can cause significant performance improvement (OpenTitan
simulates 8.5% - build single threaded and withuot --trace).
2021-06-18 16:22:51 +01:00
Geza Lore d6237e55b2 Internals: Add AstUserNAllocator utility classes.
These utility classes can be used to hang advanced data structures off
AstNode user*u() pointers, and they take care of memory management for
the client. Use via the call operator().
2021-06-18 16:22:51 +01:00
Geza Lore 3ec1e9eb07 Localize variables from other modules when possible
V3Localize can now localize variable references that reference variables
located in scopes different from the referencing function. This also
means V3Descope has now moved after V3Localize.
2021-06-18 16:22:51 +01:00
Geza Lore d5bdd07c01 Fix out of bounds index into VlWide under AstSel
When part selecting bits via an AstSel in a VlWide, V3Expand used to do
something akin to:

word_index = lsb / 32;
bit_index = lsb % 32;
result =
  wide[word_index + 1] << (32 - bit_index) | wide[word_index] >> bit_index;

The unconditional "+ 1" can cause an out of bounds access into the
VlWide, when the whole of the select is into the most significant word
(i.e.: when word_index is already the most significant word).  We now
emit roughly this instead:

lo_word_index = lsb / 32;
bit_index = lsb % 32;
hi_word_index = (lsb + width - 1) / 32;
result =
  wide[hi_word_index] << (32 - bit_index) | wide[lo_word_index] >> bit_index;

i.e.: we explicitly calculate which word the MSB of the select falls
into, and address that word, rather than the unconditional + 1. The
shifts ensure we still yield the right result, even if lo_word_index and
hi_word_index are the same.

Note: The actual expression created by V3Expand can be a bit more
complicated as we might need to access 3 words when the result is a
QData, all 3 word indices are calculated explicitly.
2021-06-18 14:31:01 +01:00
Wilson Snyder 5fddf51e8c Internals: Favor VlWide over WData arrays. No functional change intended. 2021-06-17 21:17:25 -04:00
Geza Lore 0a28fc8c63 Internals: minor cleanup to V3Descope 2021-06-17 14:30:15 +01:00
Geza Lore 9eafca5e28 Remove deprecated --no-relative-cfuncs (#3024) 2021-06-16 23:17:43 -04:00
Geza Lore 729bd268de Internals: make AstCFunc::m_isStatic a bool.
All functions are now known to be static or not static when they are
created, so turn the isStatic flag into a bool (from VBoolOrUnknown).
2021-06-16 14:24:28 +01:00
Geza Lore 6c332a2f8e Emit: Remove emitVarCmtChg
emitVarCmtChg used to emit MTask affinity of variables in comments in
the generated header. This causes unnecessary changes in the output when
scheduling changes slightly between compilation, hindering ccache reuse.
If needing this info for debugging Verilator, add a separate dump file
instead of emitting it in the generated code.
2021-06-16 14:24:25 +01:00
Geza Lore a8f83d5758 Construct AstExecGraph implementation outside of V3EmitC. (#3022)
The goal of this patch is to move functionality related to constructing
the thread entry points and then invoking them out of V3EmitC (and into
V3Partition). The long term goal being enabling V3EmitC to emit
functions partitioned based on header dependencies. V3EmitC having to
deal with only AstCFunc instances and no other magic will facilitate
this.

In this patch:
- We construct AstCFuncs for each thread entry point in
V3Partition::finalize and move AstMTaskBody nodes under these functions.
- Add the invocation of the threads as text statements within the
AstExecGraph, so they are still invoked where the exec graph is located.
(the entry point functions are still referenced via AstCCall or
AstAddOrCFunc, so lazy declarations of referenced functions are created
automatically).
- Explicitly handle MTask state variables (VlMTaskVertex in
verilated_threads.h) within Verilator, so no need to text bash a lot of
these any more (some text refs still remain but they are all created
next to each other within V3Partition.cpp).

The effect of all this on the emitted code should be nothing but some
identifier/ordering changes. No functional change intended.
2021-06-16 12:18:56 +01:00
Geza Lore 65bfb4e5ff Build Verilator without -Og in the coverage build
The coverage numbers decreased when adding -Og to the debug build. This
patch restores them by adding --enable-coverage to configure and
building without -Og if requested.
2021-06-14 19:55:03 +01:00
Geza Lore c75a686081 Internals: Update to clang-format-11 (#3021) 2021-06-14 14:50:40 -04:00
Geza Lore 2705715bb1 CI: set CI_M32 to 0 in coverage workflow 2021-06-14 01:30:50 +01:00
Geza Lore 208f1504fb CI: Add -m32 build 2021-06-14 00:37:59 +01:00
Geza Lore 24b5215cf9 Add --enable-m32 to configure 2021-06-14 00:37:59 +01:00
Geza Lore 6016e74b55 Drop ambiguous overloads in VlWide
The 32-bit build fails with the ambiguous overload present.
2021-06-14 00:37:33 +01:00
Geza Lore 0c4d88bacc Fix V3Hash when building -m32 2021-06-13 23:19:25 +01:00
Geza Lore 01a54d6960 CI: Build opt and dbg together, archive whole source tree
Prep for adding more CI targets. Building dbg and opt in the same job
(as standard) simplifies caching, debugging and artifact handling. With
ccache it should not take much longer either. Also removes the need to
re-configure in the test job.
2021-06-13 22:45:57 +01:00
Geza Lore 23fc08bdf9 CI: swap order of platforms
GitHub Actions starts the jobs earlier in the list first. This change
has the effect of starting the few longer running jobs (those on ubuntu
20.04) first.
2021-06-13 22:45:57 +01:00
Wilson Snyder 13ddd0bc1c Fix error on unsupported recursive functions (#2957). 2021-06-13 12:38:31 -04:00
Wilson Snyder 31121141db Convert pipe filter example to python 2021-06-13 12:03:53 -04:00
Wilson Snyder 9d3e800311 Commentary 2021-06-13 12:03:53 -04:00
Geza Lore 7280307a39 Implement DPI import/export as loose functions 2021-06-13 15:06:28 +01:00
Geza Lore c207e98306 Implement a distinct constant pool (#3013)
What previously used to be per module static constants created in
V3Table and V3Prelim are now merged globally within the whole model and
emitted as part of a separate constant pool. Members of the constant
pool are global variables which are declared lazily when used (similar to
loose methods).
2021-06-13 15:05:55 +01:00
Geza Lore 60d5f0e86b Emit model implementation as loose methods. (#3006)
This patch introduces the concept of 'loose' methods, which semantically
are methods, but are declared as global functions, and are passed an
explicit 'self' pointer. This enables these methods to be declared
outside the class, only when they are needed, therefore removing the
header dependency. The bulk of the emitted model implementation now uses
loose methods.
2021-06-13 14:33:11 +01:00
Geza Lore 18cabc369b CI: fix error in coverage workflow file 2021-06-13 03:16:58 +01:00
Geza Lore cd871ca79e CI: Increase cache sizes slightly 2021-06-13 03:16:56 +01:00
Geza Lore c67fe02f06 Do not generate debug info in tests
Pointless and takes up a lot of cache space in CI, so remove -ggdb and
associated debug options from tests.
2021-06-13 02:55:29 +01:00
Geza Lore 2d2bd5b95e CI: Upload separate named artifacts, only fetch ones that are needed 2021-06-13 01:05:42 +01:00
Geza Lore 02835f199b CI: Improve caching
GitHub Actions allow a total 5 GB of cache storage space per repository,
after which it will evict old caches. Tweaked cache sizes so master + a
few PRs can fit at the same time. Don't cache coverage builds as they
run weekly and with additional compiler options, so their caches would
likely be state anyway.
2021-06-13 00:19:40 +01:00
Geza Lore e6e7bd8d83 Compile the debug build with -Og -ggdb -gz if supported.
Check the C++ compiler for -Og via configure and use it if available.

Per the GCC manual:
-Og should be the optimization level of choice for the standard
edit-compile-debug cycle, offering a reasonable level of optimization
while maintaining fast compilation and a good debugging experience. It
is a better choice than -O0 for producing debuggable code because some
compiler passes that collect debug information are disabled at -O0.

The debug exe is painfully slow on large designs, hopefully this is an
improvement.

Similarly, check for and use -gz to compress the debug info as it is
huge otherwise. This should help with distribution and caching on CI.

Also checks for -ggdb via configure for compatibility.
2021-06-12 23:25:58 +01:00
Geza Lore b1c7de4ad5 Minor compiler compatibility fixes
- Initialize variable to avoid 'may be uninitialized' warning
- More reliable segfault (the previous version was compiled into an
undefined instruction by clang sometimes, thew new one is always a store
to zero).
2021-06-12 23:25:58 +01:00
Wilson Snyder f0f68f42d1 devel release 2021-06-12 13:05:33 -04:00
872 changed files with 39991 additions and 22989 deletions
-1
View File
@@ -108,4 +108,3 @@ Standard: Cpp11
TabWidth: 8
UseTab: Never
...
+2
View File
@@ -13,4 +13,6 @@ Can you attach an example that shows the issue? (Must be openly licensed, ideal
What 'verilator --version' are you using? Did you try it with the git master version?
What OS and distribution are you using?
Would you be willing to try to fix Verilator yourself with assistance?
+71 -70
View File
@@ -16,128 +16,129 @@ env:
CI_COMMIT: ${{ github.sha }}
CCACHE_COMPRESS: 1
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_LIMIT_MULTIPLE: 0.95
defaults:
run:
shell: bash
working-directory: repo
jobs:
Matrix:
runs-on: ubuntu-latest
name: Generate Build matrix
outputs:
matrix: ${{ steps.generate.outputs.matrix }}
steps:
- uses: actions/checkout@v2
- id: generate
name: Run 'generate_matrix.sh'
run: |
if [ '${{ github.event_name }}' = 'pull_request' ]; then
matrix='[ "ubuntu-20.04" ]'
else
matrix='[ "ubuntu-16.04", "ubuntu-18.04", "ubuntu-20.04" ]'
fi
echo "::set-output name=matrix::$matrix"
Build:
needs: Matrix
build:
strategy:
fail-fast: false
matrix:
os: ${{ fromJson(needs.Matrix.outputs.matrix) }}
debug: [ opt, dbg ]
os: [ubuntu-20.04, ubuntu-18.04]
compiler:
- { cc: clang, cxx: clang++ }
- { cc: gcc, cxx: g++ }
m32: [0, 1]
exclude:
# Build pull requests only with ubuntu-20.04 and without m32
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
# Build -m32 only on ubuntu-20.04
- {os: ubuntu-18.04, m32: 1}
runs-on: ${{ matrix.os }}
name: Build | ${{ matrix.os }} | ${{ matrix.compiler.cc }} | ${{ matrix.debug }}
name: Build | ${{ matrix.os }} | ${{ matrix.compiler.cc }} ${{ matrix.m32 && '| -m32' || '' }}
env:
CI_BUILD_STAGE_NAME: build
CI_RUNS_ON: ${{ matrix.os }}
CI_MAKE_SRC_TARGET: ${{ matrix.debug }}
CI_M32: ${{ matrix.m32 }}
CC: ${{ matrix.compiler.cc }}
CXX: ${{ matrix.compiler.cxx }}
CACHE_KEY: build-${{ matrix.os }}-${{ matrix.compiler.cc }}-${{ matrix.debug }}
CCACHE_MAXSIZE: 256Mi # Per build matrix entry
CACHE_BASE_KEY: build-${{ matrix.os }}-${{ matrix.compiler.cc }}-m32=${{ matrix.m32 }}
CCACHE_MAXSIZE: 250M # Per build matrix entry (2000M in total)
VERILATOR_ARCHIVE: verilator-${{ github.sha }}-${{ matrix.os }}-${{ matrix.compiler.cc }}${{ matrix.m32 && '-m32' || '' }}.tar.gz
steps:
- name: Checkout
uses: actions/checkout@v2
with:
path: repo
- name: Cache
- name: Cache $CCACHE_DIR
uses: actions/cache@v2
env:
cache-name: ccache
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
with:
path: ${{ github.workspace }}/.ccache
key: ${{ env.CACHE_KEY }}-${{ env.cache-name }}-${{ github.sha }}
restore-keys: ${{ env.CACHE_KEY }}-${{ env.cache-name }}
path: ${{ env.CCACHE_DIR }}
key: ${{ env.CACHE_KEY }}-${{ github.sha }}
restore-keys: |
${{ env.CACHE_KEY }}-
- name: Install packages for build
run: ./ci/ci-install.bash
- name: Build
run: |
ccache -z
./ci/ci-script.bash
ccache -s
tar cvzf verilator-${{ matrix.os}}-${CI_COMMIT}-${{ matrix.compiler.cc }}-${{ matrix.debug }}.tgz ./bin
run: ./ci/ci-script.bash
- uses: actions/upload-artifact@v2
- name: Tar up repository
working-directory: ${{ github.workspace }}
run: tar --posix -c -z -f ${{ env.VERILATOR_ARCHIVE }} repo
- name: Upload tar archive
uses: actions/upload-artifact@v2
with:
path: verilator-${{ matrix.os}}-${{ env.CI_COMMIT }}-${{ matrix.compiler.cc }}-${{ matrix.debug }}.tgz
path: ${{ github.workspace }}/${{ env.VERILATOR_ARCHIVE }}
name: ${{ env.VERILATOR_ARCHIVE }}
Test:
needs: [ Matrix, Build ]
test:
needs: build
strategy:
fail-fast: false
matrix:
os: ${{ fromJson(needs.Matrix.outputs.matrix) }}
os: [ubuntu-20.04, ubuntu-18.04]
compiler:
- { cc: clang, cxx: clang++ }
- { cc: gcc, cxx: g++ }
suite:
- dist-vlt-0
- dist-vlt-1
- dist-vlt-2
- vltmt-0
- vltmt-1
m32: [0, 1]
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, vltmt-0, vltmt-1]
exclude:
# Build pull requests only with ubuntu-20.04 and without m32
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
# Build -m32 only on ubuntu-20.04
- {os: ubuntu-18.04, m32: 1}
runs-on: ${{ matrix.os }}
name: Test | ${{ matrix.os }} | ${{ matrix.compiler.cc }} | ${{ matrix.suite }}
name: Test | ${{ matrix.os }} | ${{ matrix.compiler.cc }} | ${{ matrix.suite }} ${{ matrix.m32 && '| -m32' || '' }}
env:
CI_BUILD_STAGE_NAME: test
CI_RUNS_ON: ${{ matrix.os }}
CI_M32: ${{ matrix.m32 }}
CC: ${{ matrix.compiler.cc }}
CXX: ${{ matrix.compiler.cxx }}
CACHE_KEY: test-${{ matrix.os }}-${{ matrix.compiler.cc }}-${{ matrix.suite }}
CCACHE_MAXSIZE: 256Mi # Per build matrix entry
CACHE_BASE_KEY: test-${{ matrix.os }}-${{ matrix.compiler.cc }}-m32=${{ matrix.m32 }}-${ matrix.suite }}
CCACHE_MAXSIZE: 64M # Per build matrix entry (2160M in total)
VERILATOR_ARCHIVE: verilator-${{ github.sha }}-${{ matrix.os }}-${{ matrix.compiler.cc }}${{ matrix.m32 && '-m32' || '' }}.tar.gz
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Download tar archive
uses: actions/download-artifact@v2
with:
name: ${{ env.VERILATOR_ARCHIVE }}
path: ${{ github.workspace }}
- name: Cache
- name: Unpack tar archive
working-directory: ${{ github.workspace }}
run: tar -x -z -f ${{ env.VERILATOR_ARCHIVE }}
- name: Cache $CCACHE_DIR
uses: actions/cache@v2
env:
cache-name: ccache
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
with:
path: ${{ github.workspace }}/.ccache
key: ${{ env.CACHE_KEY }}-${{ env.cache-name }}-${{ github.sha }}
restore-keys: ${{ env.CACHE_KEY }}-${{ env.cache-name }}
path: ${{ env.CCACHE_DIR }}
key: ${{ env.CACHE_KEY }}-${{ github.sha }}
restore-keys: |
${{ env.CACHE_KEY }}-
- uses: actions/download-artifact@v2
- name: Install Verilator and test dependencies
run: |
tar xvzf artifact/verilator-${{ matrix.os}}-${CI_COMMIT}-${{ matrix.compiler.cc }}-opt.tgz
tar xvzf artifact/verilator-${{ matrix.os}}-${CI_COMMIT}-${{ matrix.compiler.cc }}-dbg.tgz
rm -rf artifact
./ci/ci-install.bash
- name: Install test dependencies
run: ./ci/ci-install.bash
- name: Test
env:
TESTS: ${{ matrix.suite }}
run: |
ccache -z
./ci/ci-script.bash
ccache -s
run: ./ci/ci-script.bash
+27 -43
View File
@@ -11,9 +11,14 @@ on:
env:
CI_OS_NAME: linux
CI_COMMIT: ${{ github.sha }}
CCACHE_COMPRESS: 1
CCACHE_DIR: ${{ github.workspace }}/.ccache
CI_M32: 0
COVERAGE: 1
VERILATOR_ARCHIVE: verilator-coverage-${{ github.sha }}.tar.gz
defaults:
run:
shell: bash
working-directory: repo
jobs:
@@ -23,37 +28,28 @@ jobs:
env:
CI_BUILD_STAGE_NAME: build
CI_RUNS_ON: ubuntu-20.04
CACHE_KEY: coverage-build
CCACHE_MAXSIZE: 512Mi
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Cache
uses: actions/cache@v2
env:
cache-name: ccache
with:
path: ${{ github.workspace }}/.ccache
key: ${{ env.CACHE_KEY }}-${{ env.cache-name }}-${{ github.sha }}
restore-keys: ${{ env.CACHE_KEY }}-${{ env.cache-name }}
path: repo
- name: Install packages for build
env:
CI_BUILD_STAGE_NAME: build
run: ./ci/ci-install.bash
- name: Build
run: |
ccache -z
./ci/ci-script.bash
ccache -s
tar cvzf verilator-${CI_COMMIT}-coverage.tgz bin src/obj*/*.o src/obj*/*.gcno
run: ./ci/ci-script.bash
- uses: actions/upload-artifact@v2
- name: Tar up repository
working-directory: ${{ github.workspace }}
run: tar --posix -c -z -f ${{ env.VERILATOR_ARCHIVE }} repo
- name: Upload tar archive
uses: actions/upload-artifact@v2
with:
path: verilator-${{ env.CI_COMMIT }}-coverage.tgz
path: ${{ github.workspace }}/${{ env.VERILATOR_ARCHIVE }}
name: ${{ env.VERILATOR_ARCHIVE }}
Test:
@@ -82,34 +78,22 @@ jobs:
env:
CI_BUILD_STAGE_NAME: test
CI_RUNS_ON: ubuntu-20.04
CACHE_KEY: coverage-test-${{ matrix.test }}${{ matrix.num }}
CCACHE_MAXSIZE: 512Mi # Per build matrix entry
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Cache
uses: actions/cache@v2
env:
cache-name: ccache
- name: Download tar archive
uses: actions/download-artifact@v2
with:
path: ${{ github.workspace }}/.ccache
key: ${{ env.CACHE_KEY }}-${{ env.cache-name }}-${{ github.sha }}
restore-keys: ${{ env.CACHE_KEY }}-${{ env.cache-name }}
name: ${{ env.VERILATOR_ARCHIVE }}
path: ${{ github.workspace }}
- uses: actions/download-artifact@v2
- name: Unpack tar archive
working-directory: ${{ github.workspace }}
run: tar -x -z -f ${{ env.VERILATOR_ARCHIVE }}
- name: Install Verilator and test dependencies
run: |
tar xvzf artifact/verilator-${CI_COMMIT}-coverage.tgz
touch src/obj*/*.o src/obj*/*.gcno
./ci/ci-install.bash
- name: Install test dependencies
run: ./ci/ci-install.bash
- name: Test
env:
TESTS: coverage-${{ matrix.test }}${{ matrix.num }}
run: |
ccache -z
./ci/ci-script.bash
ccache -s
run: ./ci/ci-script.bash
+3 -2
View File
@@ -16,6 +16,7 @@ jobs:
CI_OS_NAME: linux
CI_RUNS_ON: ubuntu-20.04
CI_COMMIT: ${{ github.sha }}
CI_M32: 0
steps:
- name: Checkout
uses: actions/checkout@v2
@@ -26,14 +27,14 @@ jobs:
CI_BUILD_STAGE_NAME: build
run: |
bash ci/ci-install.bash &&
sudo apt-get install clang-10 yapf3 &&
sudo apt-get install clang-format-11 yapf3 &&
git config --global user.email "[email protected]" &&
git config --global user.name "github action"
- name: Format code
run: |
autoconf &&
./configure &&
make -j 2 format CLANGFORMAT=clang-format-10 &&
make -j 2 format CLANGFORMAT=clang-format-11 &&
git status
- name: Push
run: |
+1
View File
@@ -39,3 +39,4 @@ verilator-config-version.cmake
**/__pycache__/*
**/_build/*
**/obj_dir/*
/.vscode/
+112
View File
@@ -8,6 +8,118 @@ The changes in each Verilator version are described below. The
contributors that suggested a given feature are shown in []. Thanks!
Verilator 4.216 2021-12-05
==========================
**Major:**
* Add --lib-create, similar to --protect-lib but without protections.
* Support tracing through --hierarchical/--lib-create libraries (#3200).
**Minor:**
* Internal code cleanups and improvements. [Geza Lore]
* 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).
* Fix array method names with parenthesis (#3181) (#3183). [Teng Huang]
* Fix split_var assign merging (#3177) (#3179). [Yutetsu TAKATSUKASA]
* Fix wrong bit op tree optimization (#3185). [Yutetsu TAKATSUKASA]
* Fix some SliceSels not being constants (#3186) (#3218). [Michaël Lefebvre]
* Fix nested generate if genblk naming (#3189). [yanx21]
* Fix hang on recursive definition error (#3199). [Jonathan Kimmitt]
* Fix display of signed without format (#3204). [Julie Schwartz]
* Fix display of empty string constant (#3207) (#3215). [Julie Schwartz]
* Fix incorrect width after and-or optimization (#3208). [Julie Schwartz]
* Fix $fopen etc on integer arrays (#3214). [adrienlemasle]
* Fix $size on dynamic strings (#3216).
* Fix %0 format on $value$plusargs (#3217).
* Fix timescale portability on Arm64 (#3222).
Verilator 4.214 2021-10-17
==========================
**Major:**
* Add profile-guided optmization of mtasks (#3150).
**Minor:**
* Verilator_gantt has removed the ASCII graphics, use the VCD output instead.
* Verilator_gantt now shows the predicted mtask times, eval times, and additional statistics.
* Verilator_gantt data files now include processor information, to allow later processing.
* Support displaying x and z in $display task (#3107) (#3109). [Iru Cai]
* Fix verilator_profcfunc profile accounting (#3115).
* Fix display has no time units on class function (#3116). [Damien Pretet]
* Fix removing if statement with side effect in condition (#3131). [Alexander Grobman]
* Fix --waiver-output for multiline warnings (#2429) (#3141). [Keith Colbert]
* Fix internal error on bad widths (#3140) (#3145). [Zhanglei Wang]
* Fix crash on clang 12/13 (#3148). [Kouping Hsu]
* Fix cygwin compile error due to missing -std=gnu++14 (#3149). [Sun Kim]
* Fix $urandom_range when the range is 0 ... UINT_MAX (#3161). [Iru Cai]
* Fix constructor-parameter argument comma-separation in C++ (#3162). [Matthew Ballance]
* Fix missing install of vl_file_copy/vl_hier_graph (#3165). [Popolon]
* Fix calling new with arguments in same class (#3166). [Matthew Ballance]
* Fix false EOFNEWLINE warning when DOS carriage returns present (#3171).
Verilator 4.212 2021-09-01
==========================
**Minor:**
* Fix re-evaluation of logic dependent on state set in DPI exports (#3091). [Geza Lore]
* Support unpacked array localparams in tasks/functions (#3078). [Geza Lore]
* Support timeunit/timeprecision in $unit.
* Support assignment patterns as children of pins (#3041). [Krzysztof Bieganski]
* Add --instr-count-dpi to tune assumed DPI import cost for multithreaded
model scheduling. Default value changed to 200 (#3068). [Yinan Xu]
* Output files are split based on the set of headers required
in order to aid incremental compilation via ccache (#3071). [Geza Lore]
* Parameter values are now emitted as 'static constexpr' instead of enum.
C++ direct references to parameters might require updating (#3077). [Geza Lore]
* Refactored Verilated include files; include verilated.h not verilated_heavy.h.
* Add header guards on Dpi.h generated files (#2979). [Tood Strader]
* Add XML ccall, constpool, initarray, and if/while begins (#3080). [Steven Hugg]
* Add error when constant function under a generate (#3103). [Don Owen]
* Fix -G to treat simple integer literals as signed (#3060). [Anikin1610]
* Fix emitted string array initializers (#2895). [Iztok Jeras]
* Fix bitop tree optimization dropping necessary & operator (#3096). [Flavien Solt]
* Fix internal error on wide -x-initial unique (#3106). [Alexandre Joannou]
* Fix traces to show array instances with brackets (#3092) (#3095). [Pieter Kapsenberg]
Verilator 4.210 2021-07-07
==========================
**Major:**
* Generated code is now emitted as global functions rather than methods. '$c'
contents might need to be updated, see the docs (#3006). [Geza Lore]
* The generated model class instantiated by the user is now an interface
object and no longer the TOP module instance. User code with direct
C++ member access to model internals, including verilator public_flat
items will likely need to be updated. See the manual for instructions:
https://verilator.org/guide/latest/connecting.html#porting-from-pre-4-210
(#3036). [Geza Lore]
**Minor:**
* Add --prof-c to pass profiling to compiler (#3059). [Alexander Grobman]
* Optimize a lot more model variables into function locals (#3027). [Geza Lore]
* Support middle-of-design nested topmodules (#3026). [Dan Petrisko]
* Remove deprecated --no-relative-cfuncs option (#3024). [Geza Lore]
* Remove deprecated --inhibit-sim option (#3035). [Geza Lore]
* Merge const static data globally into a new constant pool (#3013). [Geza Lore]
* Allow configure override of AR program (#2999). [ahouska]
* In XML, show pinIndex information (#2877). [errae233]
* Fix error on unsupported recursive functions (#2957). [Trefor Southwell]
* Fix type parameter specialization when struct names are same (#3055). [7FM]
* Improve speed of table optimization (-OA) pass. [Geza Lore]
Verilator 4.204 2021-06-12
==========================
-76
View File
@@ -1,76 +0,0 @@
\.ccache/
\.clang-format
\.clang-tidy
\.git/
\.git$
\.github/
\.svn/
\.(bak|old)/
\.(bak|old)$
\.tar\.
.*\.tgz
.*\.log
\..*\.swp
.*\.tmp
.*\.tidy
.*\.tex
.*\.key
.*\.vcd
.*\.1
\.codacy\.yml
\.travis\.yml
/_build/
/build/
/obj_dbg/
/obj_dir/
/obj_dist/
/obj_iv/
/obj_ms/
/obj_nc/
/obj_opt/
/obj_vcs/
/obj_vlt/
/obj_vltmt/
INCA_libs/
/cov_work/
/logs/
^Makefile$
README.html
bin/verilator_bin.*
bin/verilator_coverage_bin.*
docs/.*\.html$
docs/_build/
docs/clang-format.txt$
docs/doxygen-doc/.*
docs/spelling.txt
examples/xml_py/copied/
examples/xml_py/graph.*
sonar-project.properties
src/Makefile$
src/Makefile_obj$
include/verilated.mk$
test_regress/.gdbinit$
test_regress/transcript
codecov.yml
config.cache$
config.status$
verilator\.log
verilator\.tex
verilator-config.cmake$
verilator-config-version.cmake$
verilator.pc$
verilator_bin.*
verilator_coverage_bin.*
.vcsmx_rebuild$
ncverilog.history
autom4te\.cache/
nodist/
ci/
/simv$
/simv.daidir/
/vc_hdrs.h$
/csrc/
obj_dir.*
TAGS
gmon.out
.*~
+16 -171
View File
@@ -118,98 +118,6 @@ INFOS = verilator.html verilator.pdf
INFOS_OLD = README README.html README.pdf
# Files that can be generated, but should be up to date for a distribution.
DISTDEP = info Makefile
DISTFILES1 = $(INFOS) .gitignore \
*.in *.ac \
Artistic \
Changes \
LICENSE \
MANIFEST.SKIP \
README.rst \
verilator-config.cmake.in \
verilator-config-version.cmake.in \
bin/verilator \
bin/verilator_ccache_report \
bin/verilator_coverage \
bin/verilator_difftree \
bin/verilator_gantt \
bin/verilator_includer \
bin/verilator_profcfunc \
docs/.gitignore \
docs/CONTRIBUTING.rst \
docs/CONTRIBUTORS \
docs/Makefile \
docs/_static/*.png \
docs/_static/css/* \
docs/bin/* \
docs/gen/* \
docs/guide/*.py \
docs/guide/*.rst \
docs/guide/figures/* \
docs/install.rst \
docs/internals.rst \
docs/internals.rst \
docs/verilated.dox \
docs/xml.rst \
install-sh configure *.pod \
include/*.[chv]* \
include/*.in \
include/.*ignore \
include/gtkwave/*.[chv]* \
include/vltstd/*.[chv]* \
.*attributes */.*attributes */*/.*attributes \
src/.*ignore src/*.in src/*.cpp src/*.[chly] \
src/astgen src/bisonpre src/*fix src/cppcheck_filtered \
src/config_rev \
src/vlcovgen src/mkinstalldirs \
src/.gdbinit \
src/*.pl src/*.pod \
examples/*/.*ignore examples/*/Makefile* \
examples/*/*.[chv]* examples/*/vl_* \
examples/*/CMakeLists.txt \
DISTFILES2 = \
test_*/.*ignore test_*/Makefile* test_*/*.cpp \
test_*/*.pl test_*/*.v test_*/*.vc test_*/*.vh \
test_regress/*.pl \
test_regress/Makefile \
test_regress/Makefile_obj \
test_regress/input.vc \
test_regress/CMakeLists.txt \
test_regress/t/t*/*.sv* \
test_regress/t/t*/*.v* \
test_regress/t/t*/*/*.sv* \
test_regress/t/t*/*/*.v* \
test_regress/t/t*/*.cpp \
test_regress/t/t*/CMakeLists.txt \
test_regress/t/*.cpp \
test_regress/t/*.h \
test_regress/t/*.dat \
test_regress/t/*.mem \
test_regress/t/*.out \
test_regress/t/*.pl \
test_regress/t/*.pf \
test_regress/t/*.v* \
INST_PROJ_FILES = \
bin/verilator \
bin/verilator_ccache_report \
bin/verilator_coverage \
bin/verilator_gantt \
bin/verilator_includer \
bin/verilator_profcfunc \
include/verilated.mk \
include/*.[chv]* \
include/gtkwave/*.[chv]* \
include/vltstd/*.[chv]* \
INST_PROJ_BIN_FILES = \
bin/verilator_bin$(EXEEXT) \
bin/verilator_bin_dbg$(EXEEXT) \
bin/verilator_coverage_bin_dbg$(EXEEXT) \
EXAMPLES_FIRST = \
examples/make_hello_c \
examples/make_hello_sc \
@@ -217,7 +125,7 @@ EXAMPLES_FIRST = \
EXAMPLES = $(EXAMPLES_FIRST) $(filter-out $(EXAMPLES_FIRST), $(sort $(wildcard examples/*)))
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
VL_INST_MAN_FILES = verilator.1 verilator_coverage.1 verilator_gantt.1 verilator_profcfunc.1
VL_INST_MAN_FILES = verilator.1 verilator_coverage.1
default: all
all: all_nomsg msg_test
@@ -230,7 +138,7 @@ all_nomsg: verilator_exe $(VL_INST_MAN_FILES)
verilator_exe verilator_bin$(EXEEXT) verilator_bin_dbg$(EXEEXT) verilator_coverage_bin_dbg$(EXEEXT):
@echo ------------------------------------------------------------
@echo "making verilator in src"
$(MAKE) -C src $(OBJCACHE_JOBS) $(CI_MAKE_SRC_TARGET)
$(MAKE) -C src $(OBJCACHE_JOBS)
.PHONY:msg_test
msg_test: all_nomsg
@@ -299,8 +207,10 @@ VL_INST_INC_SRCDIR_FILES = \
include/vltstd/*.[chv]* \
VL_INST_DATA_SRCDIR_FILES = \
examples/*/*.[chv]* examples/*/Makefile* \
examples/*/CMakeLists.txt
examples/*/*.[chv]* \
examples/*/CMakeLists.txt \
examples/*/Makefile* \
examples/*/vl_* \
installbin:
$(MKINSTALLDIRS) $(DESTDIR)$(bindir)
@@ -386,44 +296,7 @@ uninstall:
install: all_nomsg install-all
install-all: installbin installman installdata install-msg
install-here: installman ftp
ifeq ($(VERILATOR_AUTHOR_SITE),1) # Local... Else don't burden users
DISTNAMEREV = $(shell sed -e '/DTVERSION/!d' -e 's/.*verilator_\([^"]*\).*/\1/' -e q ${srcdir}/src/config_rev.h)
VERILATOR_CAD_DIR = $(CAD_DIR)/verilator/$(DISTNAMEREV)/$(DIRPROJECT_ARCH)
INST_PROJ_CVS = cp_if_cvs_diff
install-cadtools: dist
@echo "Install-project to $(CAD_DIR)"
strip bin/verilator_bin*
strip bin/verilator_coverage_bin*
$(MAKE) install-cadtools-quick
$(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/man/man1
for p in $(VL_INST_MAN_FILES) ; do \
$(INSTALL_DATA) $$p $(VERILATOR_CAD_DIR)/man/man1/$$p; \
done
$(INST_PROJ_CVS) $(DISTNAME).tgz $(VERILATOR_CAD_DIR)/verilator.tgz
rm $(DISTNAME).tgz
install-cadtools-quick:
ifeq ($(CFG_WITH_DEFENV),yes)
@echo "%Error: Reconfigure with './configure --disable-defenv' to avoid hardcoded paths."
false
endif
@echo "Install-cadtools-quick (no strip) to $(VERILATOR_CAD_DIR)"
$(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/include/gtkwave
$(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/include/vltstd
$(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/bin
for p in $(INST_PROJ_FILES) ; do \
$(INST_PROJ_CVS) $$p $(VERILATOR_CAD_DIR)/$$p; \
done
for p in $(INST_PROJ_BIN_FILES) ; do \
$(INST_PROJ_CVS) $$p $(VERILATOR_CAD_DIR)/$$p; \
done
# VERILATOR_AUTHOR_SITE
endif
install-here: installman info
# Use --xml flag to see the cppcheck code to use for suppression
CPPCHECK_CPP = $(wildcard \
@@ -470,17 +343,20 @@ analyzer-include:
format: clang-format yapf format-pl-exec
CLANGFORMAT = clang-format
CLANGFORMAT = clang-format-11
CLANGFORMAT_FLAGS = -i
CLANGFORMAT_FILES = $(CPPCHECK_CPP) $(CPPCHECK_H) $(CPPCHECK_YL) test_regress/t/*.c* test_regress/t/*.h
clang-format:
@$(CLANGFORMAT) --version | egrep 10.0 > /dev/null \
|| echo "*** You are not using clang-format 10.0, indents may differ from master's ***"
@$(CLANGFORMAT) --version | egrep 11.0 > /dev/null \
|| echo "*** You are not using clang-format 11.0, indents may differ from master's ***"
$(CLANGFORMAT) $(CLANGFORMAT_FLAGS) $(CLANGFORMAT_FILES)
PY_PROGRAMS = \
bin/verilator_ccache_report \
bin/verilator_difftree \
bin/verilator_gantt \
bin/verilator_profcfunc \
examples/xml_py/vl_file_copy \
examples/xml_py/vl_hier_graph \
docs/guide/conf.py \
@@ -492,6 +368,7 @@ PY_PROGRAMS = \
src/cppcheck_filtered \
src/flexfix \
src/vlcovgen \
test_regress/t/*.pf \
nodist/code_coverage \
nodist/dot_importer \
nodist/fuzzer/actual_fail \
@@ -523,9 +400,6 @@ lint-py:
format-pl-exec:
-chmod a+x test_regress/t/*.pl
ftp: info
install-msg:
@echo
@echo "Installed binaries to $(DESTDIR)$(bindir)/verilator"
@@ -596,15 +470,6 @@ TAGS: $(TAGFILES)
doxygen:
$(MAKE) -C docs doxygen
######################################################################
# Test targets
dist-file-list:
@echo "begin-dist-file-list:"; # Scripts look for this
@echo $(wildcard $(DISTFILES1))
@echo $(wildcard $(DISTFILES2))
@echo "end-dist-file-list:"; # Scripts look for this
######################################################################
# Distributions
@@ -612,36 +477,16 @@ DISTTITLE := Verilator $(word 1,$(PACKAGE_VERSION))
DISTNAME := verilator-$(word 1,$(PACKAGE_VERSION))
DISTDATEPRE := $(word 2,$(PACKAGE_VERSION))
DISTDATE := $(subst /,-,$(DISTDATEPRE))
DISTTAGNAME := $(subst .,_,$(subst -,_,$(DISTNAME)))
tag:
svnorcvs tag $(DISTTAGNAME)
# Don't depend on DISTFILES because there's no rule for "standards.info*".
dist: $(DISTDEP) maintainer-copy
-rm -fr $(DISTNAME)
for file in $(DISTFILES1); do \
mkdir -p `dirname $(DISTNAME)/$$file` >/dev/null ; \
ln $$file $(DISTNAME)/$$file \
|| { echo copying $$file instead; cp -p $$file $(DISTNAME)/$$file;}; \
done; true;
for file in $(DISTFILES2); do \
mkdir -p `dirname $(DISTNAME)/$$file` >/dev/null ; \
ln $$file $(DISTNAME)/$$file \
|| { echo copying $$file instead; cp -p $$file $(DISTNAME)/$$file;}; \
done; true;
chmod -R a+r $(DISTNAME)
tar chf $(DISTNAME).tar $(DISTNAME)
gzip --force --best $(DISTNAME).tar
mv $(DISTNAME).tar.gz $(DISTNAME).tgz
rm -fr $(DISTNAME)
maintainer-diff:
svnorcvs diff $(DISTTAGNAME)
preexist:
svnorcvs nexists $(DISTTAGNAME)
maintainer-dist: preexist dist tag
svnorcvs release $(DISTNAME).tgz
maintainer-dist: preexist tag
svnorcvs release $(DISTTAGNAME)
+3
View File
@@ -5,6 +5,8 @@
:target: https://www.gnu.org/licenses/lgpl-3.0]
.. image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
:target: https://opensource.org/licenses/Artistic-2.0
.. image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
:target: https://repology.org/project/verilator/versions
.. image:: https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f
:target: https://www.codacy.com/gh/verilator/verilator
.. image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg
@@ -12,6 +14,7 @@
.. image:: https://github.com/verilator/verilator/workflows/build/badge.svg
:target: https://github.com/verilator/verilator/actions?query=workflow%3Abuild
Welcome to Verilator
====================
+38 -36
View File
@@ -34,11 +34,11 @@ my $opt_quiet_exit;
# No arguments can't do anything useful. Give help
if ($#ARGV < 0) {
pod2usage(-exitstatus=>2, -verbose=>0);
pod2usage(-exitstatus => 2, -verbose => 0);
}
# Insert debugging options up front
push @ARGV, (split ' ',$ENV{VERILATOR_TEST_FLAGS}||"");
push @ARGV, (split ' ', $ENV{VERILATOR_TEST_FLAGS} || "");
# We sneak a look at the flags so we can do some pre-environment checks
# All flags will hit verilator...
@@ -46,7 +46,7 @@ foreach my $sw (@ARGV) {
push @Opt_Verilator_Sw, $sw;
}
Getopt::Long::config("no_auto_abbrev","pass_through");
Getopt::Long::config("no_auto_abbrev", "pass_through");
if (! GetOptions(
# Major operating modes
"help" => \&usage,
@@ -60,7 +60,7 @@ if (! GetOptions(
# Additional parameters
"<>" => sub {}, # Ignored
)) {
pod2usage(-exitstatus=>2, -verbose=>0);
pod2usage(-exitstatus => 2, -verbose => 0);
}
if ($opt_gdbbt && !gdb_works()) {
@@ -73,12 +73,12 @@ if ($opt_gdbbt && !gdb_works()) {
# Starting with that, escape all special chars for the shell;
# The shell will undo the escapes and the verilator binary should
# then see exactly the contents of @Opt_Verilator_Sw.
my @quoted_sw = map {sh_escape($_)} @Opt_Verilator_Sw;
my @quoted_sw = map { sh_escape($_) } @Opt_Verilator_Sw;
if ($opt_gdb) {
# Generic GDB interactive
run (aslr_off()
.($ENV{VERILATOR_GDB}||"gdb")
." ".verilator_bin()
. ($ENV{VERILATOR_GDB} || "gdb")
. " " . verilator_bin()
# Note, uncomment to set breakpoints before running:
# ." -ex 'break main'"
@@ -87,42 +87,41 @@ if ($opt_gdb) {
# escapes as you would expect in a double-quoted string.
# That's not true for a single-quoted string, where \'
# actually terminates the string -- not what we want!
." -ex \"run ".join(' ', @quoted_sw)."\""
." -ex 'set width 0'"
." -ex 'bt'");
. " -ex \"run " . join(' ', @quoted_sw) . "\""
. " -ex 'set width 0'"
. " -ex 'bt'");
} elsif ($opt_rr) {
# Record with rr
run (aslr_off()
."rr record ".verilator_bin()
." ".join(' ', @quoted_sw));
. "rr record " . verilator_bin()
. " " . join(' ', @quoted_sw));
} elsif ($opt_gdbbt && $Debug) {
# Run under GDB to get gdbbt
run (aslr_off()
."gdb"
." ".verilator_bin()
." --batch --quiet --return-child-result"
." -ex \"run ".join(' ', @quoted_sw)."\""
." -ex 'set width 0'"
." -ex 'bt' -ex 'quit'");
. "gdb"
. " " . verilator_bin()
. " --batch --quiet --return-child-result"
. " -ex \"run " . join(' ', @quoted_sw)."\""
. " -ex 'set width 0'"
. " -ex 'bt' -ex 'quit'");
} elsif ($Debug) {
# Debug
run(aslr_off()
.verilator_bin()." ".join(' ',@quoted_sw));
run(aslr_off() . verilator_bin() . " " . join(' ', @quoted_sw));
} else {
# Normal, non gdb
run(verilator_bin()." ".join(' ',@quoted_sw));
run(verilator_bin() . " " . join(' ', @quoted_sw));
}
#----------------------------------------------------------------------
sub usage {
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
pod2usage(-verbose => 2, -exitval => 0, -output => \*STDOUT);
}
sub debug {
shift;
my $level = shift;
$Debug = $level||3;
$Debug = $level || 3;
}
#######################################################################
@@ -163,13 +162,13 @@ sub verilator_bin {
sub gdb_works {
$! = undef; # Cleanup -x
system("gdb /bin/echo"
." --batch-silent --quiet --return-child-result"
." -ex 'run -n'" # `echo -n`
." -ex 'set width 0'"
." -ex 'bt'"
." -ex 'quit'");
. " --batch-silent --quiet --return-child-result"
. " -ex 'run -n'" # `echo -n`
. " -ex 'set width 0'"
. " -ex 'bt'"
. " -ex 'quit'");
my $status = $?;
return $status==0;
return $status == 0;
}
sub aslr_off {
@@ -185,7 +184,7 @@ sub run {
# Run command, check errors
my $command = shift;
$! = undef; # Cleanup -x
print "\t$command\n" if $Debug>=3;
print "\t$command\n" if $Debug >= 3;
system($command);
my $status = $?;
if ($status) {
@@ -193,7 +192,7 @@ sub run {
warn "%Error: verilator: Misinstalled, or VERILATOR_ROOT might need to be in environment\n";
}
if ($Debug) { # For easy rerunning
warn "%Error: export VERILATOR_ROOT=".($ENV{VERILATOR_ROOT}||"")."\n";
warn "%Error: export VERILATOR_ROOT=" . ($ENV{VERILATOR_ROOT} || "") . "\n";
warn "%Error: $command\n";
}
if ($status & 127) {
@@ -201,13 +200,13 @@ sub run {
|| ($status & 127) == 8 # SIGFPA
|| ($status & 127) == 11) { # SIGSEGV
warn "%Error: Verilator internal fault, sorry. "
."Suggest trying --debug --gdbbt\n" if !$Debug;
. "Suggest trying --debug --gdbbt\n" if !$Debug;
} elsif (($status & 127) == 6) { # SIGABRT
warn "%Error: Verilator aborted. "
."Suggest trying --debug --gdbbt\n" if !$Debug;
. "Suggest trying --debug --gdbbt\n" if !$Debug;
} else {
warn "%Error: Verilator threw signal $status. "
."Suggest trying --debug --gdbbt\n" if !$Debug;
. "Suggest trying --debug --gdbbt\n" if !$Debug;
}
}
if (!$opt_quiet_exit && ($status != 256 || $Debug)) { # i.e. not normal exit(1)
@@ -332,11 +331,12 @@ detailed descriptions of these arguments.
--gate-stmts <value> Tune gate optimizer depth
--if-depth <value> Tune IFDEPTH warning
+incdir+<dir> Directory to search for includes
--inhibit-sim Create function to turn off sim
--inline-mult <value> Tune module inlining
--instr-count-dpi <value> Assumed dynamic instruction count of DPI imports
-LDFLAGS <flags> Linker pre-object arguments for makefile
--l2-name <value> Verilog scope name of the top module
--language <lang> Default language standard to parse
--lib-create <name> Create a DPI library
+libext+<ext>+[ext]... Extensions for finding modules
--lint-only Lint, but do not make output
-MAKEFLAGS <flags> Arguments to pass to make during --build
@@ -344,6 +344,7 @@ detailed descriptions of these arguments.
--MMD Create .d dependency files
--MP Create phony dependency targets
--Mdir <directory> Name of output object directory
--no-merge-const-pool Disable merging of different types in const pool
--mod-prefix <topname> Name to prepend to lower classes
--no-clk <signal-name> Prevent marking specified signal as clock
--no-decoration Disable comments and symbol decorations
@@ -367,6 +368,7 @@ detailed descriptions of these arguments.
--pipe-filter <command> Filter all input through a script
--pp-comments Show preprocessor comments with -E
--prefix <topname> Name of top level class
--prof-c Compile C++ code with profiling
--prof-cfuncs Name functions for profiling
--prof-threads Enable generating gantt chart data for threads
--protect-key <key> Key for symbol protection
@@ -378,7 +380,6 @@ detailed descriptions of these arguments.
-pvalue+<name>=<value> Overwrite toplevel parameter
--quiet-exit Don't print the command on failure
--relative-includes Resolve includes relative to current file
--no-relative-cfuncs Disallow 'this->' in generated functions
--reloop-limit Minimum iterations for forming loops
--report-unoptflat Extra diagnostics for UNOPTFLAT
--rr Run Verilator and record with rr
@@ -447,6 +448,7 @@ description of these arguments.
+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+rand+reset+<value> Set random reset technique
+verilator+seed+<value> Set random seed
+verilator+V Verbose version and config
+2 -1
View File
@@ -88,6 +88,7 @@ else:
args.o.write("{:{width}}| {}s\n".format(k,
v.total_seconds(),
width=wnames))
if i > 4: break
if i > 4:
break
args.o.write("#" * 80 + "\n")
+9 -9
View File
@@ -30,7 +30,7 @@ $Debug = 0;
# No arguments can't do anything useful. Give help
if ($#ARGV < 0) {
pod2usage(-exitstatus=>2, -verbose=>0);
pod2usage(-exitstatus => 2, -verbose => 0);
}
# We sneak a look at the flags so we can do some pre-environment checks
@@ -40,7 +40,7 @@ foreach my $sw (@ARGV) {
push @Opt_Verilator_Sw, $sw;
}
Getopt::Long::config("no_auto_abbrev","pass_through");
Getopt::Long::config("no_auto_abbrev", "pass_through");
if (! GetOptions (
# Major operating modes
"help" => \&usage,
@@ -49,23 +49,23 @@ if (! GetOptions (
# Additional parameters
"<>" => sub {}, # Ignored
)) {
pod2usage(-exitstatus=>2, -verbose=>0);
pod2usage(-exitstatus => 2, -verbose => 0);
}
# Normal, non gdb
run(verilator_coverage_bin()
." ".join(' ',@Opt_Verilator_Sw));
. " " . join(' ', @Opt_Verilator_Sw));
#----------------------------------------------------------------------
sub usage {
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
pod2usage(-verbose => 2, -exitval => 0, -output => \*STDOUT);
}
sub debug {
shift;
my $level = shift;
$Debug = $level||3;
$Debug = $level || 3;
}
#######################################################################
@@ -107,7 +107,7 @@ sub run {
# Run command, check errors
my $command = shift;
$! = undef; # Cleanup -x
print "\t$command\n" if $Debug>=3;
print "\t$command\n" if $Debug >= 3;
system($command);
my $status = $?;
if ($status) {
@@ -115,11 +115,11 @@ sub run {
warn "%Error: verilator_coverage: Misinstalled, or VERILATOR_ROOT might need to be in environment\n";
}
if ($Debug) { # For easy rerunning
warn "%Error: export VERILATOR_ROOT=".($ENV{VERILATOR_ROOT}||"")."\n";
warn "%Error: export VERILATOR_ROOT=" . ($ENV{VERILATOR_ROOT} || "") . "\n";
warn "%Error: $command\n";
}
if ($status & 127) {
if (($status & 127) == 8 || ($status & 127) == 11) { # SIGFPA or SIGSEGV
if (($status & 127) == 8 || ($status & 127) == 11) { # SIGFPA or SIGSEGV
warn "%Error: Verilator_coverage internal fault, sorry.\n" if !$Debug;
} elsif (($status & 127) == 6) { # SIGABRT
warn "%Error: Verilator_coverage aborted.\n" if !$Debug;
+106 -200
View File
@@ -1,233 +1,139 @@
#!/usr/bin/env perl
# See copyright, etc in below POD section.
#!/usr/bin/env python3
# pylint: disable=C0103,C0114,C0116
######################################################################
use warnings;
use Getopt::Long;
use IO::File;
use Pod::Usage;
use strict;
use vars qw($Debug);
import argparse
import collections
import glob
import os.path
import re
import sys
#======================================================================
# main
$Debug = 0;
my $Opt_A;
my $Opt_B;
my $Opt_Lineno = 1;
autoflush STDOUT 1;
autoflush STDERR 1;
Getopt::Long::config("no_auto_abbrev");
if (! GetOptions(
"help" => \&usage,
"debug" => \&debug,
"<>" => \&parameter,
"lineno!" => \$Opt_Lineno,
)) {
die "%Error: Bad usage, try 'verilator_difftree --help'\n";
}
def diff(a, b):
defined $Opt_A or die "%Error: No old diff filename\n";
defined $Opt_B or die "%Error: No new diff filename\n";
if not os.path.exists(a):
sys.exit("%Error: No old diff filename found: " + a)
if not os.path.exists(b):
sys.exit("%Error: No new diff filename found: " + b)
-e $Opt_A or die "%Error: No old diff filename found: $Opt_A\n";
-e $Opt_B or die "%Error: No new diff filename found: $Opt_B\n";
if os.path.isdir(a) and os.path.isdir(b):
diff_dir(a, b)
elif os.path.isfile(a) and os.path.isfile(b):
diff_file(a, b)
else:
sys.exit("%Error: Mix of files and dirs")
if (-d $Opt_A && -d $Opt_B) {
diff_dir($Opt_A, $Opt_B);
} elsif (-f $Opt_A && -f $Opt_B) {
diff_file($Opt_A, $Opt_B);
} else {
die "%Error: Mix of files and dirs\n";
}
sub diff_dir {
my $a = shift;
my $b = shift;
def diff_dir(a, b):
# Diff all files under two directories
my %files;
files = collections.defaultdict(lambda: {})
foreach my $fn (glob("$a/*.tree")) {
(my $base = $fn) =~ s!.*/!!;
$files{$base}{a} = $fn;
}
foreach my $fn (glob("$b/*.tree")) {
(my $base = $fn) =~ s!.*/!!;
$files{$base}{b} = $fn;
}
my $any;
foreach my $base (sort (keys %files)) {
my $a = $files{$base}{a};
my $b = $files{$base}{b};
next if !$a || !$b;
print "="x70,"\n";
print "= $a <-> $b\n";
diff_file($a,$b);
$any = 1;
}
$any or warn("%Warning: No .tree files found that have similar base names:\n "
.join("\n ", sort keys %files),"\n");
}
for fn in glob.glob(a + "/*.tree"):
base = re.sub(r'.*/', '', fn)
files[base]['a'] = fn
for fn in glob.glob(b + "/*.tree"):
base = re.sub(r'.*/', '', fn)
files[base]['b'] = fn
sub diff_file {
my $a = shift;
my $b = shift;
anyfile = False
for base in sorted(files.keys()):
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)
anyfile = True
if not anyfile:
sys.stderr.write(
"%Warning: No .tree files found that have similar base names\n")
def diff_file(a, b):
# Compare the two tree files
(my $short_a = $a) =~ s/[^a-zA-Z0-9.]+/_/g;
(my $short_b = $b) =~ s/[^a-zA-Z0-9.]+/_/g;
my $tmp_a = "/tmp/${$}_${short_a}.a";
my $tmp_b = "/tmp/${$}_${short_b}.b";
short_a = re.sub(r'[^a-zA-Z0-9.]+', '_', a)
short_b = re.sub(r'[^a-zA-Z0-9.]+', '_', b)
tmp_a = "/tmp/%s_%s.a" % (os.getpid(), short_a)
tmp_b = "/tmp/%s_%s.b" % (os.getpid(), short_b)
my $vera = version_from($a);
my $verb = version_from($b);
my $verCvt = (($vera < 0x3900 && $verb >= 0x3900)
|| ($vera >= 0x3900 && $verb < 0x3900));
# Version conversion deprecated, but for future...
# vera = version_from(a)
# verb = version_from(b)
# verCvt = ((vera < 0x3900 and verb >= 0x3900)
# or (vera >= 0x3900 and verb < 0x3900))
filter($a, $tmp_a, $verCvt);
filter($b, $tmp_b, $verCvt);
system("diff -u $tmp_a $tmp_b");
unlink $tmp_a;
unlink $tmp_b;
}
filterf(a, tmp_a)
filterf(b, tmp_b)
os.system("diff -u " + tmp_a + " " + tmp_b)
os.unlink(tmp_a)
os.unlink(tmp_b)
sub version_from {
my $fn = shift;
def version_from(filename):
# Return dump format
my $f1 = IO::File->new ($fn) or die "%Error: $! $fn,";
while (defined (my $line=$f1->getline())) {
last if $. > 10;
return hex $1 if $line =~ /\(format (0x[0-9.]+)\)/;
}
return 1.0;
}
with open(filename) as fh:
lineno = 0
for line in fh:
if lineno > 10:
break
match = re.search(r'format (0x[0-9.]+)', line)
if match:
return hex(match.group(1))
return 1.0
sub filter {
my $fn1 = shift;
my $fn2 = shift;
my $verCvt = shift;
def filterf(fn1, fn2):
# Remove hex numbers before diffing
my $f1 = IO::File->new ($fn1) or die "%Error: $! $fn1,";
my $f2 = IO::File->new ($fn2,"w") or die "%Error: $! $fn2,";
while (defined (my $line=$f1->getline())) {
same_line:
next if $line =~ / This=/;
$line =~ s/0x[a-f0-9]+/0x/g;
$line =~ s/<e[0-9]+\#?>/<e>/g;
$line =~ s/{[a-z]*\d+}/{}/g if !$Opt_Lineno;
if ($verCvt) {
next if $line =~ /^ NETLIST/;
if ($line =~ /: ([A-Z]+) /) {
my $type = $1;
next if $type =~ 'DTYPE';
if ($type eq 'TYPETABLE' || $type eq 'RANGE') {
$line =~ /^(\s+\S+:) /; my $prefix = $1;
while (defined ($line=$f1->getline())) {
next if $line =~ /^\s+[a-z]/; # Table body
next if $line =~ /^${prefix}[0-9]:/;
goto same_line;
}
next;
}
}
}
print $f2 $line;
}
$f1->close;
$f2->close;
}
with open(fn1) as fh1:
with open(fn2, "w") as fh2:
for line in fh1:
if re.search(r' This=', line):
continue
line = re.sub(r'0x[a-f0-9]+', '0x', line)
line = re.sub(r'<e[0-9]+\#?>', '<e>', line)
if not Args.no_lineno:
line = re.sub(r'{[a-z]*\d+}', '{}', line)
fh2.write(line)
#----------------------------------------------------------------------
sub usage {
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
exit(1); # Unreachable
}
######################################################################
######################################################################
sub debug {
$Debug = 1;
}
sub parameter {
my $param = shift;
if (!defined $Opt_A) {
$Opt_A = $param;
} elsif (!defined $Opt_B) {
$Opt_B = $param;
} else {
die "%Error: Unknown parameter: $param\n";
}
}
#######################################################################
sub run {
# Run a system command, check errors
my $command = shift;
print "\t$command\n";
system "$command";
my $status = $?;
($status == 0) or die "%Error: Command Failed $command, $status, stopped";
}
#######################################################################
__END__
=pod
=head1 NAME
verilator_difftree - Compare two Verilator debugging trees
=head1 SYNOPSIS
verilator_difftree .../a/a.tree .../b/a.tree
verilator_difftree .../a .../b
=head1 DESCRIPTION
Verilator_difftree is used for debugging Verilator tree output files. It
performs a diff between two files, or all files common between two
parser = argparse.ArgumentParser(
allow_abbrev=False,
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""Compare two Verilator debugging trees""",
epilog=
"""Verilator_difftree is used for debugging Verilator tree output files.
It performs a diff between two files, or all files common between two
directories, ignoring irrelevant pointer differences.
=head1 ARGUMENTS
=over 4
=item --help
Displays this message and program version and exits.
=item --nolineno
Do not show differences in line numbering.
=back
=head1 DISTRIBUTION
The latest version is available from L<https://verilator.org>.
For documentation see
https://verilator.org/guide/latest/exe_verilator_difftree.html
Copyright 2005-2021 by Wilson Snyder. This program is free software; you
can redistribute it and/or modify it under the terms of either the GNU
Lesser General Public License Version 3 or the Perl Artistic License
Version 2.0.
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
=head1 AUTHORS
parser.add_argument('--debug',
action='store_const',
const=9,
help='enable debug')
parser.add_argument('--no-lineno',
action='store_false',
help='do not show differences in line numbering')
parser.add_argument('filea', help='input file a to diff')
parser.add_argument('fileb', help='input file b to diff')
Wilson Snyder <[email protected]>
=head1 SEE ALSO
C<verilator>
and L<https://verilator.org/verilator_doc.html> for detailed documentation.
=cut
Args = parser.parse_args()
diff(Args.filea, Args.fileb)
######################################################################
### Local Variables:
### compile-command: "$V4/bin/verilator_difftree {$V4D,$V4}/test_regress/obj_dir/t_EXAMPLE/V*_03_*.tree"
### End:
# Local Variables:
# compile-command: "./verilator_difftree ../test_regress/t/t_difftree.{a,b}.tree"
# End:
+428 -511
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -10,11 +10,11 @@
require 5.005;
use warnings;
print "// DESCR"."IPTION: Generated by verilator_includer via makefile\n";
print "// DESCR" . "IPTION: Generated by verilator_includer via makefile\n";
foreach my $param (@ARGV) {
if ($param =~ /^-D([^=]+)=(.*)/) {
print "#define $1 $2\n"
print "#define $1 $2\n";
} else {
print "#include \"$param\"\n"
print "#include \"$param\"\n";
}
}
+173 -217
View File
@@ -1,248 +1,204 @@
#!/usr/bin/env perl
# See copyright, etc in below POD section.
#!/usr/bin/env python3
# pylint: disable=C0103,C0114,C0116,R0914,R0912,R0915,eval-used
######################################################################
require 5.006_001;
use warnings;
use Getopt::Long;
use IO::File;
use Pod::Usage;
eval { use Data::Dumper; $Data::Dumper::Indent = 1; }; # Debug, ok if missing
use strict;
use vars qw($Debug);
import argparse
import collections
import re
# from pprint import pprint
#======================================================================
######################################################################
#======================================================================
# main
def profcfunc(filename):
funcs = {}
$Debug = 0;
my $Opt_File;
autoflush STDOUT 1;
autoflush STDERR 1;
Getopt::Long::config("no_auto_abbrev");
if (! GetOptions(
"help" => \&usage,
"debug" => \&debug,
"<>" => \&parameter,
)) {
die "%Error: Bad usage, try 'verilator_profcfunc --help'\n";
}
with open(filename) as fh:
defined $Opt_File or die "%Error: No filename given\n";
for line in fh:
# %time cumesec selfsec calls {stuff} name
match = re.match(
r'^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$',
line)
if match:
pct = float(match.group(1))
sec = float(match.group(2))
calls = float(match.group(3))
func = match.group(4)
if func not in funcs:
funcs[func] = {'pct': 0, 'sec': 0, 'calls': 0}
funcs[func]['pct'] += pct
funcs[func]['sec'] += sec
funcs[func]['calls'] += calls
continue
profcfunc($Opt_File);
#----------------------------------------------------------------------
sub usage {
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
exit(1); # Unreachable
}
sub debug {
$Debug = 1;
}
sub parameter {
my $param = shift;
if (!defined $Opt_File) {
$Opt_File = $param;
} else {
die "%Error: Unknown parameter: $param\n";
}
}
#######################################################################
sub profcfunc {
my $filename = shift;
# Remove hex numbers before diffing
my $fh = IO::File->new ($filename) or die "%Error: $! $filename,";
my %funcs;
while (defined (my $line=$fh->getline())) {
# %time cumesec selfsec calls {stuff} name
if ($line =~ /^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$/) {
my $pct=$1; my $sec=$2; my $calls=$3; my $func=$4;
$funcs{$func}{pct} += $pct;
$funcs{$func}{sec} += $sec;
$funcs{$func}{calls} += $calls;
}
# Older gprofs have no call column for single-call functions
# %time cumesec selfsec {stuff} name
elsif ($line =~ /^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$/) {
my $pct=$1; my $sec=$2; my $calls=1; my $func=$3;
$funcs{$func}{pct} += $pct;
$funcs{$func}{sec} += $sec;
$funcs{$func}{calls} += $calls;
}
}
$fh->close;
# Older gprofs have no call column for single-call functions
# %time cumesec selfsec {stuff} name
match = re.match(
r'^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$',
line)
if match:
pct = float(match.group(1))
sec = float(match.group(2))
calls = 1
func = match.group(3)
if func not in funcs:
funcs[func] = {'pct': 0, 'sec': 0, 'calls': 0}
funcs[func]['pct'] += pct
funcs[func]['sec'] += sec
funcs[func]['calls'] += calls
continue
# Find modules
my %pointer_mods;
my %verilated_mods;
foreach my $func (keys %funcs) {
if ($func =~ /(.*)::_eval\(([a-zA-Z_0-9]+__Syms).*\)$/) {
$verilated_mods{$1} = qr/^$1/;
$pointer_mods{$2} = $1;
}
}
#print Dumper(\%pointer_mods, \%verilated_mods);
verilated_mods = {}
for func in funcs:
match = re.search(r'(.*)::eval(_step)?\(', func)
if match:
prefix = match.group(1)
if Args.debug:
print("-got _eval %s prefix=%s" % (func, prefix))
verilated_mods[prefix] = re.compile(r'^' + prefix)
# pprint(verilated_mods)
# Resort by Verilog name
my %vfuncs;
my %groups;
foreach my $func (keys %funcs) {
my $pct = $funcs{$func}{pct};
my $vfunc = $func;
my $design;
# Sort by Verilog name
vfuncs = {}
groups = {}
groups['type'] = collections.defaultdict(lambda: 0)
groups['design'] = collections.defaultdict(lambda: 0)
groups['module'] = collections.defaultdict(lambda: 0)
if ($func =~ /\(([a-zA-Z_0-9]+__Syms)/) {
$design = $pointer_mods{$1};
}
for func in funcs:
pct = funcs[func]['pct']
vfunc = func
foreach my $vde (keys %verilated_mods) {
last if $design;
if ($func =~ /$verilated_mods{$vde}/) {
$design=$vde;
last;
}
}
funcarg = re.sub(r'^.*\(', '', func)
if ($vfunc =~ /__PROF__([a-zA-Z_0-9]+)__l?([0-9]+)\(/) {
$vfunc = sprintf("VBlock %s:%d", $1, $2);
$groups{type}{"Verilog Blocks under $design"} += $pct;
$groups{design}{$design} += $pct;
$groups{module}{$1} += $pct;
} else {
if ($design) {
$vfunc = sprintf("VCommon %s", $func);
$groups{type}{"Common code under $design"} += $pct;
$groups{design}{$design} += $pct;
$groups{module}{$design." common code"} += $pct;
} elsif ($func =~ /^VL_[A-Z0-9_]+/
|| $func =~ /^_?vl_[a-zA-Z0-9_]+/
|| $func =~ /^verilated/i) {
$vfunc = sprintf("VLib %s", $func);
$groups{type}{'VLib'} += $pct;
$groups{design}{'VLib'} += $pct;
$groups{module}{'VLib'} += $pct;
} elsif ($func =~ /^_mcount_private/) {
$vfunc = sprintf("Prof %s", $func);
$groups{type}{'Prof'} += $pct;
$groups{design}{'Prof'} += $pct;
$groups{module}{'Prof'} += $pct;
} else {
$vfunc = sprintf("C++ %s", $func);
$groups{type}{'C++'} += $pct;
$groups{design}{'C++'} += $pct;
$groups{module}{'C++'} += $pct;
}
}
$vfuncs{$vfunc} = $funcs{$func};
}
design = None
for vde in verilated_mods:
if verilated_mods[vde].match(func) or verilated_mods[vde].match(
funcarg):
design = vde
break
vdesign = "-"
prof_match = re.search(r'__PROF__([a-zA-Z_0-9]+)__l?([0-9]+)\(', vfunc)
if design and prof_match:
linefunc = prof_match.group(1)
lineno = int(prof_match.group(2))
vfunc = "VBlock %s:%d" % (linefunc, lineno)
vdesign = design
groups['type']["Verilog Blocks under " + design] += pct
groups['design'][design] += pct
groups['module'][linefunc] += pct
elif design:
vfunc = "VCommon " + func
vdesign = design
groups['type']["Common code under " + design] += pct
groups['design'][design] += pct
groups['module'][design + " common code"] += pct
elif re.match(r'(VL_[A-Z0-9_]+|_?vl_[a-zA-Z0-9_]+|Verilated)', vfunc):
vfunc = "VLib " + func
groups['type']['VLib'] += pct
groups['design']['VLib'] += pct
groups['module']['VLib'] += pct
elif re.match(r'^_mcount_private', vfunc):
vfunc = "Prof " + func
groups['type']['Prof'] += pct
groups['design']['Prof'] += pct
groups['module']['Prof'] += pct
else:
vfunc = "C++ " + func
groups['type']['C++'] += pct
groups['design']['C++'] += pct
groups['module']['C++'] += pct
if vfunc not in vfuncs:
vfuncs[vfunc] = funcs[func]
vfuncs[vfunc]['design'] = vdesign
else:
vfuncs[vfunc]['pct'] += funcs[func]['pct']
vfuncs[vfunc]['calls'] += funcs[func]['calls']
vfuncs[vfunc]['sec'] += funcs[func]['sec']
for ftype in ['type', 'design', 'module']:
missing = 100
for item in groups[ftype].keys():
missing -= groups[ftype][item]
groups[ftype]["\377Unaccounted for/rounding error"] = missing
print("Overall summary by %s:" % ftype)
print(" %-6s %s" % ("% time", ftype))
for what in sorted(groups[ftype].keys()):
# \377 used to establish sort order
pwhat = re.sub(r'^\377', '', what)
print(" %6.2f %s" % (groups[ftype][what], pwhat))
print()
design_width = 1
for func in vfuncs:
if design_width < len(vfuncs[func]['design']):
design_width = len(vfuncs[func]['design'])
print("Verilog code profile:")
print(" These are split into three categories:")
print(" C++: Time in non-Verilated C++ code")
print(" Prof: Time in profile overhead")
print(" VBlock: Time attributable to a block in a" +
" Verilog file and line")
print(" VCommon: Time in a Verilated module," +
" due to all parts of the design")
print(" VLib: Time in Verilated common libraries," +
" called by the Verilated code")
print()
print(" % cumulative self ")
print((" time seconds seconds calls %-" + str(design_width) +
"s type filename and line number") % "design")
cume = 0
for func in sorted(vfuncs.keys(),
key=lambda f: vfuncs[f]['sec'],
reverse=True):
cume += vfuncs[func]['sec']
print(("%6.2f %9.2f %8.2f %10d %-" + str(design_width) + "s %s") %
(vfuncs[func]['pct'], cume, vfuncs[func]['sec'],
vfuncs[func]['calls'], vfuncs[func]['design'], func))
foreach my $type (qw(type design module)) {
my $missing = 100;
foreach (sort (keys %{$groups{$type}})) {
$missing -= $groups{$type}{$_};
}
if ($missing) {
$groups{$type}{"\377Unaccounted for/rounding error"} = $missing;
}
######################################################################
######################################################################
print("Overall summary by $type:\n");
printf(" %-6s %s\n","% time",$type);
foreach my $what (sort (keys %{$groups{$type}})) {
(my $pwhat = $what) =~ s/^\377//; # Just used to establish sort order
printf(" %6.2f %s\n", $groups{$type}{$what}, $pwhat);
}
print("\n");
}
print("Verilog code profile:\n");
print(" These are split into three categories:\n");
print(" C++: Time in non-Verilated C++ code\n");
print(" Prof: Time in profile overhead\n");
print(" VBlock: Time attributable to a block in a Verilog file and line\n");
print(" VCommon: Time in a Verilated module, due to all parts of the design\n");
print(" VLib: Time in Verilated common libraries, called by the Verilated code\n");
print("\n");
print(" % cumulative self \n");
print(" time seconds seconds calls type filename and line number\n");
my $cume = 0;
foreach my $func (sort {$vfuncs{$b}{sec} <=> $vfuncs{$a}{sec}
|| $a cmp $b}
(keys %vfuncs)) {
$cume += $vfuncs{$func}{sec};
printf +("%6.2f %9.2f %8.2f %8d %s\n",
$vfuncs{$func}{pct},
$cume, $vfuncs{$func}{sec},
$vfuncs{$func}{calls},
$func);
}
}
#######################################################################
__END__
=pod
=head1 NAME
verilator_profcfunc - Read gprof report created with --prof-cfuncs
=head1 SYNOPSIS
verilator --prof-cfuncs ....
gcc --ggdb -pg ....
{run executable}
gprof
verilator_profcfuncs gprof.out
=head1 DESCRIPTION
Verilator_profcfunc reads a profile report created by gprof. The names of
parser = argparse.ArgumentParser(
allow_abbrev=False,
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""Read gprof report created with --prof-cfuncs""",
epilog=
"""Verilator_profcfunc reads a profile report created by gprof. The names of
the functions are then transformed, assuming the user used Verilator's
--prof-cfuncs, and a report printed showing the percentage of time, etc,
in each Verilog block.
For documentation see
L<https://verilator.org/guide/latest/exe_verilator_profcfuncs.html>.
https://verilator.org/guide/latest/exe_verilator_profcfunc.html
=head1 ARGUMENT SUMMARY
<filename> Input file (gprof.out)
--help Display this help
=head1 DISTRIBUTION
The latest version is available from L<https://verilator.org>.
Copyright 2007-2021 by Wilson Snyder. This program is free software; you
Copyright 2002-2021 by Wilson Snyder. This program is free software; you
can redistribute it and/or modify it under the terms of either the GNU
Lesser General Public License Version 3 or the Perl Artistic License
Version 2.0.
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
=head1 SEE ALSO
parser.add_argument('--debug',
action='store_const',
const=9,
help='enable debug')
parser.add_argument('filename', help='input gprof output to process')
C<verilator>
and L<https://verilator.org/guide/latest/exe_verilator_profcfuncs.html> for
detailed documentation.
=cut
Args = parser.parse_args()
profcfunc(Args.filename)
######################################################################
### Local Variables:
### compile-command: "$V4/bin/verilator_profcfunc $V4/test_c/obj_dir/V*_03_*.tree $V4N/test_c/obj_dir/V*_03_*.tree"
### End:
# Local Variables:
# compile-command: "./verilator_profcfunc ../test_regress/t/t_profcfunc.gprof"
# End:
+9 -6
View File
@@ -23,6 +23,12 @@ fatal() {
echo "ERROR: $(basename "$0"): $1" >&2; exit 1;
}
if [ "$CI_M32" = "0" ]; then
unset CI_M32
elif [ "$CI_M32" != "1" ]; then
fatal "\$CI_M32 must be '0' or '1'";
fi
if [ "$CI_OS_NAME" = "linux" ]; then
MAKE=make
elif [ "$CI_OS_NAME" = "osx" ]; then
@@ -36,7 +42,7 @@ fi
install-vcddiff() {
TMP_DIR="$(mktemp -d)"
git clone https://github.com/veripool/vcddiff "$TMP_DIR"
git -C "${TMP_DIR}" checkout 5112f88b7ba8818dce9dfb72619e64a1fc19542c
git -C "${TMP_DIR}" checkout e5664be5fe39d353bf3fcb50aa05214ab7ed4ac4
"$MAKE" -C "${TMP_DIR}"
sudo cp "${TMP_DIR}/vcddiff" /usr/local/bin
}
@@ -55,7 +61,7 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
if [ "$COVERAGE" = 1 ]; then
yes yes | sudo cpan -fi Parallel::Forker
fi
if [ "$M32" = 1 ]; then
if [ "$CI_M32" = 1 ]; then
sudo apt-get install gcc-multilib g++-multilib
fi
elif [ "$CI_OS_NAME" = "osx" ]; then
@@ -82,7 +88,7 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ]; then
sudo apt-get install libsystemc-dev
fi
if [ "$M32" = 1 ]; then
if [ "$CI_M32" = 1 ]; then
sudo apt-get install lib32z1-dev gcc-multilib g++-multilib
fi
elif [ "$CI_OS_NAME" = "osx" ]; then
@@ -101,9 +107,6 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
fi
yes yes | sudo cpan -M $CI_CPAN_REPO -fi Parallel::Forker
install-vcddiff
autoconf
./configure --enable-longtests --enable-ccwarn
else
##############################################################################
# Unknown build stage
+15 -2
View File
@@ -21,6 +21,12 @@ fatal() {
echo "ERROR: $(basename "$0"): $1" >&2; exit 1;
}
if [ "$CI_M32" = "0" ]; then
unset CI_M32
elif [ "$CI_M32" != "1" ]; then
fatal "\$CI_M32 must be '0' or '1'";
fi
if [ "$CI_OS_NAME" = "linux" ]; then
export MAKE=make
NPROC=$(nproc)
@@ -40,8 +46,10 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
if [ "$COVERAGE" != 1 ]; then
autoconf
./configure --enable-longtests --enable-ccwarn
./configure --enable-longtests --enable-ccwarn ${CI_M32:+--enable-m32}
ccache -z
"$MAKE" -j "$NPROC" -k
ccache -s
if [ "$CI_OS_NAME" = "osx" ]; then
file bin/verilator_bin
file bin/verilator_bin_dbg
@@ -61,6 +69,7 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
if [ "$CI_OS_NAME" = "osx" ]; then
export VERILATOR_TEST_NO_GDB=1 # Pain to get GDB to work on OS X
# TODO below may no longer be requried as configure checks for -pg
export VERILATOR_TEST_NO_GPROF=1 # Apple Clang has no -pg
# export PATH="/Applications/gtkwave.app/Contents/Resources/bin:$PATH" # fst2vcd
file bin/verilator_bin
@@ -76,13 +85,15 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
"$MAKE" -j "$NPROC" -k
elif [ "$CI_OS_NAME" = "freebsd" ]; then
export VERILATOR_TEST_NO_GDB=1 # Disable for now, ideally should run
# TODO below may no longer be requried as configure checks for -pg
export VERILATOR_TEST_NO_GPROF=1 # gprof is a bit different on FreeBSD, disable
fi
# Run sanitize on Ubuntu 20.04 only
[ "$CI_RUNS_ON" = 'ubuntu-20.04' ] && sanitize='--sanitize' || sanitize=''
[ "$CI_RUNS_ON" = 'ubuntu-20.04' ] && [ "$CI_M32" = "" ] && sanitize='--sanitize' || sanitize=''
# Run the specified test
ccache -z
case $TESTS in
dist-vlt-0)
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=0/3
@@ -169,6 +180,8 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
fatal "Unknown test: $TESTS"
;;
esac
ccache -s
# Upload coverage data
if [[ $TESTS == coverage-* ]]; then
bash <(cat ci/coverage-upload.sh) -f nodist/obj_dir/coverage/app_total.info
+108 -36
View File
@@ -7,7 +7,7 @@
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[4.204 2021-06-12],
AC_INIT([Verilator],[4.216 2021-12-05],
[https://verilator.org],
[verilator],[https://verilator.org])
# When releasing, also update header of Changes file
@@ -35,7 +35,7 @@ AC_ARG_ENABLE([silent-rules],
AC_MSG_CHECKING(whether to perform partial static linking of Verilator binary)
AC_ARG_ENABLE([partial-static],
[AS_HELP_STRING([--disable-partial-static],
[By default, for Verilation peformance, Verilator
[By default, for Verilation performance, Verilator
is linked against some of its dependencies
statically. Use this to link the Verilator binary
fully dynamically.])],
@@ -52,7 +52,7 @@ AC_MSG_CHECKING(whether to use tcmalloc)
AC_ARG_ENABLE([tcmalloc],
[AS_HELP_STRING([--enable-tcmalloc],
[Use libtcmalloc_minimal for faster dynamic memory
management in Verilator binary@<:@default=check@:>@])],
management in Verilator binary @<:@default=check@:>@])],
[case "${enableval}" in
yes) CFG_WITH_TCMALLOC=yes ;;
no) CFG_WITH_TCMALLOC=no ;;
@@ -61,6 +61,34 @@ AC_ARG_ENABLE([tcmalloc],
[CFG_WITH_TCMALLOC=check;])
AC_MSG_RESULT($CFG_WITH_TCMALLOC)
# Flag to enable -m32 build
AC_MSG_CHECKING(whether to use -m32)
AC_ARG_ENABLE([m32],
[AS_HELP_STRING([--enable-m32],
[Use -m32 for all compilation and link,
including Verialtor and generated models.])],
[case "${enableval}" in
yes) CFG_ENABLE_M32=yes ;;
no) CFG_ENABLE_M32=no ;;
*) AC_MSG_ERROR([bad value '${enableval}' for --enable-m32]) ;;
esac],
CFG_ENABLE_M32=no)
AC_MSG_RESULT($CFG_ENABLE_M32)
# Flag to enable coverage build
AC_MSG_CHECKING(whether to build for coverage collection)
AC_ARG_ENABLE([coverage],
[AS_HELP_STRING([--enable-coverage],
[Build Verilator for code coverage collection.
For developers only.])],
[case "${enableval}" in
yes) CFG_ENABLE_COVERAGE=yes ;;
no) CFG_ENABLE_COVERAGE=no ;;
*) AC_MSG_ERROR([bad value '${enableval}' for --enable-coverage]) ;;
esac],
CFG_ENABLE_COVERAGE=no)
AC_MSG_RESULT($CFG_ENABLE_COVERAGE)
# Special Substitutions - CFG_WITH_DEFENV
AC_MSG_CHECKING(whether to use hardcoded paths)
AC_ARG_ENABLE([defenv],
@@ -135,6 +163,11 @@ AC_RUN_IFELSE(
AC_MSG_RESULT(no);AC_MSG_ERROR([a working C++ compiler is required]),
AC_MSG_RESULT(yes))
AC_CHECK_PROG(AR,ar,ar)
if test "x$AR" = "x" ; then
AC_MSG_ERROR([Cannot find "ar" in your PATH, please install it])
fi
AC_PATH_PROG(PERL,perl)
if test "x$PERL" = "x" ; then
AC_MSG_ERROR([Cannot find "perl" in your PATH, please install it])
@@ -211,24 +244,34 @@ AC_DEFUN([_MY_CXX_CHECK_FLAG],
CXXFLAGS="$ACO_SAVE_CXXFLAGS"
])
AC_DEFUN([_MY_CXX_CHECK_IFELSE],
[# _MY_CXX_CHECK_IFELSE(option,action-if-supported,action-if-not-supported)
# Check if compiler supports specific option. If it does,
# do action-if-supported, otherwise do action-if-not-supported
_MY_CXX_CHECK_FLAG($1)
if test "$_my_result" = "yes" ; then
true
$2
else
true
$3
fi
])
AC_DEFUN([_MY_CXX_CHECK_SET],
[# _MY_CXX_CHECK_SET(variable,flag) -- Check if compiler supports specific options
# If it does, set variable to flag, only if not previously set
[# _MY_CXX_CHECK_SET(variable,option)
# Check if compiler supports specific option. If it does,
# set variable to option, only if not previously set.
if test "$$1" = ""; then
_MY_CXX_CHECK_FLAG($2)
if test "$_my_result" = "yes" ; then
$1="$2"
fi
_MY_CXX_CHECK_IFELSE($2, $1="$2")
fi
])
AC_DEFUN([_MY_CXX_CHECK_OPT],
[# _MY_CXX_CHECK_OPT(flag) -- Check if compiler supports specific options
# If it does, append flag to variable
_MY_CXX_CHECK_FLAG($2)
if test "$_my_result" = "yes" ; then
$1="$$1 $2"
fi
[# _MY_CXX_CHECK_OPT(variable,option)
# Check if compiler supports specific option. If it does,
# append option to variable
_MY_CXX_CHECK_IFELSE($2, $1="$$1 $2")
])
AC_DEFUN([_MY_LDLIBS_CHECK_FLAG],
@@ -270,35 +313,52 @@ AC_DEFUN([_MY_LDLIBS_CHECK_OPT],
_MY_LDLIBS_CHECK_IFELSE($2, $1="$$1 $2")
])
# When using -m32. Check this first as later checks may fail with the -m32 flag.
if test "$CFG_ENABLE_M32" = "yes"; then
_MY_CXX_CHECK_IFELSE(
-m32,
[CXX="$CXX -m32"],
[AC_MSG_ERROR([--enable-m32 was given but compiler does not support -m32])])
fi
# Similarly, add the coverage flags early as they influence later checks.
if test "$CFG_ENABLE_COVERAGE" = "yes"; then
_MY_CXX_CHECK_OPT(CXX,--coverage)
# Otherwise inline may not show as uncovered
# If we use this then e.g. verilated.h functions properly show up
# if unused.
# However, VerilatedSerialize::write then changes from covered
# to uncovered (in G++ 9.3.0) even with all inlining turned off.
# Having false negative coverage is more effort then missing negatives.
# Also this seems to explode the runtime (since a lot more data).
# _MY_CXX_CHECK_OPT(CXX,-fkeep-inline-functions)
# Otherwise static may not show as uncovered
_MY_CXX_CHECK_OPT(CXX,-fkeep-static-functions)
# Exceptions can pollute the branch coverage data
_MY_CXX_CHECK_OPT(CXX,-fno-exceptions)
# Define-out some impossible stuff
_MY_CXX_CHECK_OPT(CXX,-DVL_GCOV)
fi
# Compiler flags to enable profiling
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_PROFILE,-pg)
AC_SUBST(CFG_CXXFLAGS_PROFILE)
# Flag to select newest language standard supported
# Macros work such that first option that passes is the one we take
# Currently enabled gnu++14/c++14 due to packaged SystemC dependency
# gnu++17 is the newest that Verilator supports
# std++03 is the oldest that Verilator supports
# Currently enabled c++14 due to packaged SystemC dependency
# c++14 is the newest that Verilator is regressed 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=gnu++17)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++14)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++11)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++03)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++20)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++17)
_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)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++11)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++03)
AC_SUBST(CFG_CXXFLAGS_STD_NEWEST)
# And likewise oldest standard (same list above, backwards)
# This is used for internal testing
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++03)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++11)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++14)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++17)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++20)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++03)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++11)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++14)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++17)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++20)
AC_SUBST(CFG_CXXFLAGS_STD_OLDEST)
# Flags for compiling Verilator internals including parser, and Verilated files
# These turn on extra warnings and are only used with 'configure --enable-ccwarn'
@@ -322,6 +382,18 @@ _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_PARSER,-Wno-parentheses-equality)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_PARSER,-Wno-unused)
AC_SUBST(CFG_CXXFLAGS_PARSER)
# Flags for compiling the debug version of Verilator (in addition to above CFG_CXXFLAGS_SRC)
if test "$CFG_ENABLE_COVERAGE" = "no"; then # Do not optimize for the coverage build
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DEBUG,-Og)
fi
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DEBUG,-ggdb)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DEBUG,-gz)
AC_SUBST(CFG_CXXFLAGS_DEBUG)
# Flags for linking the debug version of Verilator (in addition to above CFG_LDFLAGS_SRC)
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_DEBUG,-gz)
AC_SUBST(CFG_LDFLAGS_DEBUG)
# Flags for Verilated makefile
# For example, -Wno-div-by-zero isn't in 4.1.2
# Random code often does / 0. Unfortunately VL_DIV_I(0,0) will warn
+10
View File
@@ -11,6 +11,7 @@ Andreas Kuster
Chris Randall
Conor McCullough
Dan Petrisko
Daniel Bates
David Horton
David Metz
David Stanford
@@ -20,6 +21,7 @@ Driss Hafdi
Edgar E. Iglesias
Eric Rippey
Fan Shupei
Felix Yan
Garrett Smith
Geza Lore
Gianfranco Costamagna
@@ -29,6 +31,8 @@ Harald Heckmann
Howard Su
Huang Rui
HyungKi Jeong
Iru Cai
Ivan Vnučec
Iztok Jeras
James Hanlon
James Hutchinson
@@ -41,9 +45,11 @@ John Coiner
John Demme
Jonathan Drolet
Josh Redford
Julie Schwartz
Julien Margetts
Kaleb Barrett
Kanad Kanhere
Keith Colbert
Kevin Kiningham
Krzysztof Bieganski
Kuba Ober
@@ -58,6 +64,7 @@ Marshal Qiao
Martin Schmidt
Matthew Ballance
Michael Killough
Michaël Lefebvre
Mike Popoloski
Miodrag Milanović
Morten Borup Petersen
@@ -82,6 +89,8 @@ Sebastien Van Cauwenberghe
Sergi Granell
Stefan Wallentowitz
Stephen Henry
Steven Hugg
Teng Huang
Tim Snyder
Tobias Rosenkranz
Tobias Wölfel
@@ -98,3 +107,4 @@ Yossi Nivin
Yuri Victorovich
Yutetsu TAKATSUKASA
Yves Mathieu
Zhanglei Wang
+19 -10
View File
@@ -1,16 +1,25 @@
/*Verilator blue #008fd7; set in html*/
/* Verilator blue #008fd7; set in html */
.wy-side-nav-search > div.version {
color: #111;
color: #111;
}
.fa-github::before, .icon-github::before {
content: "";
.fa-github::before,
.icon-github::before {
content: "";
}
.fa-home::before, .icon-home::before {
content: "";
.fa-home::before,
.icon-home::before {
content: "";
}
.fa-arrow-circle-right::before, .icon-circle-arrow-right::before {
content: "";
.fa-arrow-circle-right::before,
.icon-circle-arrow-right::before {
content: "";
}
.fa-arrow-circle-left::before, .icon-circle-arrow-left::before {
content: "";
.fa-arrow-circle-left::before,
.icon-circle-arrow-left::before {
content: "";
}
+6
View File
@@ -0,0 +1,6 @@
.. comment: generated by t_lint_didnotconverge_bad
.. code-block:: sv
:linenos:
always_comb b = ~a;
always_comb a = b;
+7
View File
@@ -0,0 +1,7 @@
.. 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
+4
View File
@@ -0,0 +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
+5
View File
@@ -0,0 +1,5 @@
.. comment: generated by t_lint_stmtdly_bad
.. code-block:: sv
:emphasize-lines: 1
#100 $finish; //<--- Warning
+4
View File
@@ -0,0 +1,4 @@
.. comment: generated by t_lint_stmtdly_bad
.. code-block::
%Warning-STMTDLY: example.v:1:8 Unsupported: Ignoring delay on this delayed statement.
+1 -1
View File
@@ -1,4 +1,4 @@
# pylint: disable=C0103,C0114,C0116,E0402,W0622
# pylint: disable=C0103,C0114,C0116,C0301,E0402,W0622
#
# Configuration file for Verilator's Sphinx documentation builder.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
+87 -22
View File
@@ -7,22 +7,86 @@
Connecting to Verilated Models
******************************
Structure of the Verilated Model
================================
Verilator outputs a :file:`{prefix}.h` header file which defines a class
named :code:`{prefix}` which represents the generated model the user is
supposed to instantiate. This model class defines the interface of the
Verilated model.
Verilator will additionally create a :file:`{prefix}.cpp` file, together
with additional .h and .cpp files for internals. See the :file:`examples`
directory in the kit for examples. See :ref:`Files Read/Written` for
information on all the files Verilator might output.
The output of Verilator will contain a :file:`{prefix}.mk` file that may be
used with Make to build a :file:`{prefix}__ALL.a` library with all required
objects in it.
The generated model class file manages all internal state required by the
model, and exposes the following interface that allows interaction with the
model:
* Top level IO ports are exposed as references to the appropriate internal
equivalents.
* Public top level module instances are exposed as pointers to allow access
to :code:`/* verilator public */` items.
* The root of the design hierarchy (as in SystemVerilog :code:`$root`) is
exposed via the :code:`rootp` member pointer to allow access to model
internals, including :code:`/* verilator public_flat */` items.
.. _Porting from pre 4.210:
Model interface changes in version 4.210
------------------------------------------
Starting from version 4.210, the model class is an interface object.
Up until Verilator version 4.204 inclusive, the generated model class was
also the instance of the top level instance in the design hierarchy (what
you would refer to with :code:`$root` in SystemVerilog). This meant that
all internal variables that were implemented by Verilator in the root scope
were accessible as members of the model class itself. Note there were often
many such variable due to module inlining, including :code:`/* verilator
public_flat */` items.
This means that user code that accesses internal signals in the model
(likely including :code:`/* verilator public_flat */` signals, as they are
often inlined into the root scope) will need to be updated as follows:
* No change required for accessing top level IO signals. These are directly
accessible in the model class via references.
* No change required for accessing :code:`/* verilator public */` items.
These are directly accessible via sub-module pointers in the model class.
* Accessing any other internal members, including
:code:`/* verilator public_flat */` items requires the following changes:
* Additionally include :file:`{prefix}___024root.h`. This header defines
type of the :code:`rootp` pointer within the model class. Note the
:code:`__024` substring is the Verilator escape sequence for the
:code:`$` character, i.e.: :code:`rootp` points to the Verilated
SystemVerilog :code:`$root` scope.
* Replace :code:`modelp->internal->member->lookup` references with
:code:`modelp->rootp->internal->member->lookup` references, which
contain one additional indirection via the :code:`rootp` pointer.
.. _Connecting to C++:
Connecting to C++
=================
Verilator creates a :file:`{prefix}.h` and :file:`{prefix}.cpp` file for
the top level module, together with additional .h and .cpp files for
internals. See the :file:`examples` directory in the kit for examples. See
:ref:`Files Read/Written` for information on all the files it writes.
After the model is created, there will be a :file:`{prefix}.mk` file that
may be used with Make to produce a :file:`{prefix}__ALL.a` file with all
required objects in it.
The user must write a C++ wrapper and main loop for the simulation, to link
with the Verilated model. Here is a simple example:
In C++ output mode (:vlopt:`--cc`), the Verilator generated model class is a
simple C++ class. The user must write a C++ wrapper and main loop for the
simulation, which instantiates the model class, and link with the Verilated
model. Here is a simple example:
.. code-block:: C++
@@ -30,7 +94,7 @@ with the Verilated model. Here is a simple example:
#include <iostream> // Need std::cout
#include "Vtop.h" // From Verilating "top.v"
Vtop *top; // Instantiation of module
Vtop *top; // Instantiation of model
vluint64_t main_time = 0; // Current simulation time
// This is a 64-bit integer to reduce wrap over issues and
@@ -45,7 +109,7 @@ with the Verilated model. Here is a simple example:
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv); // Remember args
top = new Vtop; // Create instance
top = new Vtop; // Create model
top->reset_l = 0; // Set some inputs
@@ -70,17 +134,18 @@ with the Verilated model. Here is a simple example:
}
Note signals are read and written as member variables of the model. You
call the :code:`eval()` method to evaluate the model. When the simulation
is complete call the :code:`final()` method to execute any SystemVerilog
final blocks, and complete any assertions. See :ref:`Evaluation Loop`.
Note top level IO signals are read and written as members of the model. You
call the :code:`eval()` method to evaluate the model. When the simulation is
complete call the :code:`final()` method to execute any SystemVerilog final
blocks, and complete any assertions. See :ref:`Evaluation Loop`.
Connecting to SystemC
=====================
Verilator will convert the top level module to a SC_MODULE. This module
will attach directly into a SystemC netlist as an instantiation.
In SystemC output mode (:vlopt:`--sc`), the Verilator generated model class
is a SystemC SC_MODULE. This module will attach directly into a SystemC
netlist as an instantiation.
The SC_MODULE gets the same pinout as the Verilog module, with the
following type conversions: Pins of a single bit become bool. Pins 2-32
@@ -88,9 +153,9 @@ bits wide become uint32_t's. Pins 33-64 bits wide become sc_bv's or
vluint64_t's depending on the :vlopt:`--no-pins64` option. Wider pins
become sc_bv's. (Uints simulate the fastest so are used where possible.)
Lower modules are not pure SystemC code. This is a feature, as using the
SystemC pin interconnect scheme everywhere would reduce performance by an
order of magnitude.
Model internals, including lower level sub-modules are not pure SystemC
code. This is a feature, as using the SystemC pin interconnect scheme
everywhere would reduce performance by an order of magnitude.
Direct Programming Interface (DPI)
+2 -1
View File
@@ -13,7 +13,8 @@ When possible, please instead report bugs at `Verilator Issues
Primary author is Wilson Snyder <[email protected]>.
Major concepts by Paul Wasson, Duane Galbi, John Coiner and Jie Xu.
Major concepts by Paul Wasson, Duane Galbi, John Coiner, Geza Lore, Yutetsu
Takatsukasa, and Jie Xu.
Contributors
+5 -8
View File
@@ -9,15 +9,12 @@ 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 2022.
compiling Verilated models no sooner than January 2023.
No-Relative-Cfuncs Option
The :vlopt:`--no-relative-cfuncs` option is not be required by any C++11
compliant compiler and is planned for removal no sooner than July 2021.
Inhibit-Sim Option
The :vlopt:`--inhibit-sim` option is planned for removal no sooner than
July 2021.
Verilated_heavy.h
The legacy "verilated_heavy.h" include was replaced with just including
"verilated.h". Verilated_heavy.h is planned for removal no sooner than
July 2022.
Configuration File -msg
The :vlopt:`lint_off` "-msg" option has been replaced with the "-rule"
+6
View File
@@ -62,6 +62,12 @@ Summary:
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+vlt+file+<filename>
When a model was Verilated using :vlopt:`--prof-threads`, sets the
profile-guided optimization data runtime filename to dump to. Defaults
to :file:`profile.vlt`.
.. option:: +verilator+rand+reset+<value>
When a model was Verilated using :vlopt:`--x-initial unique
+87 -61
View File
@@ -158,41 +158,26 @@ Summary:
.. option:: --clk <signal-name>
Sometimes it is quite difficult for Verilator to distinguish clock
signals from other data signals. Occasionally the clock signals can end
up in the checking list of signals which determines if further
evaluation is needed. This will heavily degrade the performance of a
Verilated model.
With :vlopt:`--clk`, the specified signal-name is taken as a root clock
into the model, then Verilator will mark the signal as clocker and
propagate the clocker attribute automatically to other signals derived
from that. In this way, Verilator will try to avoid taking the clocker
signal into checking list.
into the model; Verilator will mark the signal as clocker and
propagate the clocker attribute automatically to other signals downstream in
that clock tree.
Note signal-name is specified by the RTL hierarchy path. For example,
v.foo.bar. If the signal is the input to top-module, the directly the
signal name. If you find it difficult to find the exact name, try to use
a :option:`/*verilator&32;clocker*/` metacomment in RTL file to mark the
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
directly provide the signal name. Alternatively, use a
:option:`/*verilator&32;clocker*/` metacomment in RTL file to mark the
signal directly.
If clock signals are assigned to vectors and then later used
individually, Verilator will attempt to decompose the vector and connect
the single-bit clock signals directly. This should be transparent to
the user.
If clock signals are assigned to vectors and then later used as
individual bits, Verilator will attempt to decompose the vector and
connect the single-bit clock signals.
.. option:: --make <build-tool>
Generates a script for the specified build tool.
Supported values are ``gmake`` for GNU Make and ``cmake`` for CMake.
Both can be specified together. If no build tool is specified, gmake is
assumed. The executable of gmake can be configured via environment
variable "MAKE".
When using :vlopt:`--build` Verilator takes over the responsibility of
building the model library/executable. For this reason :option:`--make`
cannot be specified when using :vlopt:`--build`.
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:: --compiler <compiler-name>
@@ -527,14 +512,6 @@ Summary:
See :vlopt:`-y`.
.. option:: --inhibit-sim
Rarely needed and deprecated. Create a :code:`inhibitSim(bool)`
function to enable and disable evaluation. This allows an upper level
testbench to disable modules that are not important in a given
simulation, without needing to recompile or change the SystemC modules
instantiated.
.. option:: --inline-mult <value>
Tune the inlining of modules. The default value of 2000 specifies that up
@@ -544,6 +521,14 @@ Summary:
times, but potentially faster simulation speed. This setting is ignored
for very small modules; they will always be inlined, if allowed.
.. option:: --instr-count-dpi <value>
Assumed dynamic instruction count of the average DPI import. This is used
by the partitioning algorithm when creating a multithread model. The
default value is 200. Adjusting this to an appropriate value can yield
performance improvements in multithreaded models. Ignored when creating a
single threaded model.
.. option:: -j [<value>]
Specify the level of parallelism for :vlopt:`--build`. The <value> must
@@ -590,6 +575,23 @@ Summary:
"+libext+" is fairly standard across Verilog tools. Defaults to
".v+.sv".
.. option:: --lib-create <name>
Produces C++, Verilog wrappers and a Makefile which can in turn produce
a DPI library which can be used by Verilator or other simulators along
with the corresponding Verilog wrapper. The Makefile will build both a
static and dynamic version of the library named :file:`lib<name>.a` and
:file:`lib<name>.so` respectively. This is done because some simulators
require a dynamic library, but the static library is arguably easier to
use if possible. :vlopt:`--protect-lib` implies :vlopt:`--protect-ids`.
When using :vlopt:`--lib-create` it is advised to also use
:vlopt:`--timescale-override /1fs <--timescale-override>` to ensure the
model has a time resolution that is always compatible with the time
precision of the upper instantiating module.
See also :vlopt:`--protect-lib`.
.. option:: --lint-only
Check the files for lint violations only, do not create any other
@@ -601,6 +603,19 @@ Summary:
If the design is not to be completely Verilated see also the
:vlopt:`--bbox-sys` and :vlopt:`--bbox-unsup` options.
.. option:: --make <build-tool>
Generates a script for the specified build tool.
Supported values are ``gmake`` for GNU Make and ``cmake`` for CMake.
Both can be specified together. If no build tool is specified, gmake is
assumed. The executable of gmake can be configured via environment
variable "MAKE".
When using :vlopt:`--build` Verilator takes over the responsibility of
building the model library/executable. For this reason :option:`--make`
cannot be specified when using :vlopt:`--build`.
.. option:: -MAKEFLAGS <string>
When using :vlopt:`--build`, add the specified argument to the invoked
@@ -633,6 +648,13 @@ Summary:
The directory is created if it does not exist and the parent directories
exist; otherwise manually create the Mdir before calling Verilator.
.. option:: --no-merge-const-pool
Rarely needed. In order to minimize cache footprint, values of different
data type, that are yet emitted identically in C++ are merged in the
constant pool. This option disables this and causes every constant pool
entry with a distinct data type to be emitted separately.
.. option:: --mod-prefix <topname>
Specifies the name to prepend to all lower level classes. Defaults to
@@ -654,10 +676,6 @@ Summary:
Backward compatible alias for :vlopt:`--pins-bv 33 <--pins-bv>`.
.. option:: --no-relative-cfuncs
Deprecated.
.. option:: --no-skip-identical =item --skip-identical
Rarely needed. Disables or enables skipping execution of Verilator if
@@ -808,6 +826,13 @@ Summary:
prepended to the name of the :vlopt:`--top` option, or V prepended to
the first Verilog filename passed on the command line.
.. option:: --prof-c
When compiling the C++ code, enable the compiler's profiling flag
(e.g. :code:`g++ -pg`). See :ref:`Profiling`.
Using :vlopt:`--prof-cfuncs` also enables :vlopt:`--prof-c`.
.. option:: --prof-cfuncs
Modify the created C++ functions to support profiling. The functions
@@ -818,10 +843,12 @@ Summary:
came from. This allows gprof or oprofile reports to be correlated with
the original Verilog source statements. See :ref:`Profiling`.
Using :vlopt:`--prof-cfuncs` also enables :vlopt:`--prof-c`.
.. option:: --prof-threads
Enable gantt chart data collection for threaded builds. See :ref:`Thread
Profiling`.
Profiling` and :ref:`Thread PGO`.
.. option:: --protect-key <key>
@@ -862,24 +889,15 @@ Summary:
.. option:: --protect-lib <name>
Produces C++, Verilog wrappers and a Makefile which can in turn produce
a DPI library which can be used by Verilator or other simulators along
with the corresponding Verilog wrapper. The Makefile will build both a
static and dynamic version of the library named :file:`lib<name>.a` and
:file:`lib<name>.so` respectively. This is done because some simulators
require a dynamic library, but the static library is arguably easier to
use if possible. :vlopt:`--protect-lib` implies :vlopt:`--protect-ids`.
Produces a DPI library similar to :vlopt:`--lib-create`, but hides
internal design details. :vlopt:`--protect-lib` implies
:vlopt:`--protect-ids`, and :vlopt:`--lib-create`.
This allows for the secure delivery of sensitive IP without the need for
encrypted RTL (i.e. IEEE P1735). See :file:`examples/make_protect_lib`
in the distribution for a demonstration of how to build and use the DPI
library.
When using :vlopt:`--protect-lib` it is advised to also use
:vlopt:`--timescale-override /1fs <--timescale-override>` to ensure the
model has a time resolution that is always compatible with the time
precision of the upper instantiating module.
.. option:: --private
Opposite of :vlopt:`--public`. Is the default; this option exists for
@@ -1040,6 +1058,8 @@ Summary:
Verilator assumes DPI pure imports are threadsafe, but non-pure DPI
imports are not.
See also :vlopt:`--instr-count-dpi` option.
.. option:: --threads-max-mtasks <value>
Rarely needed. When using :vlopt:`--threads`, specify the number of
@@ -1153,9 +1173,9 @@ Summary:
.. option:: --trace-underscore
Enable tracing of signals that start with an underscore. Normally, these
signals are not output during tracing. See also
:vlopt:`--coverage-underscore` option.
Enable tracing of signals or modules that start with an
underscore. Normally, these signals are not output during tracing. See
also :vlopt:`--coverage-underscore` option.
.. option:: -U<var>
@@ -1492,9 +1512,9 @@ The grammar of configuration commands is as follows:
.. option:: no_clocker -module "<modulename>" [-function "<funcname>"] -var "<signame>"
Indicate the signal is used as clock or not. This information is used by
Verilator to mark the signal as clocker and propagate the clocker
attribute automatically to derived signals. See :vlopt:`--clk`.
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
clocker. See :vlopt:`--clk`.
Same as :option:`/*verilator&32;clocker*/` metacomment.
@@ -1598,6 +1618,12 @@ The grammar of configuration commands is as follows:
:option:`/*verilator&32;public_flat*/`, etc, metacomments. See
e.g. :ref:`VPI Example`.
.. option:: profile_data -mtask "<mtask_hash>" -cost <cost_value>
Feeds profile-guided optimization data into the Verilator algorithms in
order to improve model runtime performance. This option is not expected
to be used by users directly. See :ref:`Thread PGO`.
.. option:: sc_bv -module "<modulename>" [-task "<taskname>"] -var "<signame>"
.. option:: sc_bv -module "<modulename>" [-function "<funcname>"] -var "<signame>"
+38 -27
View File
@@ -10,42 +10,60 @@ starts and ends, and showing when each thread is busy or idle.
For an overview of use of verilator_gantt, see :ref:`Profiling`.
Gantt Chart Contents
--------------------
Gantt Chart VCD
---------------
The generated Gantt chart has time on the X-axis. Times shown are to the
scale printed, i.e. a certain about of time for each character width. The
Y-axis shows threads, each thread's execution is shown on one line. That
line shows "[" at the position in time when it executes.
Verilated_gnatt creates a value change dump (VCD) format dump file which
may be viewed in a waveform viewer (e.g. C<GTKWave>):
Following the "[" is the CPU number the task executed on, followed by zero
or more "-" to make the width of the characters match the scaled execution
time, followed by a "]". If the scale is too small, the CPU number and
mtask number will not be printed. If the scale is very small, a "&"
indicates multiple mtasks started at that time position.
.. figure:: figures/fig_gantt_min.png
Example verilator_gantt output, as viewed with GTKWave.
The viewed waveform chart has time on the X-axis, with one unit for each
time tick of the system's high-performance counter.
Also creates a value change dump (VCD) format dump file which may be viewed
in a waveform viewer (e.g. C<GTKWave>). See below.
Gantt Chart VCD Signals
-----------------------
In waveforms there are the following signals. Most signals the "decimal"
format will remove the leading zeros and make the traces easier to read.
In waveforms there are the following signals. In GTKWave, using a data
format of "decimal" will remove the leading zeros and make the traces
easier to read.
parallelism
evals
Increments each time when eval_step was measured to be active. This
allow visualization of how much time eval_step was active.
eval_loop
Increments each time when the evaluation loop within eval_step was
measured to be active. For best performance there is only a single
evaluation loop within each eval_step call, that is the eval_loop
waveform looks identical to the evals waveform.
measured_parallelism
The number of mtasks active at this time, for best performance this will
match the thread count. You may want to use an "analog step" format to
match the thread count. In GTKWave, use a data format of "analog step" to
view this signal.
predicted_parallelism
The number of mtasks Verilator predicted would be active at this time,
for best performance this will match the thread count. In GTKWave, use a
data format of "analog step" to view this signal.
cpu#_thread
For the given CPU number, the thread number executing.
For the given CPU number, the thread number measured to be executing.
mtask#_cpu
For the given mtask id, the CPU it is executing on.
For the given mtask id, the CPU it was measured to execute on.
thread#_mtask
For the given thread number, the mtask id executing.
For the given thread number, the mtask id it was executing.
predicted_thread#_mtask
For the given thread number, the mtask id Verilator predicted would be
executing.
verilator_gantt Arguments
-------------------------
@@ -60,13 +78,6 @@ The filename to read data from, defaults to "profile_threads.dat".
Displays a help summary, the program version, and exits.
.. option:: --scale <n>
Sets the number of time units per character on the X-axis of the generated
Gantt chart. (On x86, time units are rdtsc ticks.) Defaults to 0, which
will automatically compute a reasonable scale where no two mtasks need to
fit into same character width's worth of scaled time.
.. option:: --no-vcd
Disables creating a .vcd file.
@@ -1,20 +1,24 @@
.. Copyright 2003-2021 by Wilson Snyder.
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
verilator_profcfuncs
====================
verilator_profcfunc
===================
Verilator_profcfunc reads a profile report created by gprof. The names of
the functions are then transformed, assuming the user used Verilator's
--prof-cfuncs, and a report printed showing the percentage of time, etc, in
each Verilog block.
For an overview of use of verilator_profcfuncs, see :ref:`Profiling`.
Due to rounding errors in gprof reports, the input report's percentages may
not total to 100%. In the verilator_profcfunc report this will get
reported as a rounding error.
verilator_profcfuncs Arguments
------------------------------
For an overview of use of verilator_profcfunc, see :ref:`Profiling`.
.. program:: verilator_profcfuncs
verilator_profcfunc Arguments
-----------------------------
.. program:: verilator_profcfunc
.. option:: <filename>
+1 -1
View File
@@ -15,5 +15,5 @@ options to each executable.
exe_verilator.rst
exe_verilator_coverage.rst
exe_verilator_gantt.rst
exe_verilator_profcfuncs.rst
exe_verilator_profcfunc.rst
exe_sim.rst
+24 -5
View File
@@ -33,7 +33,12 @@ or "`ifdef`"'s may break other tools.
that returns up to a 32-bit number (without a trailing ;). This can be
used to call C++ functions from your Verilog code.
String arguments will be put directly into the output C++ code.
String arguments will be put directly into the output C++ code, except
the word 'this' (i.e.: the object pointer) might be replaced with a
different pointer as Verilator might implement logic with non-member
functions. For this reason, any references to class members must be made
via an explicit 'this->' pointer dereference.
Expression arguments will have the code to evaluate the expression
inserted. Thus to call a C++ function, :code:`$c("func(",a,")")` will
result in :code:`func(a)` in the output C++ code. For input arguments,
@@ -43,6 +48,14 @@ or "`ifdef`"'s may break other tools.
Verilog functions where the variable is flattened out, and also enable
other optimizations.
Verilator does not use any text inside the quotes for
ordering/scheduling. If you need the $c to be called at a specific
time, e.g. when a variable changes, then the $c must be under an
appropriate sensitivity statement, e.g. :code:`always @(posedge clk)
$c("func()")` to call it on every edge, or e.g. :code:`always @*
c("func(",a,")")` to call it when :code:`a` changes (the latter working
because :code:`a` is outside the quotes).
If you will be reading or writing any Verilog variables inside the C++
functions, the Verilog signals must be declared with
:option:`/*verilator&32;public*/` metacomments.
@@ -171,10 +184,9 @@ or "`ifdef`"'s may break other tools.
.. option:: /*verilator&32;no_clocker*/
Used after a signal declaration to indicate the signal is used as clock
or not. This information is used by Verilator to mark the signal as
clocker and propagate the clocker attribute automatically to derived
signals. See :vlopt:`--clk`.
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`.
Same as :option:`clocker` and :option:`no_clocker` in configuration
files.
@@ -477,6 +489,13 @@ 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;trace_init_task*/
Attached to a DPI import to indicate that function should be called when
initializing tracing. This attribute is indented only to be used
internally in code that Verilator generates when :vlopt:`--lib-create`
or :vlopt:`--hierarchical` is used along with :vlopt:`--trace`.
.. option:: /*verilator&32;tracing_off*/
Disable waveform tracing for all future signals that are declared in
+4 -2
View File
@@ -35,8 +35,10 @@ simulators.
Does Verilator run under Windows?
"""""""""""""""""""""""""""""""""
Yes, using Cygwin. Verilated output also compiles under Microsoft Visual
C++, but this is not tested every release.
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.
Can you provide binaries?
Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 37 KiB

+42 -16
View File
@@ -54,32 +54,38 @@ For --cc/--sc, it creates:
- Arguments for hierarchical Verilation (from --make gmake)
* - *{prefix}*\ _hierCMakeArgs.f
- Arguments for hierarchical Verilation (from --make cmake)
* - *{prefix}*\ .cpp
- Top level C++ file
* - *{prefix}{__n}*\ .cpp
- Additional top C++ files (from --output-split)
* - *{prefix}*\ .h
- Top level header
* - *{prefix}*\ __Slow\ *{__n}*\ .cpp
- Model header
* - *{prefix}*\ .cpp
- Model C++ file
* - *{prefix}*\ ___024root.h
- Top level (SystemVerilog $root) internal header file
* - *{prefix}*\ ___024root.cpp
- Top level (SystemVerilog $root) internal C++ file
* - *{prefix}*___024root*{__n}*\ .cpp
- Additional top level internal C++ files (from --output-split)
* - *{prefix}*\ ___024root__Slow\ *{__n}*\ .cpp
- Infrequent cold routines
* - *{prefix}*\ __Dpi.cpp
- DPI import and export wrappers (from --dpi)
* - *{prefix}*\ ___024root__Trace{__n}*\ .cpp
- Wave file generation code (from --trace)
* - *{prefix}*\ ___024root__Trace__Slow{__n}*\ .cpp
- Wave file generation code (from --trace)
* - *{prefix}*\ __Dpi.h
- DPI import and export declarations (from --dpi)
* - *{prefix}*\ __Dpi.cpp
- Global DPI export wrappers (from --dpi)
* - *{prefix}*\ __Dpi_Export\ *{__n}*\ .cpp
- DPI export wrappers scoped to this particular model (from --dpi)
* - *{prefix}*\ __Inlines.h
- Inline support functions
* - *{prefix}*\ __Syms.cpp
- Global symbol table C++
* - *{prefix}*\ __Syms.h
- Global symbol table header
* - *{prefix}*\ __Trace{__n}*\ .cpp
- Wave file generation code (from --trace)
* - *{prefix}*\ __Trace__Slow{__n}*\ .cpp
- Wave file generation code (from --trace)
* - *{prefix}{each_verilog_module}*\ .cpp
- Lower level internal C++ files
* - *{prefix}*\ __Syms.cpp
- Global symbol table C++
* - *{prefix}{each_verilog_module}*\ .h
- Lower level internal header files
* - *{prefix}{each_verilog_module}*\ .cpp
- Lower level internal C++ files
* - *{prefix}{each_verilog_module}{__n}*\ .cpp
- Additional lower C++ files (from --output-split)
@@ -139,3 +145,23 @@ After running Make, the C++ compiler may produce the following:
- Intermediate dependencies
* - *{prefix}{misc}*\ .o
- Intermediate objects
The Verilated executable may produce the following:
.. list-table::
* - coverage.dat
- Code coverage output, and default input filename for :command:`verilator_coverage`
* - 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`
Verilator_gantt may produce the following:
.. list-table::
* - profile_threads.vcd
- Gantt report waveform output
+13 -12
View File
@@ -23,6 +23,8 @@ package:
apt-get install verilator # On Ubuntu
For other distributions refer to `Repology Verilator Distro Packages
<https://repology.org/project/verilator>`__.
.. _Git Install:
@@ -44,21 +46,20 @@ In brief, to install from git:
#sudo apt-get install zlibc zlib1g zlib1g-dev # Ubuntu only (ignore if gives error)
git clone https://github.com/verilator/verilator # Only first time
## Note the URL above is not a page you can see with a browser, it's for git only
# Every time you need to build:
unsetenv VERILATOR_ROOT # For csh; ignore error if on bash
unset VERILATOR_ROOT # For bash
cd verilator
git pull # Make sure git repository is up-to-date
git tag # See what versions exist
git pull # Make sure git repository is up-to-date
git tag # See what versions exist
#git checkout master # Use development branch (e.g. recent bug fixes)
#git checkout stable # Use most recent stable release
#git checkout v{version} # Switch to specified release version
autoconf # Create ./configure script
./configure # Configure and create Makefile
make -j # Build Verilator itself
autoconf # Create ./configure script
./configure # Configure and create Makefile
make -j `nproc` # Build Verilator itself (if error, try just 'make')
sudo make install
@@ -77,10 +78,10 @@ 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
Linux, HPUX and Solaris. It should run with minor porting on any
GNU/Linux-ish platform. Verilator also works on Windows under Cygwin, and
Windows under MinGW (gcc -mno-cygwin). Verilated output (not Verilator
itself) compiles under all the options above, plus using MSVC++.
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)
compiles under all the options above, plus using MSVC++.
Install Prerequisites
@@ -116,7 +117,7 @@ Those developing Verilator itself may also want these (see internals.rst):
::
sudo apt-get install gdb graphviz cmake clang clang-format gprof lcov
sudo apt-get install gdb graphviz cmake clang clang-format-11 gprof lcov
sudo pip3 install sphinx sphinx_rtd_theme breathe
cpan install Pod::Perldoc
cpan install Parallel::Forker
@@ -286,7 +287,7 @@ Compile Verilator:
::
make -j
make -j `nproc` # Or if error on `nproc`, the number of CPUs in system
Test
+126 -8
View File
@@ -26,7 +26,8 @@ risk of reset bugs in trade for performance; see the above documentation
for these options.
If using Verilated multithreaded, use ``numactl`` to ensure you are using
non-conflicting hardware resources. See :ref:`Multithreading`.
non-conflicting hardware resources. See :ref:`Multithreading`. Also
consider using profile-guided optimization, see :ref:`Thread PGO`.
Minor Verilog code changes can also give big wins. You should not have any
UNOPTFLAT warnings from Verilator. Fixing these warnings can result in
@@ -93,9 +94,7 @@ cases, for example regressions, it is usually worth spending extra
compilation time to reduce total CPU time.
If you will be running many simulations on a single model, you can
investigate profile guided optimization. With GCC, using GCC's
"-fprofile-arcs", then GCC's "-fbranch-probabilities" will yield another
15% or so.
investigate profile guided optimization. See :ref:`Compiler PGO`.
Modern compilers also support link-time optimization (LTO), which can help
especially if you link in DPI code. To enable LTO on GCC, pass "-flto" in
@@ -230,7 +229,7 @@ coverage point insertions into the model and collect the coverage data.
To get the coverage data from the model, in the user wrapper code,
typically at the end once a test passes, call
:code:`Verilated::coveragep()->write` with an argument of the filename for
:code:`Verilated::threadContextp()->coveragep()->write` with an argument of the filename for
the coverage data file to write coverage data to (typically
"logs/coverage.dat").
@@ -272,8 +271,6 @@ profiled C++ code functions.
To use profiling:
#. Use Verilator's :vlopt:`--prof-cfuncs`.
#. Use Verilator's :vlopt:`-CFLAGS "-g -pg" <-CFLAGS>` to pass the
profiling flags through to GCC/Clang.
#. Build and run the simulation model.
#. The model will create gmon.out.
#. Run :command:`gprof` to see where in the C++ code the time is spent.
@@ -300,6 +297,9 @@ With the :vlopt:`--prof-threads` option, Verilator will:
* Add code to save profiling data in non-human-friendly form to the file
specified with :vlopt:`+verilator+prof+threads+file+\<filename\>`.
* Add code to save profiling data for thread profile-guided
optimization. See :ref:`Thread PGO`.
The :command:`verilator_gantt` program may then be run to transform the
saved profiling file into a nicer visual format and produce some related
statistics.
@@ -308,7 +308,7 @@ statistics.
Example verilator_gantt output, as viewed with GTKWave.
The parallelism shows the number of CPUs being used at a given moment.
The measured_parallelism shows the number of CPUs being used at a given moment.
The cpu_thread section shows which thread is executing on each of the physical CPUs.
@@ -316,6 +316,7 @@ statistics.
For more information see :command:`verilator_gantt`.
.. _Profiling ccache efficiency:
Profiling ccache efficiency
@@ -379,3 +380,120 @@ For example:
os >> main_time;
os >> *topp;
}
Profile-Guided Optimization
===========================
Profile-guided optimization is the technique where profiling data is
collected by running your simulation executable, then this information is
used to guide the next Verilation or compilation.
There are two forms of profile-guided optimizations. Unfortunately for
best results they must each be performed from the highest level code to the
lowest, which means performing them separately and in this order:
* :ref:`Thread PGO`
* :ref:`Compiler PGO`
Other forms of PGO may be supported in the future, such as clock and reset
toggle rate PGO, branch prediction PGO, statement execution time PGO, or
others as they prove beneficial.
.. _Thread PGO:
Thread Profile-Guided Optimization
----------------------------------
Verilator supports thread profile-guided optimization (Thread PGO) to
improve multithreaded 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
Verilator internals document (:file:`docs/internals.rst` in the
distribution.) If the estimations are incorrect, the threads will not be
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.
Run the model executable. When the executable exits, it will create a
profile.vlt file.
Rerun Verilator, optionally omitting the :vlopt:`--prof-threads` 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
profile data file (profile.vlt) can be placed on the verilator command line
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
than a shorter running test.
If you provide any profile feedback data to Verilator, and it cannot use
it, it will issue the :option:`PROFOUTOFDATE` warning that threads were
scheduled using estimated costs. This usually indicates that the profile
data was generated from different Verilog source code than Verilator is
currently running against. Therefore, repeat the data collection phase to
create new profiling data, then rerun Verilator with the same input source
files and that new profiling data.
.. _Compiler PGO:
Compiler Profile-Guided Optimization
------------------------------------
GCC and Clang support compiler profile-guided optimization (PGO). This
optimizes any C/C++ program including Verilated code. Using compiler PGO
typically yields improvements of 5-15% on both single-threaded and
multi-threaded models.
To use compiler PGO with GCC or Clang, please see the appropriate compiler
documentation. The process in GCC 10 was as follows:
1. Compile the Verilated model with the compiler's "-fprofile-generate"
flag:
.. code-block:: bash
verilator [whatever_flags] --make \
-CFLAGS -fprofile-generate -LDFLAGS -fprofile-generate
or, if calling make yourself, add -fprofile-generate appropriately to your
Makefile.
2. Run your simulation. This will create \*.gcda file(s) in the same
directory as the source files.
3. Recompile the model with -fprofile-use. The compiler will read the
\*.gcda file(s).
For GCC:
.. code-block:: bash
verilator [whatever_flags] --build \
-CFLAGS "-fprofile-use -fprofile-correction"
For Clang:
.. code-block:: bash
llvm-profdata merge -output default.profdata *.profraw
verilator [whatever_flags] --build \
-CFLAGS "-fprofile-use -fprofile-correction"
or, if calling make yourself, add these CFLAGS switches appropriately to
your Makefile.
Clang and GCC also support -fauto-profile which uses sample-based
feedback-directed optimization. See the appropriate compiler
documentation.
+9 -4
View File
@@ -70,10 +70,9 @@ 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.
The current hierarchical Verilation is based on protect-lib. Each hierarchy
block is Verilated to a protect-lib. User modules of the hierarchy blocks
will see a tiny wrapper generated by protect-lib instead of the actual
design.
The current hierarchical Verilation is based on :vlopt:`--lib-create`. Each
hierarchy block is Verilated into a library. User modules of the hierarchy
blocks will see a tiny wrapper generated by :vlopt:`--lib-create`.
Usage
@@ -216,6 +215,12 @@ be done by a "main thread". In most cases the eval thread and main thread
are the same thread (i.e. the user's top C++ testbench runs on a single
thread), but this is not required.
When making frequent use of DPI imported functions in a multi-threaded
model, it may be beneficial to performance to adjust the
:vlopt:`--instr-count-dpi` option based on some experimentation. This
influences the partitioning of the model by adjusting the assumed execution
time of DPI imports.
The :vlopt:`--trace-threads` options can be used to produce trace dumps
using multiple threads. If :vlopt:`--trace-threads` is set without
:vlopt:`--threads`, then :vlopt:`--trace-threads` will imply
+85 -36
View File
@@ -130,9 +130,10 @@ List Of Warnings
.. option:: BADSTDPRAGMA
Error that a pragma is badly formed, when that pragma is defined by IEEE1800-2017.
For example, an empty `pragma line, or an incorrect specified '`pragma protect'.
Note that 3rd party pragmas not defined by IEEE1800-2017 are ignored.
Error that a pragma is badly formed, when that pragma is defined by IEEE
1800-2017. For example, an empty pragma line, or an incorrect specified
'pragma protect'. Note that 3rd party pragmas not defined by IEEE
1800-2017 are ignored.
.. option:: BLKANDNBLK
@@ -481,39 +482,44 @@ List Of Warnings
Faulty example:
.. code-block:: sv
.. include:: ../../docs/gen/ex_DIDNOTCONVERGE_faulty.rst
always_comb b = ~a;
always_comb a = b
Results in at runtime (not when Verilated):
This code will toggle forever, and thus to prevent an infinite loop, the
executable will give the didn't converge error.
.. include:: ../../docs/gen/ex_DIDNOTCONVERGE_nodbg_msg.rst
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.
This is because the signals keep toggling even with out time
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
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.
Next, run Verilator with :vlopt:`--prof-cfuncs -CFLAGS -DVL_DEBUG
<--prof-cfuncs>`. Rerun the test. Now just before the convergence
error you should see additional output similar to this:
.. code-block::
.. include:: ../../docs/gen/ex_DIDNOTCONVERGE_msg.rst
CHANGE: filename.v:1: b
CHANGE: filename.v:2: a
This means that signal b and signal a keep changing, inspect the code
that modifies these signals. Note if many signals are getting printed
then most likely all of them are oscillating. It may also be that
e.g. "a" may be oscillating, then "a" feeds signal "c" which then is
also reported as oscillating.
The CHANGE line means that on the given filename and line number that
drove a signal, the signal 'a' kept changing. Inspect the code that
modifies these signals. Note if many signals are getting printed then
most likely all of them are oscillating. It may also be that e.g. "a"
may be oscillating, then "a" feeds signal "c" which then is also
reported as oscillating.
One way DIDNOTCONVERGE may occur is flops are built out of gate
primitives. error. Verilator does not support building flops or latches
out of gate primitives, and any such code must change to use behavioral
primitives. Verilator does not support building flops or latches out of
gate primitives, and any such code must change to use behavioral
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.
Finally, rare, more difficult cases can be debugged like a C++ program;
either enter :command:`gdb` and use its tracing facilities, or edit the
generated C++ code to add appropriate prints to see what is going on.
@@ -572,7 +578,7 @@ List Of Warnings
must end in newline, as otherwise for example :command:`cat` with the
file as an argument may produce undesirable results.
Repair by adding a newline to the end of the file.
Repair by appending a newline to the end of the file.
Disabled by default as this is a code style warning; it will simulate
correctly.
@@ -792,8 +798,8 @@ List Of Warnings
.. TODO better example
Warns that a module has multiple definitions. Generally this indicates
a coding error, or a mistake in a library file and it's good practice to
have one module per file (and only put each file once on the command
a coding error, or a mistake in a library file, and it's good practice
to have one module per file (and only put each file once on the command
line) to avoid these issues. For some gate level netlists duplicates
are sometimes unavoidable, and MODDUP should be disabled.
@@ -1005,9 +1011,25 @@ List Of Warnings
a var/reg must be used as the target of procedural assignments.
.. option:: PROFOUTOFDATE
Warns that threads were scheduled using estimated costs, despite the
fact that data was provided from profile-guided optimization (see
:ref:`Thread PGO`) as fed into Verilator using the
:option:`profile_data` configuration file option. This usually
indicates that the profile data was generated from different Verilog
source code than Verilator is currently running against.
It is recommended to create new profiling data, then rerun Verilator
with the same input source files and that new profiling data.
Ignoring this warning may only slow simulations, it will simulate
correctly.
.. option:: PROTECTED
Warning that a '`pragma protected' section was encountered. The code
Warning that a 'pragma protected' section was encountered. The code
inside the protected region will be partly checked for correctness, but is
otherwise ignored.
@@ -1183,9 +1205,25 @@ List Of Warnings
Faulty example:
.. code-block:: sv
.. include:: ../../docs/gen/ex_STMTDLY_faulty.rst
#100 $finish; //<--- Warning
Results in:
.. 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
important difference.
Some possible work arounds:
* Move the delayed statement into the C++ wrapper file, where the
stimulus and clock generation can be done in C++.
* Convert the statement into a FSM, or other statement that tests
against $time.
.. option:: SYMRSVDWORD
@@ -1353,7 +1391,9 @@ List Of Warnings
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:
simulation which analyzes per-bus.
Faulty example:
.. code-block:: sv
@@ -1466,9 +1506,16 @@ 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
common alternative language construct.
Alternatively, check if the construct is supported by other tools, and
if so please consider submitting a github pull request against the
Verilator sources to implement the missing unsupported feature.
This error may be ignored with :vlopt:`--bbox-unsup`, however this will
make the design simulate incorrectly; see the details under
:vlopt:`--bbox-unsup`.
make the design simulate incorrectly and is only intended for lint
usage; see the details under :vlopt:`--bbox-unsup`.
.. option:: UNUSED
@@ -1598,7 +1645,7 @@ List Of Warnings
* A part select has a different size then needed to index into the
packed or unpacked array (etc).
Verilator for attempts to track the minimum width of unsized constants
Verilator attempts to track the minimum width of unsized constants
and will suppress the warning when the minimum width is appropriate to
fit the required size.
@@ -1607,11 +1654,13 @@ List Of Warnings
The recommendation is to fix these issues by:
* Resizing the variable or constant. E.g. :code:`2'd2` instead of :code:`3'd2`.
* Resizing the variable or constant to match the needed size for the
expression. E.g. :code:`2'd2` instead of :code:`3'd2`.
* Using :code:`'0` or :code:`'1`.
* Using :code:`'0` or :code:`'1` which automatically resize in an
expression.
* Using part select to narrow a variable. E.g. :code:`too_wide[1:0]`.
* Using part selects to narrow a variable. E.g. :code:`too_wide[1:0]`.
* Using concatenate to widen a variable. E.g. :code:`{1'b1, too_narrow}`.
+6 -3
View File
@@ -405,6 +405,9 @@ routines in the sources to rely more heavily on randomness, and
generally try harder not to keep input nodes together when we have the
option to scramble things.
Profile-guided optimization make this a bit better, by adjusting mtask
scheduling, but this does not yet guide the packing into mtasks.
Performance Regression
""""""""""""""""""""""
@@ -502,7 +505,7 @@ Some of the code implementing passes is extremely repetitive, and must be
implemented for each sub-class of ``AstNode``. However, while repetitive,
there is more variability than can be handled in C++ macros.
In Verilator this is implemented by using a Perl script, ``astgen`` to
In Verilator this is implemented by using a script, ``astgen`` to
pre-process the C++ code. For example in ``V3Const.cpp`` this is used to
implement the ``visit()`` functions for each binary operation using the
``TREEOP`` macro.
@@ -750,8 +753,8 @@ test. For run-time tests, this is followed by a call to the ``execute``
subroutine. Both of these functions can optionally be provided with a hash
table as argument specifying additional options.
The test driver assumes by default that the source Verilog file name
matches the PERL driver name. So a test whose driver is
The driver.pl script assumes by default that the source Verilog file name
matches the test script name. So a test whose driver is
``t/t_mytest.pl`` will expect a Verilog source file ``t/t_mytest.v``.
This can be changed using the ``top_filename`` subroutine, for example
-1
View File
@@ -676,7 +676,6 @@ prev
printf
printtimescale
profcfunc
profcfuncs
prototyptes
ps
pthread
+1 -1
View File
@@ -99,7 +99,7 @@ run:
@echo "-- DONE --------------------"
@echo "Note: Once this example is understood, see examples/cmake_tracing_sc."
@echo "Note: Also see the EXAMPLE section in the verilator manpage/document."
@echo "Note: See also https://verilator.org/guide/latest/examples.html"
clean mostlyclean distclean maintainer-clean:
@rm -rf build logs
+1 -1
View File
@@ -40,7 +40,7 @@ default:
obj_dir/Vtop
@echo "-- DONE --------------------"
@echo "Note: Once this example is understood, see examples/make_tracing_c."
@echo "Note: Also see the EXAMPLE section in the verilator manpage/document."
@echo "Note: See also https://verilator.org/guide/latest/examples.html"
######################################################################
+2 -1
View File
@@ -4,7 +4,8 @@
// any use, without warranty, 2017 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
// See also the EXAMPLE section in the verilator manpage/document.
// See also https://verilator.org/guide/latest/examples.html"
module top;
initial begin
$display("Hello World!");
+1 -1
View File
@@ -49,7 +49,7 @@ run:
obj_dir/Vtop
@echo "-- DONE --------------------"
@echo "Note: Once this example is understood, see examples/make_tracing_sc."
@echo "Note: Also see the EXAMPLE section in the verilator manpage/document."
@echo "Note: See also https://verilator.org/guide/latest/examples.html"
######################################################################
+2 -1
View File
@@ -4,7 +4,8 @@
// any use, without warranty, 2017 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
// See also the EXAMPLE section in the verilator manpage/document.
// See also https://verilator.org/guide/latest/examples.html"
module top;
initial begin
$display("Hello World!");
+2 -1
View File
@@ -4,7 +4,8 @@
// any use, without warranty, 2019 by Todd Strader.
// SPDX-License-Identifier: CC0-1.0
// See also the EXAMPLE section in the verilator manpage/document.
// See also https://verilator.org/guide/latest/examples.html"
module top (input clk);
integer cyc = 0;
+2 -7
View File
@@ -4989,9 +4989,7 @@ unsigned int secnum = 0;
int blocks_skipped = 0;
fst_off_t blkpos = 0;
uint64_t seclen, beg_tim;
#ifdef FST_DEBUG
uint64_t end_tim;
#endif
uint64_t frame_uclen, frame_clen, frame_maxhandle, vc_maxhandle;
fst_off_t vc_start;
fst_off_t indx_pntr, indx_pos;
@@ -5058,14 +5056,11 @@ for(;;)
if(!seclen) break;
beg_tim = fstReaderUint64(xc->f);
#ifdef FST_DEBUG
end_tim =
#endif
fstReaderUint64(xc->f);
end_tim = fstReaderUint64(xc->f);
if(xc->limit_range_valid)
{
if(beg_tim < xc->limit_range_start)
if(end_tim < xc->limit_range_start)
{
blocks_skipped++;
blkpos += seclen;
+272 -262
View File
File diff suppressed because it is too large Load Diff
+17 -2127
View File
File diff suppressed because it is too large Load Diff
+13 -6
View File
@@ -9,21 +9,20 @@
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
######################################################################
PERL = @PERL@
PYTHON3 = @PYTHON3@
AR = @AR@
CXX = @CXX@
LINK = @CXX@
AR = ar
RANLIB = ranlib
OBJCACHE ?= @OBJCACHE@
PERL = @PERL@
PYTHON3 = @PYTHON3@
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
CFG_WITH_LONGTESTS = @CFG_WITH_LONGTESTS@
# Compiler flags to enable profiling
CFG_CXXFLAGS_PROFILE = @CFG_CXXFLAGS_PROFILE@
# Select newest language
CFG_CXXFLAGS_STD_NEWEST = @CFG_CXXFLAGS_STD_NEWEST@
# Select oldest language (for Verilator internal testing only)
CFG_CXXFLAGS_STD_OLDEST = @CFG_CXXFLAGS_STD_OLDEST@
# Compiler flags to use to turn off unused and generated code warnings, such as -Wno-div-by-zero
CFG_CXXFLAGS_NO_UNUSED = @CFG_CXXFLAGS_NO_UNUSED@
# Compiler flags that turn on extra warnings
@@ -104,6 +103,14 @@ OPT_FAST = -Os
# to change this as the library is small, but can have significant speed impact.
OPT_GLOBAL = -Os
#######################################################################
##### Profile builds
ifeq ($(VM_PROFC),1)
CPPFLAGS += $(CFG_CXXFLAGS_PROFILE)
LDFLAGS += $(CFG_CXXFLAGS_PROFILE)
endif
#######################################################################
##### SystemC builds
+42 -38
View File
@@ -132,7 +132,7 @@ private:
int valueIndex(const std::string& value) VL_REQUIRES(m_mutex) {
const auto iter = m_valueIndexes.find(value);
if (iter != m_valueIndexes.end()) return iter->second;
m_nextIndex++;
++m_nextIndex;
assert(m_nextIndex > 0); // Didn't rollover
m_valueIndexes.emplace(value, m_nextIndex);
m_indexValues.emplace(m_nextIndex, value);
@@ -143,8 +143,9 @@ private:
std::string rtn;
for (const char* pos = text.c_str(); *pos; ++pos) {
if (!std::isprint(*pos) || *pos == '%' || *pos == '"') {
char hex[10];
VL_SNPRINTF(hex, 10, "%%%02X", pos[0]);
constexpr size_t LEN_MAX_HEX = 20;
char hex[LEN_MAX_HEX];
VL_SNPRINTF(hex, LEN_MAX_HEX, "%%%02X", pos[0]);
rtn += hex;
} else {
rtn += *pos;
@@ -165,11 +166,11 @@ private:
const std::string& value) VL_PURE {
std::string name;
if (key.length() == 1 && std::isalpha(key[0])) {
name += std::string("\001") + key;
name += std::string{"\001"} + key;
} else {
name += std::string("\001") + dequote(key);
name += std::string{"\001"} + dequote(key);
}
name += std::string("\002") + dequote(value);
name += std::string{"\002"} + dequote(value);
return name;
}
static std::string combineHier(const std::string& old, const std::string& add) VL_PURE {
@@ -180,33 +181,34 @@ private:
if (old.empty()) return add;
if (add.empty()) return old;
const char* a = old.c_str();
const char* b = add.c_str();
const char* const a = old.c_str();
const char* const b = add.c_str();
// Scan forward to first mismatch
const char* apre = a;
const char* bpre = b;
while (*apre == *bpre) {
apre++;
bpre++;
++apre;
++bpre;
}
// We used to backup and split on only .'s but it seems better to be verbose
// and not assume . is the separator
std::string prefix = std::string(a, apre - a);
const size_t prefix_len = apre - a;
const std::string prefix = std::string{a, prefix_len};
// Scan backward to last mismatch
const char* apost = a + std::strlen(a) - 1;
const char* bpost = b + std::strlen(b) - 1;
while (*apost == *bpost && apost > apre && bpost > bpre) {
apost--;
bpost--;
--apost;
--bpost;
}
// Forward to . so we have a whole word
std::string suffix = *bpost ? std::string(bpost + 1) : "";
const std::string suffix = *bpost ? std::string{bpost + 1} : "";
std::string out = prefix + "*" + suffix;
const std::string out = prefix + "*" + suffix;
// cout << "\nch pre="<<prefix<<" s="<<suffix<<"\nch a="<<old<<"\nch b="<<add
// <<"\ncho="<<out<<endl;
@@ -217,7 +219,7 @@ private:
for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
if (itemp->m_keys[i] != VerilatedCovConst::KEY_UNDEF) {
// We don't compare keys, only values
std::string val = m_indexValues[itemp->m_vals[i]];
const std::string val = m_indexValues[itemp->m_vals[i]];
if (std::string::npos != val.find(match)) { // Found
return true;
}
@@ -251,19 +253,19 @@ private:
public:
// PUBLIC METHODS
void forcePerInstance(bool flag) VL_MT_SAFE_EXCLUDES(m_mutex) {
void forcePerInstance(const bool flag) VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce();
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_forcePerInstance = flag;
}
void clear() VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce();
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
clearGuts();
}
void clearNonMatch(const char* matchp) VL_MT_SAFE_EXCLUDES(m_mutex) {
void clearNonMatch(const char* const matchp) VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce();
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
if (matchp && matchp[0]) {
ItemList newlist;
for (const auto& itemp : m_items) {
@@ -278,29 +280,29 @@ public:
}
void zero() VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce();
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
for (const auto& itemp : m_items) itemp->zero();
}
// We assume there's always call to i/f/p in that order
void inserti(VerilatedCovImpItem* itemp) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
assert(!m_insertp);
m_insertp = itemp;
}
void insertf(const char* filenamep, int lineno) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
void insertf(const char* const filenamep, const int lineno) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
m_insertFilenamep = filenamep;
m_insertLineno = lineno;
}
void insertp(const char* ckeyps[VerilatedCovConst::MAX_KEYS],
const char* valps[VerilatedCovConst::MAX_KEYS]) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
assert(m_insertp);
// First two key/vals are filename
ckeyps[0] = "filename";
valps[0] = m_insertFilenamep;
std::string linestr = vlCovCvtToStr(m_insertLineno);
const std::string linestr = vlCovCvtToStr(m_insertLineno);
ckeyps[1] = "lineno";
valps[1] = linestr.c_str();
// Default page if not specified
@@ -308,7 +310,8 @@ public:
while (const char* foundp = std::strchr(fnstartp, '/')) fnstartp = foundp + 1;
const char* fnendp = fnstartp;
for (; *fnendp && *fnendp != '.'; fnendp++) {}
std::string page_default = "sp_user/" + std::string(fnstartp, fnendp - fnstartp);
const size_t page_len = fnendp - fnstartp;
const std::string page_default = "sp_user/" + std::string{fnstartp, page_len};
ckeyps[2] = "page";
valps[2] = page_default.c_str();
@@ -337,9 +340,9 @@ public:
// cout<<" "<<__FUNCTION__<<" "<<key<<" = "<<val<<endl;
m_insertp->m_keys[addKeynum] = valueIndex(key);
m_insertp->m_vals[addKeynum] = valueIndex(val);
addKeynum++;
++addKeynum;
if (VL_UNCOVERABLE(!legalKey(key))) {
std::string msg
const std::string msg
= ("%Error: Coverage keys of one character, or letter+digit are illegal: "
+ key); // LCOV_EXCL_LINE
VL_FATAL_MT("", 0, "", msg.c_str());
@@ -353,15 +356,15 @@ public:
void write(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce();
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
#ifndef VM_COVERAGE
VL_FATAL_MT("", 0, "", "%Error: Called VerilatedCov::write when VM_COVERAGE disabled\n");
#endif
selftest();
std::ofstream os(filename);
std::ofstream os{filename};
if (os.fail()) {
std::string msg = std::string("%Error: Can't write '") + filename + "'";
const std::string msg = std::string{"%Error: Can't write '"} + filename + "'";
VL_FATAL_MT("", 0, "", msg.c_str());
return;
}
@@ -377,8 +380,9 @@ public:
for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
if (itemp->m_keys[i] != VerilatedCovConst::KEY_UNDEF) {
std::string key = VerilatedCovKey::shortKey(m_indexValues[itemp->m_keys[i]]);
std::string val = m_indexValues[itemp->m_vals[i]];
const std::string key
= VerilatedCovKey::shortKey(m_indexValues[itemp->m_keys[i]]);
const std::string val = m_indexValues[itemp->m_vals[i]];
if (key == VL_CIK_PER_INSTANCE) {
if (val != "0") per_instance = true;
}
@@ -435,10 +439,10 @@ void VerilatedCovContext::clearNonMatch(const char* matchp) VL_MT_SAFE {
void VerilatedCovContext::zero() VL_MT_SAFE { impp()->zero(); }
void VerilatedCovContext::write(const char* filenamep) VL_MT_SAFE { impp()->write(filenamep); }
void VerilatedCovContext::_inserti(vluint32_t* itemp) VL_MT_SAFE {
impp()->inserti(new VerilatedCoverItemSpec<vluint32_t>(itemp));
impp()->inserti(new VerilatedCoverItemSpec<vluint32_t>{itemp});
}
void VerilatedCovContext::_inserti(vluint64_t* itemp) VL_MT_SAFE {
impp()->inserti(new VerilatedCoverItemSpec<vluint64_t>(itemp));
impp()->inserti(new VerilatedCoverItemSpec<vluint64_t>{itemp});
}
void VerilatedCovContext::_insertf(const char* filename, int lineno) VL_MT_SAFE {
impp()->insertf(filename, lineno);
@@ -511,7 +515,7 @@ VerilatedCovContext* VerilatedCov::threadCovp() VL_MT_SAFE {
VerilatedCovContext* VerilatedContext::coveragep() VL_MT_SAFE {
static VerilatedMutex s_mutex;
if (VL_UNLIKELY(!m_coveragep)) {
const VerilatedLockGuard lock(s_mutex);
const VerilatedLockGuard lock{s_mutex};
if (VL_LIKELY(!m_coveragep)) { // Not redundant, prevents race
m_coveragep.reset(new VerilatedCovImp);
}
+90 -90
View File
@@ -68,7 +68,7 @@ svLogic svGetBitselLogic(const svLogicVecVal* sp, int bit) {
| (((sp[VL_BITWORD_I(bit)].bval >> VL_BITBIT_I(bit)) & 1) << 1));
}
void svPutBitselBit(svBitVecVal* dp, int bit, svBit s) { VL_ASSIGNBIT_WI(32, bit, dp, s); }
void svPutBitselBit(svBitVecVal* dp, int bit, svBit s) { VL_ASSIGNBIT_WI(bit, dp, s); }
void svPutBitselLogic(svLogicVecVal* dp, int bit, svLogic s) {
// Verilator doesn't support X/Z so only aval
dp[VL_BITWORD_I(bit)].aval = ((dp[VL_BITWORD_I(bit)].aval & ~(VL_UL(1) << VL_BITBIT_I(bit)))
@@ -79,19 +79,19 @@ void svPutBitselLogic(svLogicVecVal* dp, int bit, svLogic s) {
void svGetPartselBit(svBitVecVal* dp, const svBitVecVal* sp, int lsb, int width) {
// See also VL_SEL_WWI
int msb = lsb + width - 1;
int word_shift = VL_BITWORD_I(lsb);
const int msb = lsb + width - 1;
const int word_shift = VL_BITWORD_I(lsb);
if (VL_BITBIT_I(lsb) == 0) {
// Just a word extract
for (int i = 0; i < VL_WORDS_I(width); ++i) dp[i] = sp[i + word_shift];
} else {
int loffset = lsb & VL_SIZEBITS_I;
int nbitsfromlow = 32 - loffset; // bits that end up in lword (know loffset!=0)
const int loffset = lsb & VL_SIZEBITS_I;
const int nbitsfromlow = 32 - loffset; // bits that end up in lword (know loffset!=0)
// Middle words
int words = VL_WORDS_I(msb - lsb + 1);
const int words = VL_WORDS_I(msb - lsb + 1);
for (int i = 0; i < words; ++i) {
dp[i] = sp[i + word_shift] >> loffset;
int upperword = i + word_shift + 1;
const int upperword = i + word_shift + 1;
if (upperword <= static_cast<int>(VL_BITWORD_I(msb))) {
dp[i] |= sp[upperword] << nbitsfromlow;
}
@@ -101,20 +101,20 @@ void svGetPartselBit(svBitVecVal* dp, const svBitVecVal* sp, int lsb, int width)
dp[VL_WORDS_I(width) - 1] &= VL_MASK_I(width);
}
void svGetPartselLogic(svLogicVecVal* dp, const svLogicVecVal* sp, int lsb, int width) {
int msb = lsb + width - 1;
int word_shift = VL_BITWORD_I(lsb);
const int msb = lsb + width - 1;
const int word_shift = VL_BITWORD_I(lsb);
if (VL_BITBIT_I(lsb) == 0) {
// Just a word extract
for (int i = 0; i < VL_WORDS_I(width); ++i) dp[i] = sp[i + word_shift];
} else {
int loffset = lsb & VL_SIZEBITS_I;
int nbitsfromlow = 32 - loffset; // bits that end up in lword (know loffset!=0)
const int loffset = lsb & VL_SIZEBITS_I;
const int nbitsfromlow = 32 - loffset; // bits that end up in lword (know loffset!=0)
// Middle words
int words = VL_WORDS_I(msb - lsb + 1);
const int words = VL_WORDS_I(msb - lsb + 1);
for (int i = 0; i < words; ++i) {
dp[i].aval = sp[i + word_shift].aval >> loffset;
dp[i].bval = sp[i + word_shift].bval >> loffset;
int upperword = i + word_shift + 1;
const int upperword = i + word_shift + 1;
if (upperword <= static_cast<int>(VL_BITWORD_I(msb))) {
dp[i].aval |= sp[upperword].aval << nbitsfromlow;
dp[i].bval |= sp[upperword].bval << nbitsfromlow;
@@ -127,22 +127,22 @@ void svGetPartselLogic(svLogicVecVal* dp, const svLogicVecVal* sp, int lsb, int
}
void svPutPartselBit(svBitVecVal* dp, const svBitVecVal s, int lbit, int width) {
// See also _vl_insert_WI
int hbit = lbit + width - 1;
int hoffset = VL_BITBIT_I(hbit);
int loffset = VL_BITBIT_I(lbit);
const int hbit = lbit + width - 1;
const int hoffset = VL_BITBIT_I(hbit);
const int loffset = VL_BITBIT_I(lbit);
if (hoffset == VL_SIZEBITS_I && loffset == 0) {
// Fast and common case, word based insertion
dp[VL_BITWORD_I(lbit)] = s;
} else {
int hword = VL_BITWORD_I(hbit);
int lword = VL_BITWORD_I(lbit);
const int hword = VL_BITWORD_I(hbit);
const int lword = VL_BITWORD_I(lbit);
if (hword == lword) { // know < 32 bits because above checks it
IData insmask = (VL_MASK_I(hoffset - loffset + 1)) << loffset;
const IData insmask = (VL_MASK_I(hoffset - loffset + 1)) << loffset;
dp[lword] = (dp[lword] & ~insmask) | ((s << loffset) & insmask);
} else {
IData hinsmask = (VL_MASK_I(hoffset - 0 + 1)) << 0;
IData linsmask = (VL_MASK_I(31 - loffset + 1)) << loffset;
int nbitsonright = 32 - loffset; // bits that end up in lword
const IData hinsmask = (VL_MASK_I(hoffset - 0 + 1)) << 0;
const IData linsmask = (VL_MASK_I(31 - loffset + 1)) << loffset;
const int nbitsonright = 32 - loffset; // bits that end up in lword
dp[lword] = (dp[lword] & ~linsmask) | ((s << loffset) & linsmask);
dp[hword] = (dp[hword] & ~hinsmask) | ((s >> nbitsonright) & hinsmask);
}
@@ -150,24 +150,24 @@ void svPutPartselBit(svBitVecVal* dp, const svBitVecVal s, int lbit, int width)
}
// cppcheck-suppress passedByValue
void svPutPartselLogic(svLogicVecVal* dp, const svLogicVecVal s, int lbit, int width) {
int hbit = lbit + width - 1;
int hoffset = VL_BITBIT_I(hbit);
int loffset = VL_BITBIT_I(lbit);
const int hbit = lbit + width - 1;
const int hoffset = VL_BITBIT_I(hbit);
const int loffset = VL_BITBIT_I(lbit);
if (hoffset == VL_SIZEBITS_I && loffset == 0) {
// Fast and common case, word based insertion
dp[VL_BITWORD_I(lbit)].aval = s.aval;
dp[VL_BITWORD_I(lbit)].bval = s.bval;
} else {
int hword = VL_BITWORD_I(hbit);
int lword = VL_BITWORD_I(lbit);
const int hword = VL_BITWORD_I(hbit);
const int lword = VL_BITWORD_I(lbit);
if (hword == lword) { // know < 32 bits because above checks it
IData insmask = (VL_MASK_I(hoffset - loffset + 1)) << loffset;
const IData insmask = (VL_MASK_I(hoffset - loffset + 1)) << loffset;
dp[lword].aval = (dp[lword].aval & ~insmask) | ((s.aval << loffset) & insmask);
dp[lword].bval = (dp[lword].bval & ~insmask) | ((s.bval << loffset) & insmask);
} else {
IData hinsmask = (VL_MASK_I(hoffset - 0 + 1)) << 0;
IData linsmask = (VL_MASK_I(31 - loffset + 1)) << loffset;
int nbitsonright = 32 - loffset; // bits that end up in lword
const IData hinsmask = (VL_MASK_I(hoffset - 0 + 1)) << 0;
const IData linsmask = (VL_MASK_I(31 - loffset + 1)) << loffset;
const int nbitsonright = 32 - loffset; // bits that end up in lword
dp[lword].aval = (dp[lword].aval & ~linsmask) | ((s.aval << loffset) & linsmask);
dp[lword].bval = (dp[lword].bval & ~linsmask) | ((s.bval << loffset) & linsmask);
dp[hword].aval = (dp[hword].aval & ~hinsmask) | ((s.aval >> nbitsonright) & hinsmask);
@@ -184,7 +184,7 @@ static inline const VerilatedDpiOpenVar* _vl_openhandle_varp(const svOpenArrayHa
VL_FATAL_MT(__FILE__, __LINE__, "",
"%%Error: DPI svOpenArrayHandle function called with nullptr handle");
}
const VerilatedDpiOpenVar* varp = reinterpret_cast<const VerilatedDpiOpenVar*>(h);
const VerilatedDpiOpenVar* const varp = reinterpret_cast<const VerilatedDpiOpenVar*>(h);
if (VL_UNLIKELY(!varp->magicOk())) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"%%Error: DPI svOpenArrayHandle function called with non-Verilator handle");
@@ -205,13 +205,13 @@ int svDimensions(const svOpenArrayHandle h) { return _vl_openhandle_varp(h)->udi
// Return pointer to open array data, or nullptr if not in IEEE standard C layout
void* svGetArrayPtr(const svOpenArrayHandle h) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h);
if (VL_UNLIKELY(!varp->isDpiStdLayout())) return nullptr;
return varp->datap();
}
// Return size of open array, or 0 if not in IEEE standard C layout
int svSizeOfArray(const svOpenArrayHandle h) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h);
if (VL_UNLIKELY(!varp->isDpiStdLayout())) return 0;
// Truncate 64 bits to int; DPI is limited to 4GB
return static_cast<int>(varp->totalSize());
@@ -264,22 +264,22 @@ static void* _vl_svGetArrElemPtr(const svOpenArrayHandle h, int nargs, int indx1
int indx3) VL_MT_SAFE {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
if (VL_UNLIKELY(!varp->isDpiStdLayout())) return nullptr;
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
return datap;
}
// Copy to user bit array from simulator open array
static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int nargs,
int indx1, int indx2, int indx3) VL_MT_SAFE {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
if (VL_UNLIKELY(!datap)) return;
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
case VLVT_UINT8: d[0] = *(reinterpret_cast<CData*>(datap)); return;
case VLVT_UINT16: d[0] = *(reinterpret_cast<SData*>(datap)); return;
case VLVT_UINT32: d[0] = *(reinterpret_cast<IData*>(datap)); return;
case VLVT_UINT64: {
WData lwp[2];
VlWide<2> lwp;
VL_SET_WQ(lwp, *(reinterpret_cast<QData*>(datap)));
d[0] = lwp[0];
d[1] = lwp[1];
@@ -299,8 +299,8 @@ static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s,
// Copy to user logic array from simulator open array
static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int nargs,
int indx1, int indx2, int indx3) VL_MT_SAFE {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
if (VL_UNLIKELY(!datap)) return;
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
case VLVT_UINT8:
@@ -316,7 +316,7 @@ static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandl
d[0].bval = 0;
return;
case VLVT_UINT64: {
WData lwp[2];
VlWide<2> lwp;
VL_SET_WQ(lwp, *(reinterpret_cast<QData*>(datap)));
d[0].aval = lwp[0];
d[0].bval = 0;
@@ -342,8 +342,8 @@ static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandl
// Copy to simulator open array from from user bit array
static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int nargs,
int indx1, int indx2, int indx3) VL_MT_SAFE {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
if (VL_UNLIKELY(!datap)) return;
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
case VLVT_UINT8: *(reinterpret_cast<CData*>(datap)) = s[0]; return;
@@ -364,8 +364,8 @@ static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecV
// Copy to simulator open array from from user logic array
static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s,
int nargs, int indx1, int indx2, int indx3) VL_MT_SAFE {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
if (VL_UNLIKELY(!datap)) return;
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
case VLVT_UINT8: *(reinterpret_cast<CData*>(datap)) = s[0].aval; return;
@@ -386,10 +386,10 @@ static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogic
// Return bit from simulator open array
static svBit _vl_svGetBitArrElem(const svOpenArrayHandle s, int nargs, int indx1, int indx2,
int indx3, int indx4) VL_MT_SAFE {
int indx3, int) VL_MT_SAFE {
// One extra index supported, as need bit number
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
if (VL_UNLIKELY(!datap)) return 0;
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
case VLVT_UINT8: return (*(reinterpret_cast<CData*>(datap))) & 1;
@@ -401,11 +401,11 @@ static svBit _vl_svGetBitArrElem(const svOpenArrayHandle s, int nargs, int indx1
}
// Update simulator open array from bit
static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int nargs, int indx1,
int indx2, int indx3, int indx4) VL_MT_SAFE {
int indx2, int indx3, int) VL_MT_SAFE {
// One extra index supported, as need bit number
value &= 1; // Make sure clean
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
if (VL_UNLIKELY(!datap)) return;
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
case VLVT_UINT8: *(reinterpret_cast<CData*>(datap)) = value; return;
@@ -420,7 +420,7 @@ static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int narg
// DPI accessors that simply call above functions
void* svGetArrElemPtr(const svOpenArrayHandle h, int indx1, ...) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h);
void* datap;
va_list ap;
va_start(ap, indx1);
@@ -428,13 +428,13 @@ void* svGetArrElemPtr(const svOpenArrayHandle h, int indx1, ...) {
switch (varp->udims()) {
case 1: datap = _vl_svGetArrElemPtr(h, 1, indx1, 0, 0); break;
case 2: {
int indx2 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
datap = _vl_svGetArrElemPtr(h, 2, indx1, indx2, 0);
break;
}
case 3: {
int indx2 = va_arg(ap, int);
int indx3 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
const int indx3 = va_arg(ap, int);
datap = _vl_svGetArrElemPtr(h, 3, indx1, indx2, indx3);
break;
}
@@ -454,19 +454,19 @@ void* svGetArrElemPtr3(const svOpenArrayHandle h, int indx1, int indx2, int indx
}
void svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int indx1, ...) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
va_list ap;
va_start(ap, indx1);
switch (varp->udims()) {
case 1: _vl_svPutBitArrElemVecVal(d, s, 1, indx1, 0, 0); break;
case 2: {
int indx2 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
_vl_svPutBitArrElemVecVal(d, s, 2, indx1, indx2, 0);
break;
}
case 3: {
int indx2 = va_arg(ap, int);
int indx3 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
const int indx3 = va_arg(ap, int);
_vl_svPutBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
break;
}
@@ -486,19 +486,19 @@ void svPutBitArrElem3VecVal(const svOpenArrayHandle d, const svBitVecVal* s, int
_vl_svPutBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
}
void svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s, int indx1, ...) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
va_list ap;
va_start(ap, indx1);
switch (varp->udims()) {
case 1: _vl_svPutLogicArrElemVecVal(d, s, 1, indx1, 0, 0); break;
case 2: {
int indx2 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
_vl_svPutLogicArrElemVecVal(d, s, 2, indx1, indx2, 0);
break;
}
case 3: {
int indx2 = va_arg(ap, int);
int indx3 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
const int indx3 = va_arg(ap, int);
_vl_svPutLogicArrElemVecVal(d, s, 3, indx1, indx2, indx3);
break;
}
@@ -522,19 +522,19 @@ void svPutLogicArrElem3VecVal(const svOpenArrayHandle d, const svLogicVecVal* s,
// From simulator storage into user space
void svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1, ...) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
va_list ap;
va_start(ap, indx1);
switch (varp->udims()) {
case 1: _vl_svGetBitArrElemVecVal(d, s, 1, indx1, 0, 0); break;
case 2: {
int indx2 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
_vl_svGetBitArrElemVecVal(d, s, 2, indx1, indx2, 0);
break;
}
case 3: {
int indx2 = va_arg(ap, int);
int indx3 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
const int indx3 = va_arg(ap, int);
_vl_svGetBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
break;
}
@@ -553,19 +553,19 @@ void svGetBitArrElem3VecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1
_vl_svGetBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
}
void svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int indx1, ...) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
va_list ap;
va_start(ap, indx1);
switch (varp->udims()) {
case 1: _vl_svGetLogicArrElemVecVal(d, s, 1, indx1, 0, 0); break;
case 2: {
int indx2 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
_vl_svGetLogicArrElemVecVal(d, s, 2, indx1, indx2, 0);
break;
}
case 3: {
int indx2 = va_arg(ap, int);
int indx3 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
const int indx3 = va_arg(ap, int);
_vl_svGetLogicArrElemVecVal(d, s, 3, indx1, indx2, indx3);
break;
}
@@ -585,20 +585,20 @@ void svGetLogicArrElem3VecVal(svLogicVecVal* d, const svOpenArrayHandle s, int i
}
svBit svGetBitArrElem(const svOpenArrayHandle s, int indx1, ...) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
svBit out;
va_list ap;
va_start(ap, indx1);
switch (varp->udims()) {
case 1: out = _vl_svGetBitArrElem(s, 1, indx1, 0, 0, 0); break;
case 2: {
int indx2 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
out = _vl_svGetBitArrElem(s, 2, indx1, indx2, 0, 0);
break;
}
case 3: {
int indx2 = va_arg(ap, int);
int indx3 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
const int indx3 = va_arg(ap, int);
out = _vl_svGetBitArrElem(s, 3, indx1, indx2, indx3, 0);
break;
}
@@ -618,20 +618,20 @@ svBit svGetBitArrElem3(const svOpenArrayHandle s, int indx1, int indx2, int indx
}
svLogic svGetLogicArrElem(const svOpenArrayHandle s, int indx1, ...) {
// Verilator doesn't support X/Z so can just call Bit version
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
svBit out;
va_list ap;
va_start(ap, indx1);
switch (varp->udims()) {
case 1: out = _vl_svGetBitArrElem(s, 1, indx1, 0, 0, 0); break;
case 2: {
int indx2 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
out = _vl_svGetBitArrElem(s, 2, indx1, indx2, 0, 0);
break;
}
case 3: {
int indx2 = va_arg(ap, int);
int indx3 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
const int indx3 = va_arg(ap, int);
out = _vl_svGetBitArrElem(s, 3, indx1, indx2, indx3, 0);
break;
}
@@ -654,19 +654,19 @@ svLogic svGetLogicArrElem3(const svOpenArrayHandle s, int indx1, int indx2, int
}
void svPutBitArrElem(const svOpenArrayHandle d, svBit value, int indx1, ...) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
va_list ap;
va_start(ap, indx1);
switch (varp->udims()) {
case 1: _vl_svPutBitArrElem(d, value, 1, indx1, 0, 0, 0); break;
case 2: {
int indx2 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
_vl_svPutBitArrElem(d, value, 2, indx1, indx2, 0, 0);
break;
}
case 3: {
int indx2 = va_arg(ap, int);
int indx3 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
const int indx3 = va_arg(ap, int);
_vl_svPutBitArrElem(d, value, 3, indx1, indx2, indx3, 0);
break;
}
@@ -685,19 +685,19 @@ void svPutBitArrElem3(const svOpenArrayHandle d, svBit value, int indx1, int ind
}
void svPutLogicArrElem(const svOpenArrayHandle d, svLogic value, int indx1, ...) {
// Verilator doesn't support X/Z so can just call Bit version
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
va_list ap;
va_start(ap, indx1);
switch (varp->udims()) {
case 1: _vl_svPutBitArrElem(d, value, 1, indx1, 0, 0, 0); break;
case 2: {
int indx2 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
_vl_svPutBitArrElem(d, value, 2, indx1, indx2, 0, 0);
break;
}
case 3: {
int indx2 = va_arg(ap, int);
int indx3 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
const int indx3 = va_arg(ap, int);
_vl_svPutBitArrElem(d, value, 3, indx1, indx2, indx3, 0);
break;
}
@@ -732,15 +732,15 @@ svScope svGetScope() {
}
svScope svSetScope(const svScope scope) {
const VerilatedScope* prevScopep = Verilated::dpiScope();
const VerilatedScope* vscopep = reinterpret_cast<const VerilatedScope*>(scope);
const VerilatedScope* const prevScopep = Verilated::dpiScope();
const VerilatedScope* const vscopep = reinterpret_cast<const VerilatedScope*>(scope);
Verilated::dpiScope(vscopep);
// NOLINTNEXTLINE(google-readability-casting)
return (svScope)(prevScopep);
}
const char* svGetNameFromScope(const svScope scope) {
const VerilatedScope* vscopep = reinterpret_cast<const VerilatedScope*>(scope);
const VerilatedScope* const vscopep = reinterpret_cast<const VerilatedScope*>(scope);
return vscopep->name();
}
+11 -11
View File
@@ -37,7 +37,7 @@
// Convert svBitVecVal to Verilator internal data
static inline void VL_SET_W_SVBV(int obits, WDataOutP owp, const svBitVecVal* lwp) VL_MT_SAFE {
int words = VL_WORDS_I(obits);
const int words = VL_WORDS_I(obits);
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i];
owp[words - 1] = lwp[words - 1] & VL_MASK_I(obits);
}
@@ -47,20 +47,20 @@ static inline QData VL_SET_Q_SVBV(const svBitVecVal* lwp) VL_MT_SAFE {
static inline IData VL_SET_I_SVBV(const svBitVecVal* lwp) VL_MT_SAFE { return lwp[0]; }
// Convert Verilator internal data to svBitVecVal
static inline void VL_SET_SVBV_W(int obits, svBitVecVal* owp, WDataInP lwp) VL_MT_SAFE {
int words = VL_WORDS_I(obits);
static inline void VL_SET_SVBV_W(int obits, svBitVecVal* owp, const WDataInP lwp) VL_MT_SAFE {
const int words = VL_WORDS_I(obits);
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i];
owp[words - 1] = lwp[words - 1] & VL_MASK_I(obits);
}
static inline void VL_SET_SVBV_I(int, svBitVecVal* owp, IData ld) VL_MT_SAFE { owp[0] = ld; }
static inline void VL_SET_SVBV_Q(int, svBitVecVal* owp, QData ld) VL_MT_SAFE {
static inline void VL_SET_SVBV_I(int, svBitVecVal* owp, const IData ld) VL_MT_SAFE { owp[0] = ld; }
static inline void VL_SET_SVBV_Q(int, svBitVecVal* owp, const QData ld) VL_MT_SAFE {
VL_SET_WQ(owp, ld);
}
// Convert svLogicVecVal to Verilator internal data
// Note these functions ignore X/Z in svLogicVecVal
static inline void VL_SET_W_SVLV(int obits, WDataOutP owp, const svLogicVecVal* lwp) VL_MT_SAFE {
int words = VL_WORDS_I(obits);
const int words = VL_WORDS_I(obits);
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i].aval;
owp[words - 1] = lwp[words - 1].aval & VL_MASK_I(obits);
}
@@ -71,18 +71,18 @@ static inline IData VL_SET_I_SVLV(const svLogicVecVal* lwp) VL_MT_SAFE { return
// Convert Verilator internal data to svLogicVecVal
// Note these functions never create X/Z in svLogicVecVal
static inline void VL_SET_SVLV_W(int obits, svLogicVecVal* owp, WDataInP lwp) VL_MT_SAFE {
int words = VL_WORDS_I(obits);
static inline void VL_SET_SVLV_W(int obits, svLogicVecVal* owp, const WDataInP lwp) VL_MT_SAFE {
const int words = VL_WORDS_I(obits);
for (int i = 0; i < words; ++i) owp[i].bval = 0;
for (int i = 0; i < words - 1; ++i) owp[i].aval = lwp[i];
owp[words - 1].aval = lwp[words - 1] & VL_MASK_I(obits);
}
static inline void VL_SET_SVLV_I(int, svLogicVecVal* owp, IData ld) VL_MT_SAFE {
static inline void VL_SET_SVLV_I(int, svLogicVecVal* owp, const IData ld) VL_MT_SAFE {
owp[0].aval = ld;
owp[0].bval = 0;
}
static inline void VL_SET_SVLV_Q(int, svLogicVecVal* owp, QData ld) VL_MT_SAFE {
WData lwp[2];
static inline void VL_SET_SVLV_Q(int, svLogicVecVal* owp, const QData ld) VL_MT_SAFE {
VlWide<2> lwp;
VL_SET_WQ(lwp, ld);
owp[0].aval = lwp[0];
owp[0].bval = 0;
+24 -22
View File
@@ -53,29 +53,31 @@
//=============================================================================
// Check that vltscope_t matches fstScopeType
static_assert((int)FST_ST_VCD_MODULE == (int)VLT_TRACE_SCOPE_MODULE,
static_assert(static_cast<int>(FST_ST_VCD_MODULE) == static_cast<int>(VLT_TRACE_SCOPE_MODULE),
"VLT_TRACE_SCOPE_MODULE mismatches");
static_assert((int)FST_ST_VCD_TASK == (int)VLT_TRACE_SCOPE_TASK,
static_assert(static_cast<int>(FST_ST_VCD_TASK) == static_cast<int>(VLT_TRACE_SCOPE_TASK),
"VLT_TRACE_SCOPE_TASK mismatches");
static_assert((int)FST_ST_VCD_FUNCTION == (int)VLT_TRACE_SCOPE_FUNCTION,
static_assert(static_cast<int>(FST_ST_VCD_FUNCTION) == static_cast<int>(VLT_TRACE_SCOPE_FUNCTION),
"VLT_TRACE_SCOPE_FUNCTION mismatches");
static_assert((int)FST_ST_VCD_BEGIN == (int)VLT_TRACE_SCOPE_BEGIN,
static_assert(static_cast<int>(FST_ST_VCD_BEGIN) == static_cast<int>(VLT_TRACE_SCOPE_BEGIN),
"VLT_TRACE_SCOPE_BEGIN mismatches");
static_assert((int)FST_ST_VCD_FORK == (int)VLT_TRACE_SCOPE_FORK,
static_assert(static_cast<int>(FST_ST_VCD_FORK) == static_cast<int>(VLT_TRACE_SCOPE_FORK),
"VLT_TRACE_SCOPE_FORK mismatches");
static_assert((int)FST_ST_VCD_GENERATE == (int)VLT_TRACE_SCOPE_GENERATE,
static_assert(static_cast<int>(FST_ST_VCD_GENERATE) == static_cast<int>(VLT_TRACE_SCOPE_GENERATE),
"VLT_TRACE_SCOPE_GENERATE mismatches");
static_assert((int)FST_ST_VCD_STRUCT == (int)VLT_TRACE_SCOPE_STRUCT,
static_assert(static_cast<int>(FST_ST_VCD_STRUCT) == static_cast<int>(VLT_TRACE_SCOPE_STRUCT),
"VLT_TRACE_SCOPE_STRUCT mismatches");
static_assert((int)FST_ST_VCD_UNION == (int)VLT_TRACE_SCOPE_UNION,
static_assert(static_cast<int>(FST_ST_VCD_UNION) == static_cast<int>(VLT_TRACE_SCOPE_UNION),
"VLT_TRACE_SCOPE_UNION mismatches");
static_assert((int)FST_ST_VCD_CLASS == (int)VLT_TRACE_SCOPE_CLASS,
static_assert(static_cast<int>(FST_ST_VCD_CLASS) == static_cast<int>(VLT_TRACE_SCOPE_CLASS),
"VLT_TRACE_SCOPE_CLASS mismatches");
static_assert((int)FST_ST_VCD_INTERFACE == (int)VLT_TRACE_SCOPE_INTERFACE,
static_assert(static_cast<int>(FST_ST_VCD_INTERFACE)
== static_cast<int>(VLT_TRACE_SCOPE_INTERFACE),
"VLT_TRACE_SCOPE_INTERFACE mismatches");
static_assert((int)FST_ST_VCD_PACKAGE == (int)VLT_TRACE_SCOPE_PACKAGE,
static_assert(static_cast<int>(FST_ST_VCD_PACKAGE) == static_cast<int>(VLT_TRACE_SCOPE_PACKAGE),
"VLT_TRACE_SCOPE_PACKAGE mismatches");
static_assert((int)FST_ST_VCD_PROGRAM == (int)VLT_TRACE_SCOPE_PROGRAM,
static_assert(static_cast<int>(FST_ST_VCD_PROGRAM) == static_cast<int>(VLT_TRACE_SCOPE_PROGRAM),
"VLT_TRACE_SCOPE_PROGRAM mismatches");
//=============================================================================
@@ -98,7 +100,7 @@ VerilatedFst::~VerilatedFst() {
}
void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_fst = fstWriterCreate(filename, 1);
fstWriterSetPackType(m_fst, FST_WR_PT_LZ4);
fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str()); // lintok-begin-on-ref
@@ -130,14 +132,14 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
}
void VerilatedFst::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
VerilatedTrace<VerilatedFst>::closeBase();
fstWriterClose(m_fst);
m_fst = nullptr;
}
void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
VerilatedTrace<VerilatedFst>::flushBase();
fstWriterFlushContext(m_fst);
}
@@ -150,7 +152,7 @@ void VerilatedFst::emitTimeChange(vluint64_t timeui) { fstWriterEmitTimeChange(m
void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, vluint32_t elements,
unsigned int minValbits, const char** itemNamesp,
const char** itemValuesp) {
fstEnumHandle enumNum
const fstEnumHandle enumNum
= fstWriterCreateEnumTable(m_fst, name, elements, minValbits, itemNamesp, itemValuesp);
m_local2fstdtype[dtypenum] = enumNum;
}
@@ -161,11 +163,11 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
VerilatedTrace<VerilatedFst>::declCode(code, bits, false);
std::istringstream nameiss(name);
std::istringstream nameiss{name};
std::istream_iterator<std::string> beg(nameiss);
std::istream_iterator<std::string> end;
std::list<std::string> tokens(beg, end); // Split name
std::string symbol_name(tokens.back());
std::string symbol_name{tokens.back()};
tokens.pop_back(); // Remove symbol name from hierarchy
tokens.insert(tokens.begin(), moduleName()); // Add current module to the hierarchy
std::string tmpModName;
@@ -191,8 +193,8 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
tmpModName = *new_it;
tmpModName.pop_back();
// If the scope ends with a non-ascii character, it will be 0x80 + fstScopeType
fstWriterSetScope(m_fst, (fstScopeType)(new_it->back() & 0x7f), tmpModName.c_str(),
nullptr);
fstWriterSetScope(m_fst, static_cast<fstScopeType>(new_it->back() & 0x7f),
tmpModName.c_str(), nullptr);
} else
fstWriterSetScope(m_fst, FST_ST_VCD_SCOPE, new_it->c_str(), nullptr);
@@ -202,11 +204,11 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
std::stringstream name_ss;
name_ss << symbol_name;
if (array) name_ss << "(" << arraynum << ")";
if (array) name_ss << "[" << arraynum << "]";
std::string name_str = name_ss.str();
if (dtypenum > 0) {
fstEnumHandle enumNum = m_local2fstdtype[dtypenum];
const fstEnumHandle enumNum = m_local2fstdtype[dtypenum];
fstWriterEmitEnumTableRef(m_fst, enumNum);
}
+2 -2
View File
@@ -42,9 +42,9 @@ public:
// We want to avoid a depreciated warning, but still be back compatible.
// Turning off the message just for this still results in an
// annoying "to turn off" message.
sc_time t1sec(1, SC_SEC);
const sc_time t1sec(1, SC_SEC);
if (t1sec.to_default_time_units() != 0) {
sc_time tunits(1.0 / t1sec.to_default_time_units(), SC_SEC);
const sc_time tunits(1.0 / t1sec.to_default_time_units(), SC_SEC);
spTrace()->set_time_unit(tunits.to_string());
}
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
File diff suppressed because it is too large Load Diff
+7 -957
View File
@@ -12,970 +12,20 @@
//*************************************************************************
///
/// \file
/// \brief Verilated string and data-type header
/// \brief Verilated old string and data-type header
///
/// This file is included automatically by Verilator at the top of
/// all C++ files it generates. It is used when strings or other
/// heavyweight types are required; these contents are not part of
/// verilated.h to save compile time when such types aren't used.
/// This file is deprecated, and provided for backwards compatibility.
/// Include verilated.h instead.
///
//*************************************************************************
#ifndef VERILATOR_VERILATED_HEAVY_H_
#define VERILATOR_VERILATED_HEAVY_H_
#ifdef VL_NO_LEGACY
#error "Include <verilated.h> instead of <verilated_heavy.h>"
#endif
#include "verilated.h"
#include <algorithm>
#include <array>
#include <deque>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <unordered_set>
//===================================================================
// String formatters (required by below containers)
extern std::string VL_TO_STRING(CData lhs);
extern std::string VL_TO_STRING(SData lhs);
extern std::string VL_TO_STRING(IData lhs);
extern std::string VL_TO_STRING(QData lhs);
inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; }
extern std::string VL_TO_STRING_W(int words, WDataInP obj);
//===================================================================
// Shuffle RNG
class VlURNG final {
public:
using result_type = size_t;
static constexpr size_t min() { return 0; }
static constexpr size_t max() { return 1ULL << 31; }
size_t operator()() { return VL_MASK_I(31) & VL_RANDOM_I(32); }
};
//===================================================================
// Readmem/Writemem operation classes
class VlReadMem final {
bool m_hex; // Hex format
int m_bits; // Bit width of values
const std::string& m_filename; // Filename
QData m_end; // End address (as specified by user)
FILE* m_fp; // File handle for filename
QData m_addr; // Next address to read
int m_linenum; // Line number last read from file
public:
VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end);
~VlReadMem();
bool isOpen() const { return m_fp != nullptr; }
int linenum() const { return m_linenum; }
bool get(QData& addrr, std::string& valuer);
void setData(void* valuep, const std::string& rhs);
};
class VlWriteMem final {
bool m_hex; // Hex format
int m_bits; // Bit width of values
FILE* m_fp; // File handle for filename
QData m_addr; // Next address to write
public:
VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end);
~VlWriteMem();
bool isOpen() const { return m_fp != nullptr; }
void print(QData addr, bool addrstamp, const void* valuep);
};
//===================================================================
/// Verilog wide unpacked bit container.
/// Similar to std::array<WData, N>, but lighter weight, only methods needed
/// by Verilator, to help compile time.
///
/// For example a Verilog "bit [94:0]" will become a VlWide<3> because 3*32
/// bits are needed to hold the 95 bits. The MSB (bit 96) must always be
/// zero in memory, but during intermediate operations in the Verilated
/// internals is unpredictable.
template <std::size_t T_Words> class VlWide final {
EData m_storage[T_Words];
public:
// cppcheck-suppress uninitVar
VlWide() = default;
~VlWide() = default;
VlWide(const VlWide&) = default;
VlWide(VlWide&&) = default;
// OPERATOR METHODS
VlWide& operator=(const VlWide&) = default;
VlWide& operator=(VlWide&&) = default;
const EData& operator[](size_t index) const { return m_storage[index]; };
EData& operator[](size_t index) { return m_storage[index]; };
operator WDataOutP() { return &m_storage[0]; }
operator WDataInP() const { return &m_storage[0]; }
// METHODS
const EData& at(size_t index) const { return m_storage[index]; }
EData& at(size_t index) { return m_storage[index]; }
WData* data() { return &m_storage[0]; }
const WData* data() const { return &m_storage[0]; }
bool operator<(const VlWide<T_Words>& rhs) const {
return VL_LT_W(T_Words, data(), rhs.data());
}
};
// Convert a C array to std::array reference by pointer magic, without copy.
// Data type (second argument) is so the function template can automatically generate.
template <std::size_t T_Words> VlWide<T_Words>& VL_CVT_W_A(WDataInP inp, const VlWide<T_Words>&) {
return *((VlWide<T_Words>*)inp);
}
template <std::size_t T_Words> std::string VL_TO_STRING(const VlWide<T_Words>& obj) {
return VL_TO_STRING_W(T_Words, obj.data());
}
//===================================================================
// Verilog queue and dynamic array container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
//
// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound
// For dynamic arrays it is always zero
template <class T_Value, size_t T_MaxSize = 0> class VlQueue final {
private:
// TYPES
using Deque = std::deque<T_Value>;
public:
using const_iterator = typename Deque::const_iterator;
private:
// MEMBERS
Deque m_deque; // State of the assoc array
T_Value m_defaultValue; // Default value
public:
// CONSTRUCTORS
// m_defaultValue isn't defaulted. Caller's constructor must do it.
VlQueue() = default;
~VlQueue() = default;
VlQueue(const VlQueue&) = default;
VlQueue(VlQueue&&) = default;
VlQueue& operator=(const VlQueue&) = default;
VlQueue& operator=(VlQueue&&) = default;
// Standard copy constructor works. Verilog: assoca = assocb
// Also must allow conversion from a different T_MaxSize queue
template <size_t U_MaxSize = 0> VlQueue operator=(const VlQueue<T_Value, U_MaxSize>& rhs) {
m_deque = rhs.privateDeque();
if (VL_UNLIKELY(T_MaxSize && T_MaxSize < m_deque.size())) m_deque.resize(T_MaxSize - 1);
return *this;
}
static VlQueue cons(const T_Value& lhs) {
VlQueue out;
out.push_back(lhs);
return out;
}
static VlQueue cons(const T_Value& lhs, const T_Value& rhs) {
VlQueue out;
out.push_back(rhs);
out.push_back(lhs);
return out;
}
static VlQueue cons(const VlQueue& lhs, const T_Value& rhs) {
VlQueue out = lhs;
out.push_front(rhs);
return out;
}
static VlQueue cons(const T_Value& lhs, const VlQueue& rhs) {
VlQueue out = rhs;
out.push_back(lhs);
return out;
}
static VlQueue cons(const VlQueue& lhs, const VlQueue& rhs) {
VlQueue out = rhs;
for (const auto& i : lhs.m_deque) out.push_back(i);
return out;
}
// METHODS
T_Value& atDefault() { return m_defaultValue; }
const T_Value& atDefault() const { return m_defaultValue; }
const Deque& privateDeque() const { return m_deque; }
// Size. Verilog: function int size(), or int num()
int size() const { return m_deque.size(); }
// Clear array. Verilog: function void delete([input index])
void clear() { m_deque.clear(); }
void erase(vlsint32_t index) {
if (VL_LIKELY(index >= 0 && index < m_deque.size()))
m_deque.erase(m_deque.begin() + index);
}
// Dynamic array new[] becomes a renew()
void renew(size_t size) {
clear();
m_deque.resize(size, atDefault());
}
// Dynamic array new[]() becomes a renew_copy()
void renew_copy(size_t size, const VlQueue<T_Value, T_MaxSize>& rhs) {
if (size == 0) {
clear();
} else {
*this = rhs;
m_deque.resize(size, atDefault());
}
}
// function void q.push_front(value)
void push_front(const T_Value& value) {
m_deque.push_front(value);
if (VL_UNLIKELY(T_MaxSize != 0 && m_deque.size() > T_MaxSize)) m_deque.pop_back();
}
// function void q.push_back(value)
void push_back(const T_Value& value) {
if (VL_LIKELY(T_MaxSize == 0 || m_deque.size() < T_MaxSize)) m_deque.push_back(value);
}
// function value_t q.pop_front();
T_Value pop_front() {
if (m_deque.empty()) return m_defaultValue;
T_Value v = m_deque.front();
m_deque.pop_front();
return v;
}
// function value_t q.pop_back();
T_Value pop_back() {
if (m_deque.empty()) return m_defaultValue;
T_Value v = m_deque.back();
m_deque.pop_back();
return v;
}
// Setting. Verilog: assoc[index] = v
// Can't just overload operator[] or provide a "at" reference to set,
// because we need to be able to insert only when the value is set
T_Value& at(vlsint32_t index) {
static T_Value s_throwAway;
// Needs to work for dynamic arrays, so does not use T_MaxSize
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
s_throwAway = atDefault();
return s_throwAway;
} else {
return m_deque[index];
}
}
// Accessing. Verilog: v = assoc[index]
const T_Value& at(vlsint32_t index) const {
static T_Value s_throwAway;
// Needs to work for dynamic arrays, so does not use T_MaxSize
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
return atDefault();
} else {
return m_deque[index];
}
}
// function void q.insert(index, value);
void insert(vlsint32_t index, const T_Value& value) {
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return;
m_deque.insert(m_deque.begin() + index, value);
}
// Return slice q[lsb:msb]
VlQueue slice(vlsint32_t lsb, vlsint32_t msb) const {
VlQueue out;
if (VL_UNLIKELY(lsb < 0)) lsb = 0;
if (VL_UNLIKELY(lsb >= m_deque.size())) lsb = m_deque.size() - 1;
if (VL_UNLIKELY(msb >= m_deque.size())) msb = m_deque.size() - 1;
for (vlsint32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]);
return out;
}
// For save/restore
const_iterator begin() const { return m_deque.begin(); }
const_iterator end() const { return m_deque.end(); }
// Methods
void sort() { std::sort(m_deque.begin(), m_deque.end()); }
template <typename Func> void sort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
std::sort(m_deque.begin(), m_deque.end(), [=](const T_Value& a, const T_Value& b) {
// index number is meaninless with sort, as it changes
return with_func(0, a) < with_func(0, b);
});
}
void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); }
template <typename Func> void rsort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
std::sort(m_deque.rbegin(), m_deque.rend(), [=](const T_Value& a, const T_Value& b) {
// index number is meaninless with sort, as it changes
return with_func(0, a) < with_func(0, b);
});
}
void reverse() { std::reverse(m_deque.begin(), m_deque.end()); }
void shuffle() { std::shuffle(m_deque.begin(), m_deque.end(), VlURNG()); }
VlQueue unique() const {
VlQueue out;
std::unordered_set<T_Value> saw;
for (const auto& i : m_deque) {
auto it = saw.find(i);
if (it == saw.end()) {
saw.insert(it, i);
out.push_back(i);
}
}
return out;
}
VlQueue<IData> unique_index() const {
VlQueue<IData> out;
IData index = 0;
std::unordered_set<T_Value> saw;
for (const auto& i : m_deque) {
auto it = saw.find(i);
if (it == saw.end()) {
saw.insert(it, i);
out.push_back(index);
}
++index;
}
return out;
}
template <typename Func> VlQueue find(Func with_func) const {
VlQueue out;
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) out.push_back(i);
++index;
}
return out;
}
template <typename Func> VlQueue<IData> find_index(Func with_func) const {
VlQueue<IData> out;
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) out.push_back(index);
++index;
}
return out;
}
template <typename Func> VlQueue find_first(Func with_func) const {
// Can't use std::find_if as need index number
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) return VlQueue::cons(i);
++index;
}
return VlQueue{};
}
template <typename Func> VlQueue<IData> find_first_index(Func with_func) const {
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) return VlQueue<IData>::cons(index);
++index;
}
return VlQueue<IData>{};
}
template <typename Func> VlQueue find_last(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
if (with_func(index, *it)) return VlQueue::cons(*it);
--index;
}
return VlQueue{};
}
template <typename Func> VlQueue<IData> find_last_index(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
if (with_func(index, *it)) return VlQueue<IData>::cons(index);
--index;
}
return VlQueue<IData>{};
}
// Reduction operators
VlQueue min() const {
if (m_deque.empty()) return VlQueue();
const auto it = std::min_element(m_deque.begin(), m_deque.end());
return VlQueue::cons(*it);
}
VlQueue max() const {
if (m_deque.empty()) return VlQueue();
const auto it = std::max_element(m_deque.begin(), m_deque.end());
return VlQueue::cons(*it);
}
T_Value r_sum() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_deque) out += i;
return out;
}
template <typename Func> T_Value r_sum(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out += with_func(index++, i);
return out;
}
T_Value r_product() const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
T_Value out{*it};
++it;
for (; it != m_deque.end(); ++it) out *= *it;
return out;
}
template <typename Func> T_Value r_product(Func with_func) const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
IData index = 0;
T_Value out{with_func(index, *it)};
++it;
++index;
for (; it != m_deque.end(); ++it) out *= with_func(index++, *it);
return out;
}
T_Value r_and() const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
T_Value out{*it};
++it;
for (; it != m_deque.end(); ++it) out &= *it;
return out;
}
template <typename Func> T_Value r_and(Func with_func) const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
IData index = 0;
T_Value out{with_func(index, *it)};
++it;
++index;
for (; it != m_deque.end(); ++it) out &= with_func(index, *it);
return out;
}
T_Value r_or() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_deque) out |= i;
return out;
}
template <typename Func> T_Value r_or(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out |= with_func(index++, i);
return out;
}
T_Value r_xor() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_deque) out ^= i;
return out;
}
template <typename Func> T_Value r_xor(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out ^= with_func(index++, i);
return out;
}
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
if (m_deque.empty()) return "'{}"; // No trailing space
std::string out = "'{";
std::string comma;
for (const auto& i : m_deque) {
out += comma + VL_TO_STRING(i);
comma = ", ";
}
return out + "} ";
}
};
template <class T_Value> std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
return obj.to_string();
}
//===================================================================
// Verilog associative array container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
//
template <class T_Key, class T_Value> class VlAssocArray final {
private:
// TYPES
using Map = std::map<T_Key, T_Value>;
public:
using const_iterator = typename Map::const_iterator;
private:
// MEMBERS
Map m_map; // State of the assoc array
T_Value m_defaultValue; // Default value
public:
// CONSTRUCTORS
// m_defaultValue isn't defaulted. Caller's constructor must do it.
VlAssocArray() = default;
~VlAssocArray() = default;
VlAssocArray(const VlAssocArray&) = default;
VlAssocArray(VlAssocArray&&) = default;
VlAssocArray& operator=(const VlAssocArray&) = default;
VlAssocArray& operator=(VlAssocArray&&) = default;
// METHODS
T_Value& atDefault() { return m_defaultValue; }
const T_Value& atDefault() const { return m_defaultValue; }
// Size of array. Verilog: function int size(), or int num()
int size() const { return m_map.size(); }
// Clear array. Verilog: function void delete([input index])
void clear() { m_map.clear(); }
void erase(const T_Key& index) { m_map.erase(index); }
// Return 0/1 if element exists. Verilog: function int exists(input index)
int exists(const T_Key& index) const { return m_map.find(index) != m_map.end(); }
// Return first element. Verilog: function int first(ref index);
int first(T_Key& indexr) const {
const auto it = m_map.cbegin();
if (it == m_map.end()) return 0;
indexr = it->first;
return 1;
}
// Return last element. Verilog: function int last(ref index)
int last(T_Key& indexr) const {
const auto it = m_map.crbegin();
if (it == m_map.rend()) return 0;
indexr = it->first;
return 1;
}
// Return next element. Verilog: function int next(ref index)
int next(T_Key& indexr) const {
auto it = m_map.find(indexr);
if (VL_UNLIKELY(it == m_map.end())) return 0;
++it;
if (VL_UNLIKELY(it == m_map.end())) return 0;
indexr = it->first;
return 1;
}
// Return prev element. Verilog: function int prev(ref index)
int prev(T_Key& indexr) const {
auto it = m_map.find(indexr);
if (VL_UNLIKELY(it == m_map.end())) return 0;
if (VL_UNLIKELY(it == m_map.begin())) return 0;
--it;
indexr = it->first;
return 1;
}
// Setting. Verilog: assoc[index] = v
// Can't just overload operator[] or provide a "at" reference to set,
// because we need to be able to insert only when the value is set
T_Value& at(const T_Key& index) {
const auto it = m_map.find(index);
if (it == m_map.end()) {
std::pair<typename Map::iterator, bool> pit = m_map.emplace(index, m_defaultValue);
return pit.first->second;
}
return it->second;
}
// Accessing. Verilog: v = assoc[index]
const T_Value& at(const T_Key& index) const {
const auto it = m_map.find(index);
if (it == m_map.end()) {
return m_defaultValue;
} else {
return it->second;
}
}
// Setting as a chained operation
VlAssocArray& set(const T_Key& index, const T_Value& value) {
at(index) = value;
return *this;
}
VlAssocArray& setDefault(const T_Value& value) {
atDefault() = value;
return *this;
}
// For save/restore
const_iterator begin() const { return m_map.begin(); }
const_iterator end() const { return m_map.end(); }
// Methods
VlQueue<T_Value> unique() const {
VlQueue<T_Value> out;
std::set<T_Value> saw;
for (const auto& i : m_map) {
auto it = saw.find(i.second);
if (it == saw.end()) {
saw.insert(it, i.second);
out.push_back(i.second);
}
}
return out;
}
VlQueue<T_Key> unique_index() const {
VlQueue<T_Key> out;
std::set<T_Key> saw;
for (const auto& i : m_map) {
auto it = saw.find(i.second);
if (it == saw.end()) {
saw.insert(it, i.second);
out.push_back(i.first);
}
}
return out;
}
template <typename Func> VlQueue<T_Value> find(Func with_func) const {
VlQueue<T_Value> out;
for (const auto& i : m_map)
if (with_func(i.first, i.second)) out.push_back(i.second);
return out;
}
template <typename Func> VlQueue<T_Key> find_index(Func with_func) const {
VlQueue<T_Key> out;
for (const auto& i : m_map)
if (with_func(i.first, i.second)) out.push_back(i.first);
return out;
}
template <typename Func> VlQueue<T_Value> find_first(Func with_func) const {
const auto it
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.end()) return VlQueue<T_Value>{};
return VlQueue<T_Value>::cons(it->second);
}
template <typename Func> VlQueue<T_Key> find_first_index(Func with_func) const {
const auto it
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.end()) return VlQueue<T_Value>{};
return VlQueue<T_Key>::cons(it->first);
}
template <typename Func> VlQueue<T_Value> find_last(Func with_func) const {
const auto it
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.rend()) return VlQueue<T_Value>{};
return VlQueue<T_Value>::cons(it->second);
}
template <typename Func> VlQueue<T_Key> find_last_index(Func with_func) const {
const auto it
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.rend()) return VlQueue<T_Value>{};
return VlQueue<T_Key>::cons(it->first);
}
// Reduction operators
VlQueue<T_Value> min() const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::min_element(
m_map.begin(), m_map.end(),
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return a.second < b.second;
});
return VlQueue<T_Value>::cons(it->second);
}
VlQueue<T_Value> max() const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::max_element(
m_map.begin(), m_map.end(),
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return a.second < b.second;
});
return VlQueue<T_Value>::cons(it->second);
}
T_Value r_sum() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out += i.second;
return out;
}
template <typename Func> T_Value r_sum(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out += with_func(i.first, i.second);
return out;
}
T_Value r_product() const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{it->second};
++it;
for (; it != m_map.end(); ++it) out *= it->second;
return out;
}
template <typename Func> T_Value r_product(Func with_func) const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{with_func(it->first, it->second)};
++it;
for (; it != m_map.end(); ++it) out *= with_func(it->first, it->second);
return out;
}
T_Value r_and() const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{it->second};
++it;
for (; it != m_map.end(); ++it) out &= it->second;
return out;
}
template <typename Func> T_Value r_and(Func with_func) const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{with_func(it->first, it->second)};
++it;
for (; it != m_map.end(); ++it) out &= with_func(it->first, it->second);
return out;
}
T_Value r_or() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out |= i.second;
return out;
}
template <typename Func> T_Value r_or(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out |= with_func(i.first, i.second);
return out;
}
T_Value r_xor() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out ^= i.second;
return out;
}
template <typename Func> T_Value r_xor(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out ^= with_func(i.first, i.second);
return out;
}
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
if (m_map.empty()) return "'{}"; // No trailing space
std::string out = "'{";
std::string comma;
for (const auto& i : m_map) {
out += comma + VL_TO_STRING(i.first) + ":" + VL_TO_STRING(i.second);
comma = ", ";
}
// Default not printed - maybe random init data
return out + "} ";
}
};
template <class T_Key, class T_Value>
std::string VL_TO_STRING(const VlAssocArray<T_Key, T_Value>& obj) {
return obj.to_string();
}
template <class T_Key, class T_Value>
void VL_READMEM_N(bool hex, int bits, const std::string& filename,
VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
VlReadMem rmem(hex, bits, filename, start, end);
if (VL_UNLIKELY(!rmem.isOpen())) return;
while (true) {
QData addr;
std::string data;
if (rmem.get(addr /*ref*/, data /*ref*/)) {
rmem.setData(&(obj.at(addr)), data);
} else {
break;
}
}
}
template <class T_Key, class T_Value>
void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
const VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
VlWriteMem wmem(hex, bits, filename, start, end);
if (VL_UNLIKELY(!wmem.isOpen())) return;
for (const auto& i : obj) {
QData addr = i.first;
if (addr >= start && addr <= end) wmem.print(addr, true, &(i.second));
}
}
//===================================================================
/// Verilog packed array container
/// For when a standard C++[] array is not sufficient, e.g. an
/// array under a queue, or methods operating on the array.
///
/// This class may get exposed to a Verilated Model's top I/O, if the top
/// IO has an unpacked array.
template <class T_Value, std::size_t T_Depth> class VlUnpacked final {
private:
// TYPES
using Array = std::array<T_Value, T_Depth>;
public:
using const_iterator = typename Array::const_iterator;
private:
// MEMBERS
Array m_array; // Contents of the packed array
public:
// CONSTRUCTORS
VlUnpacked() = default;
~VlUnpacked() = default;
VlUnpacked(const VlUnpacked&) = default;
VlUnpacked(VlUnpacked&&) = default;
VlUnpacked& operator=(const VlUnpacked&) = default;
VlUnpacked& operator=(VlUnpacked&&) = default;
// METHODS
// Raw access
WData* data() { return &m_array[0]; }
const WData* data() const { return &m_array[0]; }
T_Value& operator[](size_t index) { return m_array[index]; };
const T_Value& operator[](size_t index) const { return m_array[index]; };
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
std::string out = "'{";
std::string comma;
for (int i = 0; i < T_Depth; ++i) {
out += comma + VL_TO_STRING(m_array[i]);
comma = ", ";
}
return out + "} ";
}
};
template <class T_Value, std::size_t T_Depth>
std::string VL_TO_STRING(const VlUnpacked<T_Value, T_Depth>& obj) {
return obj.to_string();
}
//===================================================================
// Verilog class reference container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
#define VlClassRef std::shared_ptr
template <class T> // T typically of type VlClassRef<x>
inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
if (VL_UNLIKELY(!t)) Verilated::nullPointerError(filename, linenum);
return t;
}
template <typename T, typename U>
static inline bool VL_CAST_DYNAMIC(VlClassRef<T> in, VlClassRef<U>& outr) {
VlClassRef<U> casted = std::dynamic_pointer_cast<U>(in);
if (VL_LIKELY(casted)) {
outr = casted;
return true;
} else {
return false;
}
}
//======================================================================
// Conversion functions
extern std::string VL_CVT_PACK_STR_NW(int lwords, WDataInP lwp) VL_MT_SAFE;
inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE {
WData lw[VL_WQ_WORDS_E];
VL_SET_WQ(lw, lhs);
return VL_CVT_PACK_STR_NW(VL_WQ_WORDS_E, lw);
}
inline std::string VL_CVT_PACK_STR_NN(const std::string& lhs) VL_PURE { return lhs; }
inline std::string& VL_CVT_PACK_STR_NN(std::string& lhs) VL_PURE { return lhs; }
inline std::string VL_CVT_PACK_STR_NI(IData lhs) VL_PURE {
WData lw[VL_WQ_WORDS_E];
VL_SET_WI(lw, lhs);
return VL_CVT_PACK_STR_NW(1, lw);
}
inline std::string VL_CONCATN_NNN(const std::string& lhs, const std::string& rhs) VL_PURE {
return lhs + rhs;
}
inline std::string VL_REPLICATEN_NNQ(int, int, int, const std::string& lhs, IData rep) VL_PURE {
std::string out;
out.reserve(lhs.length() * rep);
for (unsigned times = 0; times < rep; ++times) out += lhs;
return out;
}
inline std::string VL_REPLICATEN_NNI(int obits, int lbits, int rbits, const std::string& lhs,
IData rep) VL_PURE {
return VL_REPLICATEN_NNQ(obits, lbits, rbits, lhs, rep);
}
inline IData VL_LEN_IN(const std::string& ld) { return ld.length(); }
extern std::string VL_TOLOWER_NN(const std::string& ld);
extern std::string VL_TOUPPER_NN(const std::string& ld);
extern IData VL_FERROR_IN(IData fpi, std::string& outputr) VL_MT_SAFE;
extern IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) VL_MT_SAFE;
extern IData VL_FOPEN_MCD_N(const std::string& filename) VL_MT_SAFE;
extern void VL_READMEM_N(bool hex, int bits, QData depth, int array_lsb,
const std::string& filename, void* memp, QData start,
QData end) VL_MT_SAFE;
extern void VL_WRITEMEM_N(bool hex, int bits, QData depth, int array_lsb,
const std::string& filename, const void* memp, QData start,
QData end) VL_MT_SAFE;
extern IData VL_SSCANF_INX(int lbits, const std::string& ld, const char* formatp, ...) VL_MT_SAFE;
extern void VL_SFORMAT_X(int obits_ignored, std::string& output, const char* formatp,
...) VL_MT_SAFE;
extern std::string VL_SFORMATF_NX(const char* formatp, ...) VL_MT_SAFE;
extern void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix, int width,
VerilatedContext* contextp) VL_MT_SAFE;
extern IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE;
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, CData& rdr) VL_MT_SAFE {
WData rwp[2]; // WData must always be at least 2
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
if (got) rdr = rwp[0];
return got;
}
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, SData& rdr) VL_MT_SAFE {
WData rwp[2]; // WData must always be at least 2
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
if (got) rdr = rwp[0];
return got;
}
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, IData& rdr) VL_MT_SAFE {
WData rwp[2];
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
if (got) rdr = rwp[0];
return got;
}
inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, QData& rdr) VL_MT_SAFE {
WData rwp[2];
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
if (got) rdr = VL_SET_QW(rwp);
return got;
}
inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, double& rdr) VL_MT_SAFE {
WData rwp[2];
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
if (got) rdr = VL_CVT_D_Q(VL_SET_QW(rwp));
return got;
}
extern IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_SAFE;
//======================================================================
// Strings
extern std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE;
extern CData VL_GETC_N(const std::string& lhs, IData rhs) VL_PURE;
extern std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_PURE;
inline IData VL_CMP_NN(const std::string& lhs, const std::string& rhs, bool ignoreCase) VL_PURE {
// SystemVerilog does not allow a string variable to contain '\0'.
// So C functions such as strcmp() can correctly compare strings.
if (ignoreCase) {
return VL_STRCASECMP(lhs.c_str(), rhs.c_str());
} else {
return std::strcmp(lhs.c_str(), rhs.c_str());
}
}
extern IData VL_ATOI_N(const std::string& str, int base) VL_PURE;
extern IData VL_FGETS_NI(std::string& dest, IData fpi);
#endif // Guard
+29 -30
View File
@@ -30,7 +30,6 @@
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_heavy.h"
#include "verilated_syms.h"
#include <deque>
@@ -104,7 +103,7 @@ public:
// METHODS
// Add message to queue (called by producer)
void post(const VerilatedMsg& msg) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_queue.insert(msg); // Pass by value to copy the message into queue
++m_depth;
}
@@ -124,7 +123,7 @@ public:
const VerilatedMsg msg = *(it);
m_queue.erase(it);
m_mutex.unlock();
m_depth--; // Ok if outside critical section as only this code checks the value
--m_depth; // Ok if outside critical section as only this code checks the value
{
VL_DEBUG_IF(VL_DBG_MSGF("Executing callback from mtaskId=%d\n", msg.mtaskId()););
msg.run();
@@ -179,7 +178,7 @@ public:
// FILE* list constructed from a file-descriptor
class VerilatedFpList final {
FILE* m_fp[31];
FILE* m_fp[31] = {};
std::size_t m_sz = 0;
public:
@@ -256,11 +255,11 @@ public: // But only for verilated*.cpp
int timeFormatWidth() const VL_MT_SAFE { return m_s.m_timeFormatWidth; }
void timeFormatWidth(int value) VL_MT_SAFE { m_s.m_timeFormatWidth = value; }
std::string timeFormatSuffix() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
const VerilatedLockGuard lock(m_timeDumpMutex);
const VerilatedLockGuard lock{m_timeDumpMutex};
return m_timeFormatSuffix;
}
void timeFormatSuffix(const std::string& value) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
const VerilatedLockGuard lock(m_timeDumpMutex);
const VerilatedLockGuard lock{m_timeDumpMutex};
m_timeFormatSuffix = value;
}
@@ -276,19 +275,19 @@ public: // But only for verilated*.cpp
public: // But only for verilated*.cpp
// METHODS - file IO
IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
if (m_fdFreeMct.empty()) return 0;
IData idx = m_fdFreeMct.back();
const IData idx = m_fdFreeMct.back();
m_fdFreeMct.pop_back();
m_fdps[idx] = std::fopen(filenamep, "w");
if (VL_UNLIKELY(!m_fdps[idx])) return 0;
return (1 << idx);
}
IData fdNew(const char* filenamep, const char* modep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
FILE* fp = std::fopen(filenamep, modep);
FILE* const fp = std::fopen(filenamep, modep);
if (VL_UNLIKELY(!fp)) return 0;
// Bit 31 indicates it's a descriptor not a MCD
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
if (m_fdFree.empty()) {
// Need to create more space in m_fdps and m_fdFree
const std::size_t start = std::max<std::size_t>(31UL + 1UL + 3UL, m_fdps.size());
@@ -300,31 +299,31 @@ public: // But only for verilated*.cpp
m_fdFree[i] = id;
}
}
IData idx = m_fdFree.back();
const IData idx = m_fdFree.back();
m_fdFree.pop_back();
m_fdps[idx] = fp;
return (idx | (1UL << 31)); // bit 31 indicates not MCD
}
void fdFlush(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
for (const auto& i : fdlist) std::fflush(i);
}
IData fdSeek(IData fdi, IData offset, IData origin) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
if (VL_UNLIKELY(fdlist.size() != 1)) return 0;
return static_cast<IData>(
std::fseek(*fdlist.begin(), static_cast<long>(offset), static_cast<int>(origin)));
}
IData fdTell(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
if (VL_UNLIKELY(fdlist.size() != 1)) return 0;
return static_cast<IData>(std::ftell(*fdlist.begin()));
}
void fdWrite(IData fdi, const std::string& output) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
for (const auto& i : fdlist) {
if (VL_UNLIKELY(!i)) continue;
@@ -332,10 +331,10 @@ public: // But only for verilated*.cpp
}
}
void fdClose(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
if (VL_BITISSET_I(fdi, 31)) {
// Non-MCD case
IData idx = VL_MASK_I(31) & fdi;
const IData idx = VL_MASK_I(31) & fdi;
if (VL_UNLIKELY(idx >= m_fdps.size())) return;
if (VL_UNLIKELY(idx <= 2)) return; // stdout/stdin/stderr
if (VL_UNLIKELY(!m_fdps[idx])) return; // Already free
@@ -356,7 +355,7 @@ public: // But only for verilated*.cpp
}
}
inline FILE* fdToFp(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
if (VL_UNLIKELY(fdlist.size() != 1)) return nullptr;
return *fdlist.begin();
@@ -463,7 +462,7 @@ public:
// per map overhead * N scopes would take much more space and cache thrashing.
// As scopep's are pointers, this implicitly handles multiple Context's
static inline void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE {
const VerilatedLockGuard lock(s().m_userMapMutex);
const VerilatedLockGuard lock{s().m_userMapMutex};
const auto it = s().m_userMap.find(std::make_pair(scopep, userKey));
if (it != s().m_userMap.end()) {
it->second = userData;
@@ -472,7 +471,7 @@ public:
}
}
static inline void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
const VerilatedLockGuard lock(s().m_userMapMutex);
const VerilatedLockGuard lock{s().m_userMapMutex};
const auto& it = vlstd::as_const(s().m_userMap).find(std::make_pair(scopep, userKey));
if (VL_UNLIKELY(it == s().m_userMap.end())) return nullptr;
return it->second;
@@ -482,7 +481,7 @@ public: // But only for verilated.cpp
// Symbol table destruction cleans up the entries for each scope.
static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE {
// Slow ok - called once/scope on destruction, so we simply iterate.
const VerilatedLockGuard lock(s().m_userMapMutex);
const VerilatedLockGuard lock{s().m_userMapMutex};
for (auto it = s().m_userMap.begin(); it != s().m_userMap.end();) {
if (it->first.first == scopep) {
s().m_userMap.erase(it++);
@@ -492,7 +491,7 @@ public: // But only for verilated.cpp
}
}
static void userDump() VL_MT_SAFE {
const VerilatedLockGuard lock(s().m_userMapMutex); // Avoid it changing in middle of dump
const VerilatedLockGuard lock{s().m_userMapMutex}; // Avoid it changing in middle of dump
bool first = true;
for (const auto& i : s().m_userMap) {
if (first) {
@@ -508,13 +507,13 @@ public: // But only for verilated*.cpp
// METHODS - hierarchy
static void hierarchyAdd(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE {
// Slow ok - called at construction for VPI accessible elements
const VerilatedLockGuard lock(s().m_hierMapMutex);
const VerilatedLockGuard lock{s().m_hierMapMutex};
s().m_hierMap[fromp].push_back(top);
}
static void hierarchyRemove(const VerilatedScope* fromp,
const VerilatedScope* top) VL_MT_SAFE {
// Slow ok - called at destruction for VPI accessible elements
const VerilatedLockGuard lock(s().m_hierMapMutex);
const VerilatedLockGuard lock{s().m_hierMapMutex};
VerilatedHierarchyMap& map = s().m_hierMap;
if (map.find(fromp) == map.end()) return;
auto& scopes = map[fromp];
@@ -537,7 +536,7 @@ public: // But only for verilated*.cpp
// miss at the cost of a multiply, and all lookups move to slowpath.
static int exportInsert(const char* namep) VL_MT_SAFE {
// Slow ok - called once/function at creation
const VerilatedLockGuard lock(s().m_exportMutex);
const VerilatedLockGuard lock{s().m_exportMutex};
const auto it = s().m_exportMap.find(namep);
if (it == s().m_exportMap.end()) {
s().m_exportMap.emplace(namep, s().m_exportNext++);
@@ -547,24 +546,24 @@ public: // But only for verilated*.cpp
}
}
static int exportFind(const char* namep) VL_MT_SAFE {
const VerilatedLockGuard lock(s().m_exportMutex);
const VerilatedLockGuard lock{s().m_exportMutex};
const auto& it = s().m_exportMap.find(namep);
if (VL_LIKELY(it != s().m_exportMap.end())) return it->second;
std::string msg = (std::string("%Error: Testbench C called ") + namep
+ " but no such DPI export function name exists in ANY model");
const std::string msg = (std::string{"%Error: Testbench C called "} + namep
+ " but no such DPI export function name exists in ANY model");
VL_FATAL_MT("unknown", 0, "", msg.c_str());
return -1;
}
static const char* exportName(int funcnum) VL_MT_SAFE {
// Slowpath; find name for given export; errors only so no map to reverse-map it
const VerilatedLockGuard lock(s().m_exportMutex);
const VerilatedLockGuard lock{s().m_exportMutex};
for (const auto& i : s().m_exportMap) {
if (i.second == funcnum) return i.first;
}
return "*UNKNOWN*";
}
static void exportsDump() VL_MT_SAFE {
const VerilatedLockGuard lock(s().m_exportMutex);
const VerilatedLockGuard lock{s().m_exportMutex};
bool first = true;
for (const auto& i : s().m_exportMap) {
if (first) {
+113
View File
@@ -0,0 +1,113 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//=============================================================================
//
// Code available from: https://verilator.org
//
// Copyright 2012-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//=============================================================================
///
/// \file
/// \brief Verilated general profiling header
///
/// This file is not part of the Verilated public-facing API.
/// It is only for internal use by Verilated library routines.
///
//=============================================================================
#ifndef VERILATOR_VERILATED_PROFILER_H_
#define VERILATOR_VERILATED_PROFILER_H_
#include "verilatedos.h"
#include "verilated.h" // for VerilatedMutex and clang annotations
// 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:
// 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; }
};
// 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
public:
// METHODS
VerilatedProfiler() = default;
~VerilatedProfiler() = 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});
}
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;
}
};
template <std::size_t T_Entries>
void VerilatedProfiler<T_Entries>::write(const char* modelp,
const std::string& filename) VL_MT_SAFE {
static VerilatedMutex s_mutex;
const VerilatedLockGuard lock{s_mutex};
// On the first call we create the file. On later calls we append.
// 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
// 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
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;
// TODO Perhaps merge with verilated_coverage output format, so can
// have a common merging and reporting tool, etc.
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();
fprintf(fp, "profile_data -model \"%s\" -mtask \"%s\" -cost 64'd%" VL_PRI64 "u\n", modelp,
name.c_str(), m_counters[it.counterNumber()]);
}
std::fclose(fp);
}
#endif
+19 -17
View File
@@ -72,9 +72,10 @@ bool VerilatedDeserialize::readDiffers(const void* __restrict datap,
VerilatedDeserialize& VerilatedDeserialize::readAssert(const void* __restrict datap,
size_t size) VL_MT_UNSAFE_ONE {
if (VL_UNLIKELY(readDiffers(datap, size))) {
std::string fn = filename();
std::string msg = "Can't deserialize save-restore file as was made from different model: "
+ filename();
const std::string fn = filename();
const std::string msg
= "Can't deserialize save-restore file as was made from different model: "
+ filename();
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
// Die before we close() as close would infinite loop
}
@@ -90,10 +91,10 @@ void VerilatedSerialize::header() VL_MT_UNSAFE_ONE {
void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE {
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, std::strlen(VLTSAVE_HEADER_STR)))) {
std::string fn = filename();
std::string msg
= std::string(
"Can't deserialize; file has wrong header signature, or file not found: ")
const std::string fn = filename();
const std::string msg
= std::
string{"Can't deserialize; file has wrong header signature, or file not found: "}
+ filename();
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
// Die before we close() as close would infinite loop
@@ -109,9 +110,10 @@ void VerilatedSerialize::trailer() VL_MT_UNSAFE_ONE {
void VerilatedDeserialize::trailer() VL_MT_UNSAFE_ONE {
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_TRAILER_STR, std::strlen(VLTSAVE_TRAILER_STR)))) {
std::string fn = filename();
std::string msg = std::string("Can't deserialize; file has wrong end-of-file signature: ")
+ filename();
const std::string fn = filename();
const std::string msg
= std::string{"Can't deserialize; file has wrong end-of-file signature: "}
+ filename();
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
// Die before we close() as close would infinite loop
}
@@ -190,19 +192,19 @@ void VerilatedRestore::close() VL_MT_UNSAFE_ONE {
void VerilatedSave::flush() VL_MT_UNSAFE_ONE {
m_assertOne.check();
if (VL_UNLIKELY(!isOpen())) return;
vluint8_t* wp = m_bufp;
const vluint8_t* wp = m_bufp;
while (true) {
ssize_t remaining = (m_cp - wp);
const ssize_t remaining = (m_cp - wp);
if (remaining == 0) break;
errno = 0;
ssize_t got = ::write(m_fd, wp, remaining);
const ssize_t got = ::write(m_fd, wp, remaining);
if (got > 0) {
wp += got;
} else if (VL_UNCOVERABLE(got < 0)) {
if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) {
// LCOV_EXCL_START
// write failed, presume error (perhaps out of disk space)
std::string msg = std::string(__FUNCTION__) + ": " + std::strerror(errno);
const std::string msg = std::string{__FUNCTION__} + ": " + std::strerror(errno);
VL_FATAL_MT("", 0, "", msg.c_str());
close();
break;
@@ -223,17 +225,17 @@ void VerilatedRestore::fill() VL_MT_UNSAFE_ONE {
m_cp = m_bufp; // Reset buffer
// Read into buffer starting at m_endp
while (true) {
ssize_t remaining = (m_bufp + bufferSize() - m_endp);
const ssize_t remaining = (m_bufp + bufferSize() - m_endp);
if (remaining == 0) break;
errno = 0;
ssize_t got = ::read(m_fd, m_endp, remaining);
const ssize_t got = ::read(m_fd, m_endp, remaining);
if (got > 0) {
m_endp += got;
} else if (VL_UNCOVERABLE(got < 0)) {
if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) {
// LCOV_EXCL_START
// write failed, presume error (perhaps out of disk space)
std::string msg = std::string(__FUNCTION__) + ": " + std::strerror(errno);
const std::string msg = std::string{__FUNCTION__} + ": " + std::strerror(errno);
VL_FATAL_MT("", 0, "", msg.c_str());
close();
break;
+12 -12
View File
@@ -23,7 +23,7 @@
#define VERILATOR_VERILATED_SAVE_C_H_
#include "verilatedos.h"
#include "verilated_heavy.h"
#include "verilated.h"
#include <string>
@@ -236,50 +236,50 @@ public:
//=============================================================================
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, vluint64_t& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint64_t& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint64_t& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, vluint32_t& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint32_t& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint32_t& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, vluint16_t& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint16_t& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint16_t& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, vluint8_t& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint8_t& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint8_t& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, bool& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const bool& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, bool& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, double& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const double& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, double& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, float& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const float& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, float& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const std::string& rhs) {
vluint32_t len = rhs.length();
const vluint32_t len = rhs.length();
os << len;
return os.write(rhs.data(), len);
}
@@ -295,11 +295,11 @@ VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhs
template <class T_Key, class T_Value>
VerilatedSerialize& operator<<(VerilatedSerialize& os, VlAssocArray<T_Key, T_Value>& rhs) {
os << rhs.atDefault();
vluint32_t len = rhs.size();
const vluint32_t len = rhs.size();
os << len;
for (const auto& i : rhs) {
T_Key index = i.first; // Copy to get around const_iterator
T_Value value = i.second;
const T_Key index = i.first; // Copy to get around const_iterator
const T_Value value = i.second;
os << index << value;
}
return os;
+23 -19
View File
@@ -152,30 +152,34 @@ public:
const VerilatedRange& unpacked() const { return m_unpacked[0]; }
// DPI accessors
int left(int dim) const {
return dim == 0 ? m_packed.left()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].left() : 0;
return dim == 0 ? m_packed.left()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].left()
: 0;
}
int right(int dim) const {
return dim == 0 ? m_packed.right()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].right() : 0;
return dim == 0 ? m_packed.right()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].right()
: 0;
}
int low(int dim) const {
return dim == 0 ? m_packed.low()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].low() : 0;
return dim == 0 ? m_packed.low()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].low()
: 0;
}
int high(int dim) const {
return dim == 0 ? m_packed.high()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].high() : 0;
return dim == 0 ? m_packed.high()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].high()
: 0;
}
int increment(int dim) const {
return dim == 0
? m_packed.increment()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].increment() : 0;
return dim == 0 ? m_packed.increment()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].increment()
: 0;
}
int elements(int dim) const {
return dim == 0
? m_packed.elements()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].elements() : 0;
return dim == 0 ? m_packed.elements()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].elements()
: 0;
}
// Total size in bytes (note DPI limited to 4GB)
size_t totalSize() const;
@@ -188,8 +192,8 @@ public:
class VerilatedDpiOpenVar final {
// MEMBERS
const VerilatedVarProps* m_propsp; // Variable properties
void* m_datap; // Location of data (local to thread always, so safe)
const VerilatedVarProps* const m_propsp; // Variable properties
void* const m_datap; // Location of data (local to thread always, so safe)
public:
// CONSTRUCTORS
VerilatedDpiOpenVar(const VerilatedVarProps* propsp, void* datap)
@@ -226,10 +230,10 @@ public:
class VerilatedVar final : public VerilatedVarProps {
// MEMBERS
void* m_datap; // Location of data
const char* m_namep; // Name - slowpath
void* const m_datap; // Location of data
const char* const m_namep; // Name - slowpath
protected:
bool m_isParam;
const bool m_isParam;
friend class VerilatedScope;
// CONSTRUCTORS
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
+1 -1
View File
@@ -30,7 +30,7 @@
#define VERILATOR_VERILATED_SYMS_H_
#include "verilatedos.h"
#include "verilated_heavy.h"
#include "verilated.h"
#include "verilated_sym_props.h"
#include <map>
+43 -15
View File
@@ -25,6 +25,7 @@
#include "verilated_threads.h"
#include <cstdio>
#include <fstream>
//=============================================================================
// Globals
@@ -48,7 +49,8 @@ VlMTaskVertex::VlMTaskVertex(vluint32_t upstreamDepCount)
// VlWorkerThread
VlWorkerThread::VlWorkerThread(VlThreadPool* poolp, VerilatedContext* contextp, bool profiling)
: m_poolp{poolp}
: m_ready_size{0}
, m_poolp{poolp}
, m_profiling{profiling} // Must init this last -- after setting up fields that it might read:
, m_exiting{false}
, m_cthread{startWorker, this}
@@ -74,7 +76,7 @@ void VlWorkerThread::workerLoop() {
if (VL_UNLIKELY(m_exiting.load(std::memory_order_acquire))) break;
if (VL_LIKELY(work.m_fnp)) {
work.m_fnp(work.m_evenCycle, work.m_sym);
work.m_fnp(work.m_selfp, work.m_evenCycle);
work.m_fnp = nullptr;
}
}
@@ -93,7 +95,7 @@ void VlWorkerThread::startWorker(VlWorkerThread* workerp) {
VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads, bool profiling)
: m_profiling{profiling} {
// --threads N passes nThreads=N-1, as the "main" threads counts as 1
unsigned cpus = std::thread::hardware_concurrency();
const unsigned cpus = std::thread::hardware_concurrency();
if (cpus < nThreads + 1) {
static int warnedOnce = 0;
if (!warnedOnce++) {
@@ -104,7 +106,7 @@ VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads, bool profil
}
// Create'em
for (int i = 0; i < nThreads; ++i) {
m_workers.push_back(new VlWorkerThread(this, contextp, profiling));
m_workers.push_back(new VlWorkerThread{this, contextp, profiling});
}
// Set up a profile buffer for the current thread too -- on the
// assumption that it's the same thread that calls eval and may be
@@ -131,25 +133,25 @@ void VlThreadPool::setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex) {
// try not to malloc while collecting profiling.
t_profilep->reserve(4096);
{
const VerilatedLockGuard lk(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_allProfiles.insert(t_profilep);
}
}
void VlThreadPool::profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lk(m_mutex);
const VerilatedLockGuard lock{m_mutex};
for (const auto& profilep : m_allProfiles) {
// Every thread's profile trace gets a copy of rec.
profilep->emplace_back(rec);
}
}
void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
void VlThreadPool::profileDump(const char* filenamep, vluint64_t tickStart, vluint64_t tickEnd)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lk(m_mutex);
const VerilatedLockGuard lock{m_mutex};
VL_DEBUG_IF(VL_DBG_MSGF("+prof+threads writing to '%s'\n", filenamep););
FILE* fp = std::fopen(filenamep, "w");
FILE* const fp = std::fopen(filenamep, "w");
if (VL_UNLIKELY(!fp)) {
VL_FATAL_MT(filenamep, 0, "", "+prof+threads+file file not writable");
// cppcheck-suppress resourceLeak // bug, doesn't realize fp is nullptr
@@ -158,7 +160,7 @@ void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
// TODO Perhaps merge with verilated_coverage output format, so can
// have a common merging and reporting tool, etc.
fprintf(fp, "VLPROFTHREAD 1.0 # Verilator thread profile dump version 1.0\n");
fprintf(fp, "VLPROFTHREAD 1.1 # Verilator thread profile dump version 1.1\n");
fprintf(fp, "VLPROF arg --threads %" VL_PRI64 "u\n", vluint64_t(m_workers.size() + 1));
fprintf(fp, "VLPROF arg +verilator+prof+threads+start+%" VL_PRI64 "u\n",
Verilated::threadContextp()->profThreadsStart());
@@ -166,6 +168,16 @@ void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
Verilated::threadContextp()->profThreadsWindow());
fprintf(fp, "VLPROF stat yields %" VL_PRI64 "u\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;
@@ -176,20 +188,36 @@ void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
case VlProfileRec::TYPE_BARRIER: //
printing = true;
break;
case VlProfileRec::TYPE_EVAL:
if (!printing) break;
fprintf(fp,
"VLPROF eval start %" VL_PRI64 "u elapsed %" VL_PRI64 "u"
" 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 %" VL_PRI64 "u elapsed %" VL_PRI64 "u"
" 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 %" VL_PRI64 "u end %" VL_PRI64 "u elapsed %" VL_PRI64 "u"
" predict_time %u cpu %u on thread %u\n",
ei.m_mtaskId, ei.m_startTime, ei.m_endTime,
(ei.m_endTime - ei.m_startTime), ei.m_predictTime, ei.m_cpu, thread_id);
" start %" VL_PRI64 "u elapsed %" VL_PRI64 "u"
" 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 %" VL_PRI64 "u\n", ticksElapsed);
fprintf(fp, "VLPROF stat ticks %" VL_PRI64 "u\n", tickEnd - tickStart);
std::fclose(fp);
}
+44 -28
View File
@@ -48,12 +48,12 @@
#endif
// clang-format on
// VlMTaskVertex and VlThreadpool will work with multiple symbol table types.
// VlMTaskVertex and VlThreadpool will work with multiple model class types.
// Since the type is opaque to VlMTaskVertex and VlThreadPool, represent it
// as a void* here.
using VlThrSymTab = void*;
using VlSelfP = void*;
using VlExecFnp = void (*)(bool, VlThrSymTab);
using VlExecFnp = void (*)(VlSelfP, bool);
// Track dependencies for a single MTask.
class VlMTaskVertex final {
@@ -99,19 +99,19 @@ public:
// false while it's still waiting on more dependencies.
inline bool signalUpstreamDone(bool evenCycle) {
if (evenCycle) {
vluint32_t upstreamDepsDone
const vluint32_t upstreamDepsDone
= 1 + m_upstreamDepsDone.fetch_add(1, std::memory_order_release);
assert(upstreamDepsDone <= m_upstreamDepCount);
return (upstreamDepsDone == m_upstreamDepCount);
} else {
vluint32_t upstreamDepsDone_prev
const vluint32_t upstreamDepsDone_prev
= m_upstreamDepsDone.fetch_sub(1, std::memory_order_release);
assert(upstreamDepsDone_prev > 0);
return (upstreamDepsDone_prev == 1);
}
}
inline bool areUpstreamDepsDone(bool evenCycle) const {
vluint32_t target = evenCycle ? m_upstreamDepCount : 0;
const vluint32_t target = evenCycle ? m_upstreamDepCount : 0;
return m_upstreamDepsDone.load(std::memory_order_acquire) == target;
}
inline void waitUntilUpstreamDone(bool evenCycle) const {
@@ -131,21 +131,36 @@ public:
class VlProfileRec final {
protected:
friend class VlThreadPool;
enum VlProfileE { TYPE_MTASK_RUN, TYPE_BARRIER };
VlProfileE m_type = TYPE_BARRIER; // Record type
vluint32_t m_mtaskId = 0; // Mtask we're logging
vluint32_t m_predictTime = 0; // How long scheduler predicted would take
vluint64_t m_startTime = 0; // Tick at start of execution
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 startRecord(vluint64_t time, uint32_t mtask, uint32_t predict) {
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_predictTime = predict;
m_predictStart = predictStart;
m_predictCost = predictCost;
m_startTime = time;
m_cpu = getcpu();
}
@@ -177,15 +192,15 @@ private:
// TYPES
struct ExecRec {
VlExecFnp m_fnp; // Function to execute
VlThrSymTab m_sym; // Symbol table to execute
VlSelfP m_selfp; // Symbol table to execute
bool m_evenCycle; // Even/odd for flag alternation
ExecRec()
: m_fnp{nullptr}
, m_sym{nullptr}
, m_selfp{nullptr}
, m_evenCycle{false} {}
ExecRec(VlExecFnp fnp, bool evenCycle, VlThrSymTab sym)
ExecRec(VlExecFnp fnp, VlSelfP selfp, bool evenCycle)
: m_fnp{fnp}
, m_sym{sym}
, m_selfp{selfp}
, m_evenCycle{evenCycle} {}
};
@@ -202,12 +217,12 @@ private:
// Store the size atomically, so we can spin wait
std::atomic<size_t> m_ready_size;
VlThreadPool* m_poolp; // Our associated thread pool
VlThreadPool* const m_poolp; // Our associated thread pool
bool m_profiling; // Is profiling enabled?
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* m_contextp; // Context for spawned thread
VerilatedContext* const m_contextp; // Context for spawned thread
VL_UNCOPYABLE(VlWorkerThread);
@@ -225,10 +240,10 @@ public:
}
VL_CPU_RELAX();
}
VerilatedLockGuard lk(m_mutex);
VerilatedLockGuard lock{m_mutex};
while (m_ready.empty()) {
m_waiting = true;
m_cv.wait(lk);
m_cv.wait(lock);
}
m_waiting = false;
// As noted above this is inefficient if our ready list is ever
@@ -237,13 +252,13 @@ public:
m_ready.erase(m_ready.begin());
m_ready_size.fetch_sub(1, std::memory_order_relaxed);
}
inline void wakeUp() { addTask(nullptr, false, nullptr); }
inline void addTask(VlExecFnp fnp, bool evenCycle, VlThrSymTab sym)
inline void wakeUp() { addTask(nullptr, nullptr, false); }
inline void addTask(VlExecFnp fnp, VlSelfP selfp, bool evenCycle)
VL_MT_SAFE_EXCLUDES(m_mutex) {
bool notify;
{
const VerilatedLockGuard lk(m_mutex);
m_ready.emplace_back(fnp, evenCycle, sym);
const VerilatedLockGuard lock{m_mutex};
m_ready.emplace_back(fnp, selfp, evenCycle);
m_ready_size.fetch_add(1, std::memory_order_relaxed);
notify = m_waiting;
}
@@ -259,7 +274,7 @@ class VlThreadPool final {
// MEMBERS
std::vector<VlWorkerThread*> m_workers; // our workers
bool m_profiling; // is profiling enabled?
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
@@ -292,7 +307,8 @@ public:
return &(t_profilep->back());
}
void profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex);
void profileDump(const char* filenamep, vluint64_t ticksElapsed) 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);
+4 -4
View File
@@ -52,21 +52,21 @@ private:
public:
// Put an element at the back of the queue
void put(T value) VL_MT_SAFE_EXCLUDES(m_mutex) {
VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_queue.push_back(value);
m_cv.notify_one();
}
// Put an element at the front of the queue
void put_front(T value) VL_MT_SAFE_EXCLUDES(m_mutex) {
VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_queue.push_front(value);
m_cv.notify_one();
}
// Get an element from the front of the queue. Blocks if none available
T get() VL_MT_SAFE_EXCLUDES(m_mutex) {
VerilatedLockGuard lock(m_mutex);
VerilatedLockGuard lock{m_mutex};
m_cv.wait(lock, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); });
assert(!m_queue.empty());
T value = m_queue.front();
@@ -76,7 +76,7 @@ public:
// Non blocking get
bool tryGet(T& result) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lockGuard(m_mutex);
const VerilatedLockGuard lockGuard{m_mutex};
if (m_queue.empty()) return false;
result = m_queue.front();
m_queue.pop_front();
+11 -11
View File
@@ -246,7 +246,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::closeBase() {
shutdownWorker();
while (m_numTraceBuffers) {
delete[] m_buffersFromWorker.get();
m_numTraceBuffers--;
--m_numTraceBuffers;
}
#endif
}
@@ -267,7 +267,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::flushBase() {
// Callbacks to run on global events
template <> void VerilatedTrace<VL_DERIVED_T>::onFlush(void* selfp) {
// This calls 'flush' on the derived classo (which must then get any mutex)
// This calls 'flush' on the derived class (which must then get any mutex)
reinterpret_cast<VL_DERIVED_T*>(selfp)->flush();
}
@@ -351,7 +351,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
m_traceBufferSize = nextCode() + numSignals() * 2 + 4;
// Start the worker thread
m_workerThread.reset(new std::thread(&VerilatedTrace<VL_DERIVED_T>::workerThreadMain, this));
m_workerThread.reset(new std::thread{&VerilatedTrace<VL_DERIVED_T>::workerThreadMain, this});
#endif
}
@@ -398,7 +398,7 @@ void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m
// Not really VL_MT_SAFE but more VL_MT_UNSAFE_ONE.
// This does get the mutex, but if multiple threads are trying to dump
// chances are the data being dumped will have other problems
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
if (VL_UNCOVERABLE(m_timeLastDump && timeui <= m_timeLastDump)) { // LCOV_EXCL_START
VL_PRINTF_MT("%%Warning: previous dump at t=%" VL_PRI64 "u, requesting t=%" VL_PRI64
"u, dump call ignored\n",
@@ -479,29 +479,29 @@ template <>
void VerilatedTrace<VL_DERIVED_T>::addCallbackRecord(std::vector<CallbackRecord>& cbVec,
CallbackRecord& cbRec)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
if (VL_UNCOVERABLE(timeLastDump() != 0)) { // LCOV_EXCL_START
std::string msg = (std::string("Internal: ") + __FILE__ + "::" + __FUNCTION__
+ " called with already open file");
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);
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_initCbs, cbr);
}
template <> void VerilatedTrace<VL_DERIVED_T>::addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr(cb, userp);
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_fullCbs, cbr);
}
template <> void VerilatedTrace<VL_DERIVED_T>::addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr(cb, userp);
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_chgCbs, cbr);
}
template <> void VerilatedTrace<VL_DERIVED_T>::addCleanupCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr(cb, userp);
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_cleanupCbs, cbr);
}
template <> void VerilatedTrace<VL_DERIVED_T>::module(const std::string& name) VL_MT_UNSAFE {
+897
View File
@@ -0,0 +1,897 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Code available from: https://verilator.org
//
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
///
/// \file
/// \brief Verilated common data type containers
///
/// verilated.h should be included instead of this file.
///
/// Those macro/function/variable starting or ending in _ are internal,
/// however many of the other function/macros here are also internal.
///
//*************************************************************************
#ifndef VERILATOR_VERILATED_TYPES_H_
#define VERILATOR_VERILATED_TYPES_H_
#ifndef VERILATOR_VERILATED_H_INTERNAL_
#error "verilated_types.h should only be included by verilated.h"
#endif
//===================================================================
// String formatters (required by below containers)
extern std::string VL_TO_STRING(CData lhs);
extern std::string VL_TO_STRING(SData lhs);
extern std::string VL_TO_STRING(IData lhs);
extern std::string VL_TO_STRING(QData lhs);
inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; }
extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
//=========================================================================
// Declare net data types
#define VL_SIG8(name, msb, lsb) CData name ///< Declare signal, 1-8 bits
#define VL_SIG16(name, msb, lsb) SData name ///< Declare signal, 9-16 bits
#define VL_SIG64(name, msb, lsb) QData name ///< Declare signal, 33-64 bits
#define VL_SIG(name, msb, lsb) IData name ///< Declare signal, 17-32 bits
#define VL_SIGW(name, msb, lsb, words) WData name[words] ///< Declare signal, 65+ bits
#define VL_IN8(name, msb, lsb) CData name ///< Declare input signal, 1-8 bits
#define VL_IN16(name, msb, lsb) SData name ///< Declare input signal, 9-16 bits
#define VL_IN64(name, msb, lsb) QData name ///< Declare input signal, 33-64 bits
#define VL_IN(name, msb, lsb) IData name ///< Declare input signal, 17-32 bits
#define VL_INW(name, msb, lsb, words) WData name[words] ///< Declare input signal, 65+ bits
#define VL_INOUT8(name, msb, lsb) CData name ///< Declare bidir signal, 1-8 bits
#define VL_INOUT16(name, msb, lsb) SData name ///< Declare bidir signal, 9-16 bits
#define VL_INOUT64(name, msb, lsb) QData name ///< Declare bidir signal, 33-64 bits
#define VL_INOUT(name, msb, lsb) IData name ///< Declare bidir signal, 17-32 bits
#define VL_INOUTW(name, msb, lsb, words) WData name[words] ///< Declare bidir signal, 65+ bits
#define VL_OUT8(name, msb, lsb) CData name ///< Declare output signal, 1-8 bits
#define VL_OUT16(name, msb, lsb) SData name ///< Declare output signal, 9-16 bits
#define VL_OUT64(name, msb, lsb) QData name ///< Declare output signal, 33-64bits
#define VL_OUT(name, msb, lsb) IData name ///< Declare output signal, 17-32 bits
#define VL_OUTW(name, msb, lsb, words) WData name[words] ///< Declare output signal, 65+ bits
//===================================================================
// Shuffle RNG
extern vluint64_t vl_rand64() VL_MT_SAFE;
class VlURNG final {
public:
using result_type = size_t;
static constexpr size_t min() { return 0; }
static constexpr size_t max() { return 1ULL << 31; }
size_t operator()() { return VL_MASK_I(31) & vl_rand64(); }
};
//===================================================================
// Readmem/Writemem operation classes
class VlReadMem final {
const bool m_hex; // Hex format
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
public:
VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end);
~VlReadMem();
bool isOpen() const { return m_fp != nullptr; }
int linenum() const { return m_linenum; }
bool get(QData& addrr, std::string& valuer);
void setData(void* valuep, const std::string& rhs);
};
class VlWriteMem final {
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
public:
VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end);
~VlWriteMem();
bool isOpen() const { return m_fp != nullptr; }
void print(QData addr, bool addrstamp, const void* valuep);
};
//===================================================================
/// Verilog wide packed bit container.
/// Similar to std::array<WData, N>, but lighter weight, only methods needed
/// by Verilator, to help compile time.
///
/// A 'struct' as we want this to be an aggregate type that allows
/// static aggregate initialization. Consider data members private.
///
/// For example a Verilog "bit [94:0]" will become a VlWide<3> because 3*32
/// bits are needed to hold the 95 bits. The MSB (bit 96) must always be
/// zero in memory, but during intermediate operations in the Verilated
/// internals is unpredictable.
static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE;
template <std::size_t T_Words> struct VlWide final {
// MEMBERS
// This should be the only data member, otherwise generated static initializers need updating
EData m_storage[T_Words]; // Contents of the packed array
// CONSTRUCTORS
// Default constructors and destructor are used. Note however that C++20 requires that
// aggregate types do not have a user declared constructor, not even an explicitly defaulted
// one.
// OPERATOR METHODS
// Default copy assignment operators are used.
operator WDataOutP() { return &m_storage[0]; } // This also allows []
operator WDataInP() const { return &m_storage[0]; } // This also allows []
// METHODS
const EData& at(size_t index) const { return m_storage[index]; }
EData& at(size_t index) { return m_storage[index]; }
WData* data() { return &m_storage[0]; }
const WData* data() const { return &m_storage[0]; }
bool operator<(const VlWide<T_Words>& rhs) const {
return _vl_cmp_w(T_Words, data(), rhs.data()) < 0;
}
};
// Convert a C array to std::array reference by pointer magic, without copy.
// Data type (second argument) is so the function template can automatically generate.
template <std::size_t T_Words>
VlWide<T_Words>& VL_CVT_W_A(const WDataInP inp, const VlWide<T_Words>&) {
return *((VlWide<T_Words>*)inp);
}
template <std::size_t T_Words> std::string VL_TO_STRING(const VlWide<T_Words>& obj) {
return VL_TO_STRING_W(T_Words, obj.data());
}
//===================================================================
// Verilog queue and dynamic array container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
//
// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound
// For dynamic arrays it is always zero
template <class T_Value, size_t T_MaxSize = 0> class VlQueue final {
private:
// TYPES
using Deque = std::deque<T_Value>;
public:
using const_iterator = typename Deque::const_iterator;
private:
// MEMBERS
Deque m_deque; // State of the assoc array
T_Value m_defaultValue; // Default value
public:
// CONSTRUCTORS
// m_defaultValue isn't defaulted. Caller's constructor must do it.
VlQueue() = default;
~VlQueue() = default;
VlQueue(const VlQueue&) = default;
VlQueue(VlQueue&&) = default;
VlQueue& operator=(const VlQueue&) = default;
VlQueue& operator=(VlQueue&&) = default;
// Standard copy constructor works. Verilog: assoca = assocb
// Also must allow conversion from a different T_MaxSize queue
template <size_t U_MaxSize = 0> VlQueue operator=(const VlQueue<T_Value, U_MaxSize>& rhs) {
m_deque = rhs.privateDeque();
if (VL_UNLIKELY(T_MaxSize && T_MaxSize < m_deque.size())) m_deque.resize(T_MaxSize - 1);
return *this;
}
static VlQueue cons(const T_Value& lhs) {
VlQueue out;
out.push_back(lhs);
return out;
}
static VlQueue cons(const T_Value& lhs, const T_Value& rhs) {
VlQueue out;
out.push_back(rhs);
out.push_back(lhs);
return out;
}
static VlQueue cons(const VlQueue& lhs, const T_Value& rhs) {
VlQueue out = lhs;
out.push_front(rhs);
return out;
}
static VlQueue cons(const T_Value& lhs, const VlQueue& rhs) {
VlQueue out = rhs;
out.push_back(lhs);
return out;
}
static VlQueue cons(const VlQueue& lhs, const VlQueue& rhs) {
VlQueue out = rhs;
for (const auto& i : lhs.m_deque) out.push_back(i);
return out;
}
// METHODS
T_Value& atDefault() { return m_defaultValue; }
const T_Value& atDefault() const { return m_defaultValue; }
const Deque& privateDeque() const { return m_deque; }
// Size. Verilog: function int size(), or int num()
int size() const { return m_deque.size(); }
// Clear array. Verilog: function void delete([input index])
void clear() { m_deque.clear(); }
void erase(vlsint32_t index) {
if (VL_LIKELY(index >= 0 && index < m_deque.size()))
m_deque.erase(m_deque.begin() + index);
}
// Dynamic array new[] becomes a renew()
void renew(size_t size) {
clear();
m_deque.resize(size, atDefault());
}
// Dynamic array new[]() becomes a renew_copy()
void renew_copy(size_t size, const VlQueue<T_Value, T_MaxSize>& rhs) {
if (size == 0) {
clear();
} else {
*this = rhs;
m_deque.resize(size, atDefault());
}
}
// function void q.push_front(value)
void push_front(const T_Value& value) {
m_deque.push_front(value);
if (VL_UNLIKELY(T_MaxSize != 0 && m_deque.size() > T_MaxSize)) m_deque.pop_back();
}
// function void q.push_back(value)
void push_back(const T_Value& value) {
if (VL_LIKELY(T_MaxSize == 0 || m_deque.size() < T_MaxSize)) m_deque.push_back(value);
}
// function value_t q.pop_front();
T_Value pop_front() {
if (m_deque.empty()) return m_defaultValue;
T_Value v = m_deque.front();
m_deque.pop_front();
return v;
}
// function value_t q.pop_back();
T_Value pop_back() {
if (m_deque.empty()) return m_defaultValue;
T_Value v = m_deque.back();
m_deque.pop_back();
return v;
}
// Setting. Verilog: assoc[index] = v
// Can't just overload operator[] or provide a "at" reference to set,
// because we need to be able to insert only when the value is set
T_Value& at(vlsint32_t index) {
static T_Value s_throwAway;
// Needs to work for dynamic arrays, so does not use T_MaxSize
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
s_throwAway = atDefault();
return s_throwAway;
} else {
return m_deque[index];
}
}
// Accessing. Verilog: v = assoc[index]
const T_Value& at(vlsint32_t index) const {
static T_Value s_throwAway;
// Needs to work for dynamic arrays, so does not use T_MaxSize
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
return atDefault();
} else {
return m_deque[index];
}
}
// function void q.insert(index, value);
void insert(vlsint32_t index, const T_Value& value) {
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return;
m_deque.insert(m_deque.begin() + index, value);
}
// Return slice q[lsb:msb]
VlQueue slice(vlsint32_t lsb, vlsint32_t msb) const {
VlQueue out;
if (VL_UNLIKELY(lsb < 0)) lsb = 0;
if (VL_UNLIKELY(lsb >= m_deque.size())) lsb = m_deque.size() - 1;
if (VL_UNLIKELY(msb >= m_deque.size())) msb = m_deque.size() - 1;
for (vlsint32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]);
return out;
}
// For save/restore
const_iterator begin() const { return m_deque.begin(); }
const_iterator end() const { return m_deque.end(); }
// Methods
void sort() { std::sort(m_deque.begin(), m_deque.end()); }
template <typename Func> void sort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
std::sort(m_deque.begin(), m_deque.end(), [=](const T_Value& a, const T_Value& b) {
// index number is meaninless with sort, as it changes
return with_func(0, a) < with_func(0, b);
});
}
void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); }
template <typename Func> void rsort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
std::sort(m_deque.rbegin(), m_deque.rend(), [=](const T_Value& a, const T_Value& b) {
// index number is meaninless with sort, as it changes
return with_func(0, a) < with_func(0, b);
});
}
void reverse() { std::reverse(m_deque.begin(), m_deque.end()); }
void shuffle() { std::shuffle(m_deque.begin(), m_deque.end(), VlURNG{}); }
VlQueue unique() const {
VlQueue out;
std::unordered_set<T_Value> saw;
for (const auto& i : m_deque) {
auto it = saw.find(i);
if (it == saw.end()) {
saw.insert(it, i);
out.push_back(i);
}
}
return out;
}
VlQueue<IData> unique_index() const {
VlQueue<IData> out;
IData index = 0;
std::unordered_set<T_Value> saw;
for (const auto& i : m_deque) {
auto it = saw.find(i);
if (it == saw.end()) {
saw.insert(it, i);
out.push_back(index);
}
++index;
}
return out;
}
template <typename Func> VlQueue find(Func with_func) const {
VlQueue out;
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) out.push_back(i);
++index;
}
return out;
}
template <typename Func> VlQueue<IData> find_index(Func with_func) const {
VlQueue<IData> out;
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) out.push_back(index);
++index;
}
return out;
}
template <typename Func> VlQueue find_first(Func with_func) const {
// Can't use std::find_if as need index number
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) return VlQueue::cons(i);
++index;
}
return VlQueue{};
}
template <typename Func> VlQueue<IData> find_first_index(Func with_func) const {
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) return VlQueue<IData>::cons(index);
++index;
}
return VlQueue<IData>{};
}
template <typename Func> VlQueue find_last(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
if (with_func(index, *it)) return VlQueue::cons(*it);
--index;
}
return VlQueue{};
}
template <typename Func> VlQueue<IData> find_last_index(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
if (with_func(index, *it)) return VlQueue<IData>::cons(index);
--index;
}
return VlQueue<IData>{};
}
// Reduction operators
VlQueue min() const {
if (m_deque.empty()) return VlQueue{};
const auto it = std::min_element(m_deque.begin(), m_deque.end());
return VlQueue::cons(*it);
}
VlQueue max() const {
if (m_deque.empty()) return VlQueue{};
const auto it = std::max_element(m_deque.begin(), m_deque.end());
return VlQueue::cons(*it);
}
T_Value r_sum() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_deque) out += i;
return out;
}
template <typename Func> T_Value r_sum(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out += with_func(index++, i);
return out;
}
T_Value r_product() const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
T_Value out{*it};
++it;
for (; it != m_deque.end(); ++it) out *= *it;
return out;
}
template <typename Func> T_Value r_product(Func with_func) const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
IData index = 0;
T_Value out{with_func(index, *it)};
++it;
++index;
for (; it != m_deque.end(); ++it) out *= with_func(index++, *it);
return out;
}
T_Value r_and() const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
T_Value out{*it};
++it;
for (; it != m_deque.end(); ++it) out &= *it;
return out;
}
template <typename Func> T_Value r_and(Func with_func) const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
IData index = 0;
T_Value out{with_func(index, *it)};
++it;
++index;
for (; it != m_deque.end(); ++it) out &= with_func(index, *it);
return out;
}
T_Value r_or() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_deque) out |= i;
return out;
}
template <typename Func> T_Value r_or(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out |= with_func(index++, i);
return out;
}
T_Value r_xor() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_deque) out ^= i;
return out;
}
template <typename Func> T_Value r_xor(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out ^= with_func(index++, i);
return out;
}
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
if (m_deque.empty()) return "'{}"; // No trailing space
std::string out = "'{";
std::string comma;
for (const auto& i : m_deque) {
out += comma + VL_TO_STRING(i);
comma = ", ";
}
return out + "} ";
}
};
template <class T_Value> std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
return obj.to_string();
}
//===================================================================
// Verilog associative array container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
//
template <class T_Key, class T_Value> class VlAssocArray final {
private:
// TYPES
using Map = std::map<T_Key, T_Value>;
public:
using const_iterator = typename Map::const_iterator;
private:
// MEMBERS
Map m_map; // State of the assoc array
T_Value m_defaultValue; // Default value
public:
// CONSTRUCTORS
// m_defaultValue isn't defaulted. Caller's constructor must do it.
VlAssocArray() = default;
~VlAssocArray() = default;
VlAssocArray(const VlAssocArray&) = default;
VlAssocArray(VlAssocArray&&) = default;
VlAssocArray& operator=(const VlAssocArray&) = default;
VlAssocArray& operator=(VlAssocArray&&) = default;
// METHODS
T_Value& atDefault() { return m_defaultValue; }
const T_Value& atDefault() const { return m_defaultValue; }
// Size of array. Verilog: function int size(), or int num()
int size() const { return m_map.size(); }
// Clear array. Verilog: function void delete([input index])
void clear() { m_map.clear(); }
void erase(const T_Key& index) { m_map.erase(index); }
// Return 0/1 if element exists. Verilog: function int exists(input index)
int exists(const T_Key& index) const { return m_map.find(index) != m_map.end(); }
// Return first element. Verilog: function int first(ref index);
int first(T_Key& indexr) const {
const auto it = m_map.cbegin();
if (it == m_map.end()) return 0;
indexr = it->first;
return 1;
}
// Return last element. Verilog: function int last(ref index)
int last(T_Key& indexr) const {
const auto it = m_map.crbegin();
if (it == m_map.rend()) return 0;
indexr = it->first;
return 1;
}
// Return next element. Verilog: function int next(ref index)
int next(T_Key& indexr) const {
auto it = m_map.find(indexr);
if (VL_UNLIKELY(it == m_map.end())) return 0;
++it;
if (VL_UNLIKELY(it == m_map.end())) return 0;
indexr = it->first;
return 1;
}
// Return prev element. Verilog: function int prev(ref index)
int prev(T_Key& indexr) const {
auto it = m_map.find(indexr);
if (VL_UNLIKELY(it == m_map.end())) return 0;
if (VL_UNLIKELY(it == m_map.begin())) return 0;
--it;
indexr = it->first;
return 1;
}
// Setting. Verilog: assoc[index] = v
// Can't just overload operator[] or provide a "at" reference to set,
// because we need to be able to insert only when the value is set
T_Value& at(const T_Key& index) {
const auto it = m_map.find(index);
if (it == m_map.end()) {
std::pair<typename Map::iterator, bool> pit = m_map.emplace(index, m_defaultValue);
return pit.first->second;
}
return it->second;
}
// Accessing. Verilog: v = assoc[index]
const T_Value& at(const T_Key& index) const {
const auto it = m_map.find(index);
if (it == m_map.end()) {
return m_defaultValue;
} else {
return it->second;
}
}
// Setting as a chained operation
VlAssocArray& set(const T_Key& index, const T_Value& value) {
at(index) = value;
return *this;
}
VlAssocArray& setDefault(const T_Value& value) {
atDefault() = value;
return *this;
}
// For save/restore
const_iterator begin() const { return m_map.begin(); }
const_iterator end() const { return m_map.end(); }
// Methods
VlQueue<T_Value> unique() const {
VlQueue<T_Value> out;
std::set<T_Value> saw;
for (const auto& i : m_map) {
auto it = saw.find(i.second);
if (it == saw.end()) {
saw.insert(it, i.second);
out.push_back(i.second);
}
}
return out;
}
VlQueue<T_Key> unique_index() const {
VlQueue<T_Key> out;
std::set<T_Key> saw;
for (const auto& i : m_map) {
auto it = saw.find(i.second);
if (it == saw.end()) {
saw.insert(it, i.second);
out.push_back(i.first);
}
}
return out;
}
template <typename Func> VlQueue<T_Value> find(Func with_func) const {
VlQueue<T_Value> out;
for (const auto& i : m_map)
if (with_func(i.first, i.second)) out.push_back(i.second);
return out;
}
template <typename Func> VlQueue<T_Key> find_index(Func with_func) const {
VlQueue<T_Key> out;
for (const auto& i : m_map)
if (with_func(i.first, i.second)) out.push_back(i.first);
return out;
}
template <typename Func> VlQueue<T_Value> find_first(Func with_func) const {
const auto it
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.end()) return VlQueue<T_Value>{};
return VlQueue<T_Value>::cons(it->second);
}
template <typename Func> VlQueue<T_Key> find_first_index(Func with_func) const {
const auto it
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.end()) return VlQueue<T_Value>{};
return VlQueue<T_Key>::cons(it->first);
}
template <typename Func> VlQueue<T_Value> find_last(Func with_func) const {
const auto it
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.rend()) return VlQueue<T_Value>{};
return VlQueue<T_Value>::cons(it->second);
}
template <typename Func> VlQueue<T_Key> find_last_index(Func with_func) const {
const auto it
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.rend()) return VlQueue<T_Value>{};
return VlQueue<T_Key>::cons(it->first);
}
// Reduction operators
VlQueue<T_Value> min() const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::min_element(
m_map.begin(), m_map.end(),
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return a.second < b.second;
});
return VlQueue<T_Value>::cons(it->second);
}
VlQueue<T_Value> max() const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::max_element(
m_map.begin(), m_map.end(),
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return a.second < b.second;
});
return VlQueue<T_Value>::cons(it->second);
}
T_Value r_sum() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out += i.second;
return out;
}
template <typename Func> T_Value r_sum(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out += with_func(i.first, i.second);
return out;
}
T_Value r_product() const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{it->second};
++it;
for (; it != m_map.end(); ++it) out *= it->second;
return out;
}
template <typename Func> T_Value r_product(Func with_func) const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{with_func(it->first, it->second)};
++it;
for (; it != m_map.end(); ++it) out *= with_func(it->first, it->second);
return out;
}
T_Value r_and() const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{it->second};
++it;
for (; it != m_map.end(); ++it) out &= it->second;
return out;
}
template <typename Func> T_Value r_and(Func with_func) const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{with_func(it->first, it->second)};
++it;
for (; it != m_map.end(); ++it) out &= with_func(it->first, it->second);
return out;
}
T_Value r_or() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out |= i.second;
return out;
}
template <typename Func> T_Value r_or(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out |= with_func(i.first, i.second);
return out;
}
T_Value r_xor() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out ^= i.second;
return out;
}
template <typename Func> T_Value r_xor(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out ^= with_func(i.first, i.second);
return out;
}
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
if (m_map.empty()) return "'{}"; // No trailing space
std::string out = "'{";
std::string comma;
for (const auto& i : m_map) {
out += comma + VL_TO_STRING(i.first) + ":" + VL_TO_STRING(i.second);
comma = ", ";
}
// Default not printed - maybe random init data
return out + "} ";
}
};
template <class T_Key, class T_Value>
std::string VL_TO_STRING(const VlAssocArray<T_Key, T_Value>& obj) {
return obj.to_string();
}
template <class T_Key, class T_Value>
void VL_READMEM_N(bool hex, int bits, const std::string& filename,
VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
VlReadMem rmem{hex, bits, filename, start, end};
if (VL_UNLIKELY(!rmem.isOpen())) return;
while (true) {
QData addr;
std::string data;
if (rmem.get(addr /*ref*/, data /*ref*/)) {
rmem.setData(&(obj.at(addr)), data);
} else {
break;
}
}
}
template <class T_Key, class T_Value>
void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
const VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
VlWriteMem wmem{hex, bits, filename, start, end};
if (VL_UNLIKELY(!wmem.isOpen())) return;
for (const auto& i : obj) {
const QData addr = i.first;
if (addr >= start && addr <= end) wmem.print(addr, true, &(i.second));
}
}
//===================================================================
/// Verilog unpacked array container
/// For when a standard C++[] array is not sufficient, e.g. an
/// array under a queue, or methods operating on the array.
///
/// A 'struct' as we want this to be an aggregate type that allows
/// static aggregate initialization. Consider data members private.
///
/// This class may get exposed to a Verilated Model's top I/O, if the top
/// IO has an unpacked array.
template <class T_Value, std::size_t T_Depth> struct VlUnpacked final {
// MEMBERS
// This should be the only data member, otherwise generated static initializers need updating
T_Value m_storage[T_Depth]; // Contents of the unpacked array
// CONSTRUCTORS
// Default constructors and destructor are used. Note however that C++20 requires that
// aggregate types do not have a user declared constructor, not even an explicitly defaulted
// one.
// OPERATOR METHODS
// Default copy assignment operators are used.
// METHODS
// Raw access
WData* data() { return &m_storage[0]; }
const WData* data() const { return &m_storage[0]; }
T_Value& operator[](size_t index) { return m_storage[index]; };
const T_Value& operator[](size_t index) const { return m_storage[index]; };
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
std::string out = "'{";
std::string comma;
for (int i = 0; i < T_Depth; ++i) {
out += comma + VL_TO_STRING(m_storage[i]);
comma = ", ";
}
return out + "} ";
}
};
template <class T_Value, std::size_t T_Depth>
std::string VL_TO_STRING(const VlUnpacked<T_Value, T_Depth>& obj) {
return obj.to_string();
}
//===================================================================
// Verilog class reference container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
#define VlClassRef std::shared_ptr
template <class T> // T typically of type VlClassRef<x>
inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
if (VL_UNLIKELY(!t)) Verilated::nullPointerError(filename, linenum);
return t;
}
template <typename T, typename U>
static inline bool VL_CAST_DYNAMIC(VlClassRef<T> in, VlClassRef<U>& outr) {
VlClassRef<U> casted = std::dynamic_pointer_cast<U>(in);
if (VL_LIKELY(casted)) {
outr = casted;
return true;
} else {
return false;
}
}
//======================================================================
#endif // Guard
+30 -29
View File
@@ -102,7 +102,7 @@ VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep) {
}
void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
if (isOpen()) return;
// Set member variables
@@ -120,7 +120,7 @@ void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
void VerilatedVcd::openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex) {
// Open next filename in concat sequence, mangle filename if
// incFilename is true.
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
openNextImp(incFilename);
}
@@ -129,7 +129,7 @@ void VerilatedVcd::openNextImp(bool incFilename) {
if (incFilename) {
// Find _0000.{ext} in filename
std::string name = m_filename;
size_t pos = name.rfind('.');
const size_t pos = name.rfind('.');
if (pos > 8 && 0 == std::strncmp("_cat", name.c_str() + pos - 8, 4)
&& std::isdigit(name.c_str()[pos - 4]) && std::isdigit(name.c_str()[pos - 3])
&& std::isdigit(name.c_str()[pos - 2]) && std::isdigit(name.c_str()[pos - 1])) {
@@ -195,11 +195,11 @@ void VerilatedVcd::makeNameMap() {
if (!hiername.empty() && hiername[0] == '\t') nullScope = true;
}
if (nullScope) {
NameMap* newmapp = new NameMap;
NameMap* const newmapp = new NameMap;
for (const auto& i : *m_namemapp) {
const std::string& hiername = i.first;
const std::string& decl = i.second;
std::string newname = std::string("top");
std::string newname{"top"};
if (hiername[0] != '\t') newname += ' ';
newname += hiername;
newmapp->emplace(newname, decl);
@@ -243,7 +243,7 @@ void VerilatedVcd::closeErr() {
void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
// This function is on the flush() call path
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
if (!isOpen()) return;
if (m_evcd) {
printStr("$vcdclose ");
@@ -257,7 +257,7 @@ void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
}
void VerilatedVcd::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
VerilatedTrace<VerilatedVcd>::flushBase();
bufferFlush();
}
@@ -271,8 +271,9 @@ void VerilatedVcd::printStr(const char* str) {
}
void VerilatedVcd::printQuad(vluint64_t n) {
char buf[100];
VL_SNPRINTF(buf, 100, "%" VL_PRI64 "u", n);
constexpr size_t LEN_STR_QUAD = 40;
char buf[LEN_STR_QUAD];
VL_SNPRINTF(buf, LEN_STR_QUAD, "%" VL_PRI64 "u", n);
printStr(buf);
}
@@ -280,7 +281,7 @@ void VerilatedVcd::bufferResize(vluint64_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)) {
char* oldbufp = m_wrBufp;
const char* oldbufp = m_wrBufp;
m_wrChunkSize = minsize * 2;
m_wrBufp = new char[m_wrChunkSize * 8];
std::memcpy(m_wrBufp, oldbufp, m_writep - oldbufp);
@@ -297,12 +298,12 @@ void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
// When it gets nearly full we dump it using this routine which calls write()
// This is much faster than using buffered I/O
if (VL_UNLIKELY(!isOpen())) return;
char* wp = m_wrBufp;
const char* wp = m_wrBufp;
while (true) {
ssize_t remaining = (m_writep - wp);
const ssize_t remaining = (m_writep - wp);
if (remaining == 0) break;
errno = 0;
ssize_t got = m_filep->write(wp, remaining);
const ssize_t got = m_filep->write(wp, remaining);
if (got > 0) {
wp += got;
m_wroteBytes += got;
@@ -310,8 +311,8 @@ void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) {
// LCOV_EXCL_START
// write failed, presume error (perhaps out of disk space)
std::string msg
= std::string("VerilatedVcd::bufferFlush: ") + std::strerror(errno);
const std::string msg
= std::string{"VerilatedVcd::bufferFlush: "} + std::strerror(errno);
VL_FATAL_MT("", 0, "", msg.c_str());
closeErr();
break;
@@ -331,7 +332,7 @@ char* VerilatedVcd::writeCode(char* writep, vluint32_t code) {
*writep++ = static_cast<char>('!' + code % 94);
code /= 94;
while (code) {
code--;
--code;
*writep++ = static_cast<char>('!' + code % 94);
code /= 94;
}
@@ -352,12 +353,12 @@ void VerilatedVcd::dumpHeader() {
printStr("$version Generated by VerilatedVcd $end\n");
printStr("$date ");
{
time_t tick = time(nullptr);
const time_t tick = time(nullptr);
tm ticktm;
VL_LOCALTIME_R(&tick, &ticktm);
constexpr int bufsize = 50;
char buf[bufsize];
std::strftime(buf, bufsize, "%c", &ticktm);
constexpr size_t LEN_BUF = 50;
char buf[LEN_BUF];
std::strftime(buf, LEN_BUF, "%c", &ticktm);
printStr(buf);
}
printStr(" $end\n");
@@ -385,7 +386,7 @@ void VerilatedVcd::dumpHeader() {
const std::string& decl = i.second;
// Determine difference between the old and new names
const char* hiername = hiernamestr.c_str();
const char* const hiername = hiernamestr.c_str();
const char* lp = lastName;
const char* np = hiername;
lastName = hiername;
@@ -393,8 +394,8 @@ void VerilatedVcd::dumpHeader() {
// Skip common prefix, it must break at a space or tab
for (; *np && (*np == *lp); np++, lp++) {}
while (np != hiername && *np && *np != ' ' && *np != '\t') {
np--;
lp--;
--np;
--lp;
}
// printf("hier %s\n lp=%s\n np=%s\n",hiername,lp,np);
@@ -430,9 +431,9 @@ void VerilatedVcd::dumpHeader() {
for (; *np && *np != ' ' && *np != '\t'; np++) {
if (*np == '[') {
printStr("(");
printStr("[");
} else if (*np == ']') {
printStr(")");
printStr("]");
} else if (!(*np & '\x80')) {
*m_writep++ = *np;
}
@@ -531,7 +532,7 @@ void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep,
decl += " ";
decl += basename;
if (array) {
VL_SNPRINTF(buf, bufsize, "(%d)", arraynum);
VL_SNPRINTF(buf, bufsize, "[%d]", arraynum);
decl += buf;
hiername += buf;
}
@@ -896,9 +897,9 @@ void vcdTestMain(const char* filenamep) {
VerilatedVcdC* vcdp = new VerilatedVcdC;
vcdp->evcd(true);
vcdp->set_time_unit("1ms");
vcdp->set_time_unit(std::string("1ms"));
vcdp->set_time_unit(std::string{"1ms"});
vcdp->set_time_resolution("1ns");
vcdp->set_time_resolution(std::string("1ns"));
vcdp->set_time_resolution(std::string{"1ns"});
vcdp->spTrace()->addInitCb(&vcdInit, 0);
vcdp->spTrace()->addFullCb(&vcdFull, 0);
vcdp->spTrace()->addChgCb(&vcdChange, 0);
@@ -927,7 +928,7 @@ void vcdTestMain(const char* filenamep) {
vcdp->dump(++timestamp);
vcdp->dump(++timestamp);
# ifdef VERILATED_VCD_TEST_64BIT
vluint64_t bytesPerDump = 15ULL;
const vluint64_t bytesPerDump = 15ULL;
for (vluint64_t i = 0; i < ((1ULL << 32) / bytesPerDump); i++) {
v1 = i;
vcdp->dump(++timestamp);
+8 -8
View File
@@ -74,7 +74,7 @@ private:
int m_modDepth = 0; // Depth of module hierarchy
char* m_wrBufp; // Output buffer
char* m_wrFlushp; // Output buffer flush trigger location
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
@@ -233,11 +233,11 @@ public:
// Inside dumping routines, dump one signal if it has changed.
// We do want to inline these to avoid calls when the value did not change.
inline void chgBit(vluint32_t code, const vluint32_t newval) {
vluint32_t diff = oldp(code)[0] ^ newval;
const vluint32_t diff = oldp(code)[0] ^ newval;
if (VL_UNLIKELY(diff)) fullBit(code, newval);
}
inline void chgBus(vluint32_t code, const vluint32_t newval, int bits) {
vluint32_t diff = oldp(code)[0] ^ newval;
const vluint32_t diff = oldp(code)[0] ^ newval;
if (VL_UNLIKELY(diff)) {
if (VL_UNLIKELY(bits == 32 || (diff & ((1U << bits) - 1)))) {
fullBus(code, newval, bits);
@@ -245,7 +245,7 @@ public:
}
}
inline void chgQuad(vluint32_t code, const vluint64_t newval, int bits) {
vluint64_t diff = (*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval;
const vluint64_t diff = (*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval;
if (VL_UNLIKELY(diff)) {
if (VL_UNLIKELY(bits == 64 || (diff & ((1ULL << bits) - 1)))) {
fullQuad(code, newval, bits);
@@ -270,7 +270,7 @@ public:
}
}
inline void chgTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
const vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
if (VL_UNLIKELY(diff)) {
// Verilator 3.510 and newer provide clean input, so the below
// is only for back compatibility
@@ -281,7 +281,7 @@ public:
}
inline void chgTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri,
int bits) {
vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
const vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
if (VL_UNLIKELY(diff)) {
if (VL_UNLIKELY(bits == 32 || (diff & ((1U << bits) - 1)))) {
fullTriBus(code, newval, newtri, bits);
@@ -290,8 +290,8 @@ public:
}
inline void chgTriQuad(vluint32_t code, const vluint64_t newval, const vluint64_t newtri,
int bits) {
vluint64_t diff = (((*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval)
| ((*(reinterpret_cast<vluint64_t*>(oldp(code + 1)))) ^ newtri));
const vluint64_t diff = (((*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval)
| ((*(reinterpret_cast<vluint64_t*>(oldp(code + 1)))) ^ newtri));
if (VL_UNLIKELY(diff)) {
if (VL_UNLIKELY(bits == 64 || (diff & ((1ULL << bits) - 1)))) {
fullTriQuad(code, newval, newtri, bits);
+2 -2
View File
@@ -44,9 +44,9 @@ public:
// We want to avoid a depreciated warning, but still be back compatible.
// Turning off the message just for this still results in an
// annoying "to turn off" message.
sc_time t1sec(1, SC_SEC);
const sc_time t1sec{1, SC_SEC};
if (t1sec.to_default_time_units() != 0) {
sc_time tunits(1.0 / t1sec.to_default_time_units(), SC_SEC);
const sc_time tunits{1.0 / t1sec.to_default_time_units(), SC_SEC};
spTrace()->set_time_unit(tunits.to_string());
}
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
+179 -173
View File
@@ -80,7 +80,7 @@ public:
static const size_t chunk = 96;
if (VL_UNCOVERABLE(size > chunk)) VL_FATAL_MT(__FILE__, __LINE__, "", "increase chunk");
if (VL_LIKELY(t_freeHead)) {
vluint8_t* newp = t_freeHead;
vluint8_t* const newp = t_freeHead;
t_freeHead = *(reinterpret_cast<vluint8_t**>(newp));
*(reinterpret_cast<vluint32_t*>(newp)) = activeMagic();
return newp + 8;
@@ -91,7 +91,7 @@ public:
return newp + 8;
}
static void operator delete(void* obj, size_t /*size*/)VL_MT_SAFE {
vluint8_t* oldp = (static_cast<vluint8_t*>(obj)) - 8;
vluint8_t* const oldp = (static_cast<vluint8_t*>(obj)) - 8;
if (VL_UNLIKELY(*(reinterpret_cast<vluint32_t*>(oldp)) != activeMagic())) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"vpi_release_handle() called on same object twice, or on non-Verilator "
@@ -123,12 +123,12 @@ 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
vluint64_t m_id; // Unique id/sequence number to find schedule's event
QData m_time;
const vluint64_t m_id; // Unique id/sequence number to find schedule's event
const QData m_time;
public:
VerilatedVpioTimedCb(vluint64_t id, QData time)
: m_id(id)
: m_id{id}
, m_time{time} {}
virtual ~VerilatedVpioTimedCb() override = default;
static VerilatedVpioTimedCb* castp(vpiHandle h) {
@@ -141,13 +141,13 @@ public:
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
vluint64_t m_id; // Unique id/sequence number to find schedule's event
PLI_INT32 m_reason; // VPI callback reason code
const vluint64_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)
: m_id(id)
: m_id{id}
, m_reason{reason} {}
virtual ~VerilatedVpioReasonCb() override = default;
static VerilatedVpioReasonCb* castp(vpiHandle h) {
@@ -158,7 +158,7 @@ public:
};
class VerilatedVpioConst final : public VerilatedVpio {
vlsint32_t m_num;
const vlsint32_t m_num;
public:
explicit VerilatedVpioConst(vlsint32_t num)
@@ -200,7 +200,7 @@ public:
virtual const char* name() const override { return m_varp->name(); }
virtual const char* fullname() const override {
static VL_THREAD_LOCAL std::string t_out;
t_out = std::string(m_scopep->name()) + "." + name();
t_out = std::string{m_scopep->name()} + "." + name();
return t_out.c_str();
}
};
@@ -208,7 +208,7 @@ public:
class VerilatedVpioParam final : public VerilatedVpioVarBase {
public:
VerilatedVpioParam(const VerilatedVar* varp, const VerilatedScope* scopep)
: VerilatedVpioVarBase(varp, scopep) {}
: VerilatedVpioVarBase{varp, scopep} {}
virtual ~VerilatedVpioParam() override = default;
static VerilatedVpioParam* castp(vpiHandle h) {
@@ -219,7 +219,7 @@ public:
};
class VerilatedVpioRange final : public VerilatedVpio {
const VerilatedRange* m_range;
const VerilatedRange* const m_range;
public:
explicit VerilatedVpioRange(const VerilatedRange* range)
@@ -235,7 +235,7 @@ public:
class VerilatedVpioRangeIter final : public VerilatedVpio {
// Only supports 1 dimension
const VerilatedRange* m_range;
const VerilatedRange* const m_range;
bool m_done = false;
public:
@@ -252,13 +252,13 @@ public:
return nullptr;
}
m_done = true;
return ((new VerilatedVpioRange(m_range))->castVpiHandle());
return ((new VerilatedVpioRange{m_range})->castVpiHandle());
}
};
class VerilatedVpioScope VL_NOT_FINAL : public VerilatedVpio {
protected:
const VerilatedScope* m_scopep;
const VerilatedScope* const m_scopep;
public:
explicit VerilatedVpioScope(const VerilatedScope* scopep)
@@ -286,13 +286,13 @@ protected:
public:
VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep)
: VerilatedVpioVarBase(varp, scopep) {
: VerilatedVpioVarBase{varp, scopep} {
m_mask.u32 = VL_MASK_I(varp->packed().elements());
m_entSize = varp->entSize();
m_varDatap = varp->datap();
}
explicit VerilatedVpioVar(const VerilatedVpioVar* varp)
: VerilatedVpioVarBase(varp) {
: VerilatedVpioVarBase{varp} {
if (varp) {
m_mask.u32 = varp->m_mask.u32;
m_entSize = varp->m_entSize;
@@ -310,7 +310,7 @@ public:
return dynamic_cast<VerilatedVpioVar*>(reinterpret_cast<VerilatedVpio*>(h));
}
vluint32_t mask() const { return m_mask.u32; }
vluint8_t mask_byte(int idx) { return m_mask.u8[idx & 3]; }
vluint8_t mask_byte(int idx) const { return m_mask.u8[idx & 3]; }
vluint32_t entSize() const { return m_entSize; }
vluint32_t index() const { return m_index; }
virtual vluint32_t type() const override {
@@ -343,15 +343,16 @@ public:
virtual const VerilatedRange* rangep() const override { return &(varp()->packed()); }
virtual const char* fullname() const override {
static VL_THREAD_LOCAL std::string t_out;
char num[25];
VL_SNPRINTF(num, 25, "%d", m_index);
t_out = std::string(scopep()->name()) + "." + name() + "[" + num + "]";
constexpr size_t LEN_MAX_INDEX = 25;
char num[LEN_MAX_INDEX];
VL_SNPRINTF(num, LEN_MAX_INDEX, "%d", m_index);
t_out = std::string{scopep()->name()} + "." + name() + "[" + num + "]";
return t_out.c_str();
}
};
class VerilatedVpioVarIter final : public VerilatedVpio {
const VerilatedScope* m_scopep;
const VerilatedScope* const m_scopep;
VerilatedVarNameMap::const_iterator m_it;
bool m_started = false;
@@ -365,7 +366,7 @@ public:
virtual vluint32_t type() const override { return vpiIterator; }
virtual vpiHandle dovpi_scan() override {
if (VL_LIKELY(m_scopep->varsp())) {
VerilatedVarNameMap* varsp = m_scopep->varsp();
const VerilatedVarNameMap* const varsp = m_scopep->varsp();
if (VL_UNLIKELY(!m_started)) {
m_it = varsp->begin();
m_started = true;
@@ -379,7 +380,7 @@ public:
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
}
return ((new VerilatedVpioVar(&(m_it->second), m_scopep))->castVpiHandle());
return ((new VerilatedVpioVar{&(m_it->second), m_scopep})->castVpiHandle());
}
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr; // End of list - only one deep
@@ -388,9 +389,9 @@ public:
class VerilatedVpioMemoryWordIter final : public VerilatedVpio {
const vpiHandle m_handle;
const VerilatedVar* m_varp;
const VerilatedVar* const m_varp;
vlsint32_t m_iteration;
vlsint32_t m_direction;
const vlsint32_t m_direction;
bool m_done = false;
public:
@@ -412,7 +413,7 @@ public:
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
}
vpiHandle result = vpi_handle_by_index(m_handle, m_iteration);
const vpiHandle result = vpi_handle_by_index(m_handle, m_iteration);
iterationInc();
return result;
}
@@ -456,8 +457,8 @@ public:
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
}
const VerilatedScope* modp = *m_it++;
return (new VerilatedVpioModule(modp))->castVpiHandle();
const VerilatedScope* const modp = *m_it++;
return (new VerilatedVpioModule{modp})->castVpiHandle();
}
};
@@ -475,16 +476,16 @@ class VerilatedVpiCbHolder final {
public:
// cppcheck-suppress uninitVar // m_value
VerilatedVpiCbHolder(vluint64_t id, const s_cb_data* cbDatap, const VerilatedVpioVar* varop)
: m_id(id)
, m_cbData(*cbDatap)
, m_varo(varop) {
: m_id{id}
, m_cbData{*cbDatap}
, m_varo{varop} {
m_value.format = cbDatap->value ? cbDatap->value->format : vpiSuppressVal;
m_cbData.value = &m_value;
if (varop) {
m_cbData.obj = m_varo.castVpiHandle();
m_varo.createPrevDatap();
} else {
m_cbData.obj = NULL;
m_cbData.obj = nullptr;
}
}
~VerilatedVpiCbHolder() = default;
@@ -564,7 +565,7 @@ public:
}
static void callTimedCbs() VL_MT_UNSAFE_ONE {
assertOneCheck();
QData time = VL_TIME_Q();
const QData time = VL_TIME_Q();
for (auto it = s().m_timedCbs.begin(); it != s().m_timedCbs.end();) {
if (VL_UNLIKELY(it->first.first <= time)) {
VerilatedVpiCbHolder& ho = it->second;
@@ -587,14 +588,14 @@ public:
if (VL_LIKELY(it != s().m_timedCbs.cend())) return it->first.first;
return ~0ULL; // maxquad
}
static bool callCbs(vluint32_t reason) VL_MT_UNSAFE_ONE {
static bool callCbs(const vluint32_t reason) VL_MT_UNSAFE_ONE {
VpioCbList& cbObjList = s().m_cbObjLists[reason];
bool called = false;
if (cbObjList.empty()) return called;
const auto last = std::prev(cbObjList.end()); // prevent looping over newly added elements
for (auto it = cbObjList.begin(); true;) {
// cbReasonRemove sets to nullptr, so we know on removal the old end() will still exist
bool was_last = it == last;
const bool was_last = it == last;
if (VL_UNLIKELY(it->invalid())) { // Deleted earlier, cleanup
it = cbObjList.erase(it);
if (was_last) break;
@@ -619,23 +620,25 @@ public:
const auto last = std::prev(cbObjList.end()); // prevent looping over newly added elements
for (auto it = cbObjList.begin(); true;) {
// cbReasonRemove sets to nullptr, so we know on removal the old end() will still exist
bool was_last = it == last;
const bool was_last = it == last;
if (VL_UNLIKELY(it->invalid())) { // Deleted earlier, cleanup
it = cbObjList.erase(it);
if (was_last) break;
continue;
}
VerilatedVpiCbHolder& ho = *it++;
if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(ho.cb_datap()->obj)) {
void* newDatap = varop->varDatap();
void* prevDatap = varop->prevDatap(); // Was malloced when we added the callback
if (VerilatedVpioVar* const varop = VerilatedVpioVar::castp(ho.cb_datap()->obj)) {
void* const newDatap = varop->varDatap();
void* const prevDatap
= varop->prevDatap(); // Was malloced when we added the callback
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
varop->fullname(), *((CData*)newDatap),
*((CData*)prevDatap), newDatap, prevDatap););
varop->fullname(), *(static_cast<CData*>(newDatap)),
*(static_cast<CData*>(prevDatap)), newDatap,
prevDatap););
if (std::memcmp(prevDatap, newDatap, varop->entSize()) != 0) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" VL_PRI64
"d %s v[0]=%d\n",
ho.id(), varop->fullname(), *((CData*)newDatap)););
VL_DEBUG_IF_PLI(
VL_DBG_MSGF("- vpi: value_callback %" VL_PRI64 "d %s v[0]=%d\n", ho.id(),
varop->fullname(), *(static_cast<CData*>(newDatap))););
update.insert(varop);
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
(ho.cb_rtnp())(ho.cb_datap());
@@ -706,7 +709,8 @@ public:
va_end(args);
m_errorInfo.state = vpiPLI;
t_filehold = file;
setError((PLI_BYTE8*)m_buff, nullptr, const_cast<PLI_BYTE8*>(t_filehold.c_str()), line);
setError(static_cast<PLI_BYTE8*>(m_buff), nullptr,
const_cast<PLI_BYTE8*>(t_filehold.c_str()), line);
}
p_vpi_error_info getError() {
if (m_flag) return &m_errorInfo;
@@ -715,7 +719,7 @@ public:
void resetError() { m_flag = false; }
static void vpi_unsupported() {
// Not supported yet
p_vpi_error_info error_info_p = VerilatedVpiImp::error_info()->getError();
const p_vpi_error_info error_info_p = VerilatedVpiImp::error_info()->getError();
if (error_info_p) {
VL_FATAL_MT(error_info_p->file, error_info_p->line, "", error_info_p->message);
return;
@@ -758,7 +762,7 @@ PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() {
VerilatedVpiError* VerilatedVpiImp::error_info() VL_MT_UNSAFE_ONE {
VerilatedVpiImp::assertOneCheck();
if (VL_UNLIKELY(!s().m_errorInfop)) s().m_errorInfop = new VerilatedVpiError();
if (VL_UNLIKELY(!s().m_errorInfop)) s().m_errorInfop = new VerilatedVpiError;
return s().m_errorInfop;
}
@@ -1107,14 +1111,14 @@ const char* VerilatedVpiError::strFromVpiProp(PLI_INT32 vpiVal) VL_MT_SAFE {
#define SELF_CHECK_RESULT_CSTR(got, exp) \
if (0 != std::strcmp((got), (exp))) { \
std::string msg \
= std::string("%Error: ") + "GOT = '" + got + "'" + " EXP = '" + exp + "'"; \
const std::string msg \
= std::string{"%Error: "} + "GOT = '" + (got) + "'" + " EXP = '" + (exp) + "'"; \
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); \
}
#define SELF_CHECK_ENUM_STR(fn, enumn) \
do { \
const char* strVal = VerilatedVpiError::fn(enumn); \
const char* const strVal = VerilatedVpiError::fn(enumn); \
SELF_CHECK_RESULT_CSTR(strVal, #enumn); \
} while (0)
@@ -1300,9 +1304,9 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
case cbAfterDelay: {
QData time = 0;
if (cb_data_p->time) time = VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low);
QData abstime = VL_TIME_Q() + time;
vluint64_t id = VerilatedVpiImp::nextCallbackId();
VerilatedVpioTimedCb* vop = new VerilatedVpioTimedCb{id, abstime};
const QData abstime = VL_TIME_Q() + time;
const vluint64_t id = VerilatedVpiImp::nextCallbackId();
VerilatedVpioTimedCb* const vop = new VerilatedVpioTimedCb{id, abstime};
VerilatedVpiImp::cbTimedAdd(id, cb_data_p, abstime);
return vop->castVpiHandle();
}
@@ -1316,8 +1320,8 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
case cbEnterInteractive: // FALLTHRU // NOP, but need to return handle, so make object
case cbExitInteractive: // FALLTHRU // NOP, but need to return handle, so make object
case cbInteractiveScopeChange: { // FALLTHRU // NOP, but need to return handle, so make object
vluint64_t id = VerilatedVpiImp::nextCallbackId();
VerilatedVpioReasonCb* vop = new VerilatedVpioReasonCb{id, cb_data_p->reason};
const vluint64_t id = VerilatedVpiImp::nextCallbackId();
VerilatedVpioReasonCb* const vop = new VerilatedVpioReasonCb{id, cb_data_p->reason};
VerilatedVpiImp::cbReasonAdd(id, cb_data_p);
return vop->castVpiHandle();
}
@@ -1332,7 +1336,7 @@ PLI_INT32 vpi_remove_cb(vpiHandle cb_obj) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_remove_cb %p\n", cb_obj););
VerilatedVpiImp::assertOneCheck();
VL_VPI_ERROR_RESET_();
VerilatedVpio* vop = VerilatedVpio::castp(cb_obj);
VerilatedVpio* const vop = VerilatedVpio::castp(cb_obj);
if (VL_UNLIKELY(!vop)) return 0;
return vop->dovpi_remove_cb();
}
@@ -1355,10 +1359,10 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle_by_name %s %p\n", namep, scope););
const VerilatedVar* varp = nullptr;
const VerilatedScope* scopep;
VerilatedVpioScope* voScopep = VerilatedVpioScope::castp(scope);
const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope);
std::string scopeAndName = namep;
if (voScopep) {
scopeAndName = std::string(voScopep->fullname()) + "." + namep;
scopeAndName = std::string{voScopep->fullname()} + "." + namep;
namep = const_cast<PLI_BYTE8*>(scopeAndName.c_str());
}
{
@@ -1366,17 +1370,18 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
scopep = Verilated::threadContextp()->scopeFind(namep);
if (scopep) { // Whole thing found as a scope
if (scopep->type() == VerilatedScope::SCOPE_MODULE) {
return (new VerilatedVpioModule(scopep))->castVpiHandle();
return (new VerilatedVpioModule{scopep})->castVpiHandle();
} else {
return (new VerilatedVpioScope(scopep))->castVpiHandle();
return (new VerilatedVpioScope{scopep})->castVpiHandle();
}
}
const char* baseNamep = scopeAndName.c_str();
std::string scopename;
const char* dotp = std::strrchr(namep, '.');
const char* const dotp = std::strrchr(namep, '.');
if (VL_LIKELY(dotp)) {
baseNamep = dotp + 1;
scopename = std::string(namep, dotp - namep);
const size_t len = dotp - namep;
scopename = std::string{namep, len};
}
if (scopename.find('.') == std::string::npos) {
@@ -1393,9 +1398,9 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
if (!varp) return nullptr;
if (varp->isParam()) {
return (new VerilatedVpioParam(varp, scopep))->castVpiHandle();
return (new VerilatedVpioParam{varp, scopep})->castVpiHandle();
} else {
return (new VerilatedVpioVar(varp, scopep))->castVpiHandle();
return (new VerilatedVpioVar{varp, scopep})->castVpiHandle();
}
}
@@ -1405,24 +1410,24 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) {
VerilatedVpiImp::assertOneCheck();
VL_VPI_ERROR_RESET_();
// Memory words are not indexable
VerilatedVpioMemoryWord* vop = VerilatedVpioMemoryWord::castp(object);
const VerilatedVpioMemoryWord* const vop = VerilatedVpioMemoryWord::castp(object);
if (VL_UNLIKELY(vop)) return nullptr;
VerilatedVpioVar* varop = VerilatedVpioVar::castp(object);
const VerilatedVpioVar* const varop = VerilatedVpioVar::castp(object);
if (VL_LIKELY(varop)) {
if (varop->varp()->dims() < 2) return nullptr;
if (VL_LIKELY(varop->varp()->unpacked().left() >= varop->varp()->unpacked().right())) {
if (VL_UNLIKELY(indx > varop->varp()->unpacked().left()
|| indx < varop->varp()->unpacked().right()))
return nullptr;
return (new VerilatedVpioMemoryWord(varop->varp(), varop->scopep(), indx,
indx - varop->varp()->unpacked().right()))
return (new VerilatedVpioMemoryWord{varop->varp(), varop->scopep(), indx,
indx - varop->varp()->unpacked().right()})
->castVpiHandle();
}
if (VL_UNLIKELY(indx < varop->varp()->unpacked().left()
|| indx > varop->varp()->unpacked().right()))
return nullptr;
return (new VerilatedVpioMemoryWord(varop->varp(), varop->scopep(), indx,
indx - varop->varp()->unpacked().left()))
return (new VerilatedVpioMemoryWord{varop->varp(), varop->scopep(), indx,
indx - varop->varp()->unpacked().left()})
->castVpiHandle();
}
VL_VPI_INTERNAL_(__FILE__, __LINE__, "%s : can't resolve handle", __func__);
@@ -1437,12 +1442,12 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
VL_VPI_ERROR_RESET_();
switch (type) {
case vpiLeftRange: {
if (VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object)) {
if (const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object)) {
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
return (new VerilatedVpioConst(vop->rangep()->left()))->castVpiHandle();
} else if (VerilatedVpioRange* vop = VerilatedVpioRange::castp(object)) {
return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle();
} else if (const VerilatedVpioRange* const vop = VerilatedVpioRange::castp(object)) {
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
return (new VerilatedVpioConst(vop->rangep()->left()))->castVpiHandle();
return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle();
}
VL_VPI_WARNING_(__FILE__, __LINE__,
"%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
@@ -1450,12 +1455,12 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
return nullptr;
}
case vpiRightRange: {
if (VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object)) {
if (const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object)) {
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
return (new VerilatedVpioConst(vop->rangep()->right()))->castVpiHandle();
} else if (VerilatedVpioRange* vop = VerilatedVpioRange::castp(object)) {
return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle();
} else if (const VerilatedVpioRange* const vop = VerilatedVpioRange::castp(object)) {
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
return (new VerilatedVpioConst(vop->rangep()->right()))->castVpiHandle();
return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle();
}
VL_VPI_WARNING_(__FILE__, __LINE__,
"%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
@@ -1463,19 +1468,20 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
return nullptr;
}
case vpiIndex: {
VerilatedVpioVar* vop = VerilatedVpioVar::castp(object);
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
return (new VerilatedVpioConst(vop->index()))->castVpiHandle();
const vlsint32_t val = vop->index();
return (new VerilatedVpioConst{val})->castVpiHandle();
}
case vpiScope: {
VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object);
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
return (new VerilatedVpioScope(vop->scopep()))->castVpiHandle();
return (new VerilatedVpioScope{vop->scopep()})->castVpiHandle();
}
case vpiParent: {
VerilatedVpioMemoryWord* vop = VerilatedVpioMemoryWord::castp(object);
const VerilatedVpioMemoryWord* const vop = VerilatedVpioMemoryWord::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
return (new VerilatedVpioVar(vop->varp(), vop->scopep()))->castVpiHandle();
return (new VerilatedVpioVar{vop->varp(), vop->scopep()})->castVpiHandle();
}
default:
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
@@ -1496,7 +1502,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
VL_VPI_ERROR_RESET_();
switch (type) {
case vpiMemoryWord: {
VerilatedVpioVar* vop = VerilatedVpioVar::castp(object);
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
if (vop->varp()->dims() < 2) return nullptr;
if (vop->varp()->dims() > 2) {
@@ -1505,10 +1511,10 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
VerilatedVpiError::strFromVpiMethod(type), vop->fullname(),
vop->varp()->dims());
}
return (new VerilatedVpioMemoryWordIter(object, vop->varp()))->castVpiHandle();
return (new VerilatedVpioMemoryWordIter{object, vop->varp()})->castVpiHandle();
}
case vpiRange: {
VerilatedVpioVar* vop = VerilatedVpioVar::castp(object);
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
if (vop->varp()->dims() < 2) return nullptr;
// Unsupported is multidim list
@@ -1518,20 +1524,20 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
VerilatedVpiError::strFromVpiMethod(type), vop->fullname(),
vop->varp()->dims());
}
return ((new VerilatedVpioRangeIter(vop->rangep()))->castVpiHandle());
return ((new VerilatedVpioRangeIter{vop->rangep()})->castVpiHandle());
}
case vpiReg: {
VerilatedVpioScope* vop = VerilatedVpioScope::castp(object);
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
return ((new VerilatedVpioVarIter(vop->scopep()))->castVpiHandle());
return ((new VerilatedVpioVarIter{vop->scopep()})->castVpiHandle());
}
case vpiModule: {
VerilatedVpioModule* vop = VerilatedVpioModule::castp(object);
const VerilatedHierarchyMap* map = VerilatedImp::hierarchyMap();
const VerilatedScope* mod = vop ? vop->scopep() : nullptr;
const auto it = vlstd::as_const(map)->find(const_cast<VerilatedScope*>(mod));
const VerilatedVpioModule* const vop = VerilatedVpioModule::castp(object);
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
const VerilatedScope* const modp = vop ? vop->scopep() : nullptr;
const auto it = vlstd::as_const(map)->find(const_cast<VerilatedScope*>(modp));
if (it == map->end()) return nullptr;
return ((new VerilatedVpioModuleIter(it->second))->castVpiHandle());
return ((new VerilatedVpioModuleIter{it->second})->castVpiHandle());
}
default:
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
@@ -1543,7 +1549,7 @@ vpiHandle vpi_scan(vpiHandle object) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_scan %p\n", object););
VerilatedVpiImp::assertOneCheck();
VL_VPI_ERROR_RESET_();
VerilatedVpio* vop = VerilatedVpio::castp(object);
VerilatedVpio* const vop = VerilatedVpio::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
return vop->dovpi_scan();
}
@@ -1560,30 +1566,30 @@ PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) {
return Verilated::threadContextp()->timeprecision();
}
case vpiTimeUnit: {
VerilatedVpioScope* vop = VerilatedVpioScope::castp(object);
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
if (!vop)
return Verilated::threadContextp()->timeunit(); // Null asks for global, not unlikely
return vop->scopep()->timeunit();
}
case vpiType: {
VerilatedVpio* vop = VerilatedVpio::castp(object);
const VerilatedVpio* const vop = VerilatedVpio::castp(object);
if (VL_UNLIKELY(!vop)) return 0;
return vop->type();
}
case vpiDirection: {
// By forthought, the directions already are vpi enumerated
VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object);
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
if (VL_UNLIKELY(!vop)) return 0;
return vop->varp()->vldir();
}
case vpiScalar: // FALLTHRU
case vpiVector: {
VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object);
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
if (VL_UNLIKELY(!vop)) return 0;
return (property == vpiVector) ^ (vop->varp()->dims() == 0);
}
case vpiSize: {
VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object);
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
if (VL_UNLIKELY(!vop)) return 0;
return vop->size();
}
@@ -1602,7 +1608,7 @@ PLI_INT64 vpi_get64(PLI_INT32 /*property*/, vpiHandle /*object*/) {
PLI_BYTE8* vpi_get_str(PLI_INT32 property, vpiHandle object) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_str %d %p\n", property, object););
VerilatedVpiImp::assertOneCheck();
VerilatedVpio* vop = VerilatedVpio::castp(object);
const VerilatedVpio* const vop = VerilatedVpio::castp(object);
VL_VPI_ERROR_RESET_();
if (VL_UNLIKELY(!vop)) return nullptr;
switch (property) {
@@ -1689,15 +1695,15 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
const char* fullname) {
if (!vl_check_format(varp, valuep, fullname, true)) return;
// Maximum required size is for binary string, one byte per bit plus null termination
static VL_THREAD_LOCAL char t_outStr[1 + VL_MULS_MAX_WORDS * 32];
static VL_THREAD_LOCAL char t_outStr[VL_VALUE_STRING_MAX_WORDS * VL_EDATASIZE + 1];
// cppcheck-suppress variableScope
static VL_THREAD_LOCAL int t_outStrSz = sizeof(t_outStr) - 1;
const static VL_THREAD_LOCAL int t_outStrSz = sizeof(t_outStr) - 1;
// We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal
// This may cause backward compatibility issues with older code.
if (valuep->format == vpiVectorVal) {
// Vector pointer must come from our memory pool
// It only needs to persist until the next vpi_get_value
static VL_THREAD_LOCAL t_vpi_vecval t_out[VL_MULS_MAX_WORDS * 2];
static VL_THREAD_LOCAL t_vpi_vecval t_out[VL_VALUE_STRING_MAX_WORDS * 2];
valuep->value.vector = t_out;
if (varp->vltype() == VLVT_UINT8) {
t_out[0].aval = *(reinterpret_cast<CData*>(varDatap));
@@ -1712,20 +1718,20 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
t_out[0].bval = 0;
return;
} else if (varp->vltype() == VLVT_UINT64) {
QData data = *(reinterpret_cast<QData*>(varDatap));
const QData data = *(reinterpret_cast<QData*>(varDatap));
t_out[1].aval = static_cast<IData>(data >> 32ULL);
t_out[1].bval = 0;
t_out[0].aval = static_cast<IData>(data);
t_out[0].bval = 0;
return;
} else if (varp->vltype() == VLVT_WDATA) {
int words = VL_WORDS_I(varp->packed().elements());
if (VL_UNCOVERABLE(words >= VL_MULS_MAX_WORDS)) {
VL_FATAL_MT(
__FILE__, __LINE__, "",
"vpi_get_value with more than VL_MULS_MAX_WORDS; increase and recompile");
const int words = VL_WORDS_I(varp->packed().elements());
if (VL_UNCOVERABLE(words >= VL_VALUE_STRING_MAX_WORDS)) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"vpi_get_value with more than VL_VALUE_STRING_MAX_WORDS; increase and "
"recompile");
}
WDataInP datap = (reinterpret_cast<EData*>(varDatap));
const WDataInP datap = (reinterpret_cast<EData*>(varDatap));
for (int i = 0; i < words; ++i) {
t_out[i].aval = datap[i];
t_out[i].bval = 0;
@@ -1735,7 +1741,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
} else if (valuep->format == vpiBinStrVal) {
valuep->value.str = t_outStr;
int bits = varp->packed().elements();
CData* datap = (reinterpret_cast<CData*>(varDatap));
const CData* datap = (reinterpret_cast<CData*>(varDatap));
int i;
if (bits > t_outStrSz) {
// limit maximum size of output to size of buffer to prevent overrun.
@@ -1743,12 +1749,12 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)",
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
VL_MULS_MAX_WORDS, bits);
VL_VALUE_STRING_MAX_WORDS, bits);
}
for (i = 0; i < bits; ++i) {
char val = (datap[i >> 3] >> (i & 7)) & 1;
const char val = (datap[i >> 3] >> (i & 7)) & 1;
t_outStr[bits - i - 1] = val ? '1' : '0';
}
t_outStr[i] = '\0';
@@ -1756,21 +1762,21 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
} else if (valuep->format == vpiOctStrVal) {
valuep->value.str = t_outStr;
int chars = (varp->packed().elements() + 2) / 3;
int bytes = VL_BYTES_I(varp->packed().elements());
CData* datap = (reinterpret_cast<CData*>(varDatap));
const int bytes = VL_BYTES_I(varp->packed().elements());
const CData* datap = (reinterpret_cast<CData*>(varDatap));
int i;
if (chars > t_outStrSz) {
// limit maximum size of output to size of buffer to prevent overrun.
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)",
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
VL_MULS_MAX_WORDS, chars);
VL_VALUE_STRING_MAX_WORDS, chars);
chars = t_outStrSz;
}
for (i = 0; i < chars; ++i) {
div_t idx = div(i * 3, 8);
const div_t idx = div(i * 3, 8);
int val = datap[idx.quot];
if ((idx.quot + 1) < bytes) {
// if the next byte is valid or that in
@@ -1782,7 +1788,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
if (i == (chars - 1)) {
// most signifcant char, mask off non existant bits when vector
// size is not a multiple of 3
unsigned int rem = varp->packed().elements() % 3;
const unsigned int rem = varp->packed().elements() % 3;
if (rem) {
// generate bit mask & zero non existant bits
val &= (1 << rem) - 1;
@@ -1815,16 +1821,16 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
} else if (valuep->format == vpiHexStrVal) {
valuep->value.str = t_outStr;
int chars = (varp->packed().elements() + 3) >> 2;
CData* datap = (reinterpret_cast<CData*>(varDatap));
const CData* datap = (reinterpret_cast<CData*>(varDatap));
int i;
if (chars > t_outStrSz) {
// limit maximum size of output to size of buffer to prevent overrun.
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)",
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
VL_MULS_MAX_WORDS, chars);
VL_VALUE_STRING_MAX_WORDS, chars);
chars = t_outStrSz;
}
for (i = 0; i < chars; ++i) {
@@ -1832,7 +1838,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
if (i == (chars - 1)) {
// most signifcant char, mask off non existant bits when vector
// size is not a multiple of 4
unsigned int rem = varp->packed().elements() & 3;
const unsigned int rem = varp->packed().elements() & 3;
if (rem) {
// generate bit mask & zero non existant bits
val &= (1 << rem) - 1;
@@ -1849,20 +1855,20 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
} else {
valuep->value.str = t_outStr;
int bytes = VL_BYTES_I(varp->packed().elements());
CData* datap = (reinterpret_cast<CData*>(varDatap));
const CData* datap = (reinterpret_cast<CData*>(varDatap));
int i;
if (bytes > t_outStrSz) {
// limit maximum size of output to size of buffer to prevent overrun.
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname,
t_outStrSz, VL_MULS_MAX_WORDS, bytes);
VL_VPI_WARNING_(__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than "
"required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format),
fullname, t_outStrSz, VL_VALUE_STRING_MAX_WORDS, bytes);
bytes = t_outStrSz;
}
for (i = 0; i < bytes; ++i) {
char val = datap[bytes - i - 1];
const char val = datap[bytes - i - 1];
// other simulators replace [leading?] zero chars with spaces, replicate here.
t_outStr[i] = val ? val : ' ';
}
@@ -1893,13 +1899,13 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) {
VL_VPI_ERROR_RESET_();
if (VL_UNLIKELY(!valuep)) return;
if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) {
if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) {
vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname());
return;
} else if (VerilatedVpioParam* vop = VerilatedVpioParam::castp(object)) {
} else if (const VerilatedVpioParam* const vop = VerilatedVpioParam::castp(object)) {
vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname());
return;
} else if (VerilatedVpioConst* vop = VerilatedVpioConst::castp(object)) {
} else if (const VerilatedVpioConst* const vop = VerilatedVpioConst::castp(object)) {
if (valuep->format == vpiIntVal) {
valuep->value.integer = vop->num();
return;
@@ -1920,7 +1926,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_put_value with nullptr value pointer");
return nullptr;
}
if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) {
if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) {
VL_DEBUG_IF_PLI(
VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n", vop->fullname(),
valuep->format, valuep->value.integer);
@@ -1953,7 +1959,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
valuep->value.vector[1].aval & vop->mask(), valuep->value.vector[0].aval);
return object;
} else if (vop->varp()->vltype() == VLVT_WDATA) {
int words = VL_WORDS_I(vop->varp()->packed().elements());
const int words = VL_WORDS_I(vop->varp()->packed().elements());
WDataOutP datap = (reinterpret_cast<EData*>(vop->varDatap()));
for (int i = 0; i < words; ++i) {
datap[i] = valuep->value.vector[i].aval;
@@ -1962,11 +1968,11 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
return object;
}
} else if (valuep->format == vpiBinStrVal) {
int bits = vop->varp()->packed().elements();
int len = std::strlen(valuep->value.str);
CData* datap = (reinterpret_cast<CData*>(vop->varDatap()));
const int bits = vop->varp()->packed().elements();
const int len = std::strlen(valuep->value.str);
CData* const datap = (reinterpret_cast<CData*>(vop->varDatap()));
for (int i = 0; i < bits; ++i) {
char set = (i < len) ? (valuep->value.str[len - i - 1] == '1') : 0;
const char set = (i < len) ? (valuep->value.str[len - i - 1] == '1') : 0;
// zero bits 7:1 of byte when assigning to bit 0, else
// or in 1 if bit set
if (i & 7) {
@@ -1977,10 +1983,10 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
}
return object;
} else if (valuep->format == vpiOctStrVal) {
int chars = (vop->varp()->packed().elements() + 2) / 3;
int bytes = VL_BYTES_I(vop->varp()->packed().elements());
int len = std::strlen(valuep->value.str);
CData* datap = (reinterpret_cast<CData*>(vop->varDatap()));
const int chars = (vop->varp()->packed().elements() + 2) / 3;
const int bytes = VL_BYTES_I(vop->varp()->packed().elements());
const int len = std::strlen(valuep->value.str);
CData* const datap = (reinterpret_cast<CData*>(vop->varDatap()));
div_t idx;
datap[0] = 0; // reset zero'th byte
for (int i = 0; i < chars; ++i) {
@@ -1991,7 +1997,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
idx = div(i * 3, 8);
if (i < len) {
// ignore illegal chars
char digit = valuep->value.str[len - i - 1];
const char digit = valuep->value.str[len - i - 1];
if (digit >= '0' && digit <= '7') {
val.half = digit - '0';
} else {
@@ -2029,7 +2035,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
} else if (valuep->format == vpiDecStrVal) {
char remainder[16];
unsigned long long val;
int success = std::sscanf(valuep->value.str, "%30llu%15s", &val, remainder);
const int success = std::sscanf(valuep->value.str, "%30llu%15s", &val, remainder);
if (success < 1) {
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Parsing failed for '%s' as value %s for %s",
__func__, valuep->value.str,
@@ -2057,17 +2063,17 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
return object;
}
} else if (valuep->format == vpiHexStrVal) {
int chars = (vop->varp()->packed().elements() + 3) >> 2;
CData* datap = (reinterpret_cast<CData*>(vop->varDatap()));
char* val = valuep->value.str;
const int chars = (vop->varp()->packed().elements() + 3) >> 2;
CData* const datap = (reinterpret_cast<CData*>(vop->varDatap()));
const char* val = valuep->value.str;
// skip hex ident if one is detected at the start of the string
if (val[0] == '0' && (val[1] == 'x' || val[1] == 'X')) val += 2;
int len = std::strlen(val);
const int len = std::strlen(val);
for (int i = 0; i < chars; ++i) {
char hex;
// compute hex digit value
if (i < len) {
char digit = val[len - i - 1];
const char digit = val[len - i - 1];
if (digit >= '0' && digit <= '9') {
hex = digit - '0';
} else if (digit >= 'a' && digit <= 'f') {
@@ -2097,9 +2103,9 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
datap[(chars - 1) >> 1] &= vop->mask_byte((chars - 1) >> 1);
return object;
} else if (valuep->format == vpiStringVal) {
int bytes = VL_BYTES_I(vop->varp()->packed().elements());
int len = std::strlen(valuep->value.str);
CData* datap = (reinterpret_cast<CData*>(vop->varDatap()));
const int bytes = VL_BYTES_I(vop->varp()->packed().elements());
const int len = std::strlen(valuep->value.str);
CData* const datap = (reinterpret_cast<CData*>(vop->varDatap()));
for (int i = 0; i < bytes; ++i) {
// prepend with 0 values before placing string the least significant bytes
datap[i] = (i < len) ? valuep->value.str[len - i - 1] : 0;
@@ -2120,11 +2126,11 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname());
return nullptr;
} else if (VerilatedVpioParam* vop = VerilatedVpioParam::castp(object)) {
} else if (const VerilatedVpioParam* const vop = VerilatedVpioParam::castp(object)) {
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Ignoring vpi_put_value to vpiParameter: %s",
__func__, vop->fullname());
return nullptr;
} else if (VerilatedVpioConst* vop = VerilatedVpioConst::castp(object)) {
} else if (const VerilatedVpioConst* const vop = VerilatedVpioConst::castp(object)) {
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Ignoring vpi_put_value to vpiConstant: %s",
__func__, vop->fullname());
return nullptr;
@@ -2153,18 +2159,18 @@ void vpi_get_time(vpiHandle object, p_vpi_time time_p) {
return;
}
if (time_p->type == vpiSimTime) {
QData qtime = VL_TIME_Q();
WData itime[2];
const QData qtime = VL_TIME_Q();
VlWide<2> itime;
VL_SET_WQ(itime, qtime);
time_p->low = itime[0];
time_p->high = itime[1];
return;
} else if (time_p->type == vpiScaledRealTime) {
double dtime = VL_TIME_D();
if (VerilatedVpioScope* vop = VerilatedVpioScope::castp(object)) {
int scalePow10
if (const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object)) {
const int scalePow10
= Verilated::threadContextp()->timeprecision() - vop->scopep()->timeunit();
double scale = vl_time_multiplier(scalePow10); // e.g. 0.0001
const double scale = vl_time_multiplier(scalePow10); // e.g. 0.0001
dtime *= scale;
}
time_p->real = dtime;
@@ -2198,7 +2204,7 @@ PLI_INT32 vpi_mcd_printf(PLI_UINT32 mcd, PLI_BYTE8* formatp, ...) {
VL_VPI_ERROR_RESET_();
va_list ap;
va_start(ap, formatp);
int chars = vpi_mcd_vprintf(mcd, formatp, ap);
const int chars = vpi_mcd_vprintf(mcd, formatp, ap);
va_end(ap);
return chars;
}
@@ -2208,7 +2214,7 @@ PLI_INT32 vpi_printf(PLI_BYTE8* formatp, ...) {
VL_VPI_ERROR_RESET_();
va_list ap;
va_start(ap, formatp);
int chars = vpi_vprintf(formatp, ap);
const int chars = vpi_vprintf(formatp, ap);
va_end(ap);
return chars;
}
@@ -2221,11 +2227,11 @@ PLI_INT32 vpi_vprintf(PLI_BYTE8* formatp, va_list ap) {
PLI_INT32 vpi_mcd_vprintf(PLI_UINT32 mcd, PLI_BYTE8* format, va_list ap) {
VerilatedVpiImp::assertOneCheck();
FILE* fp = VL_CVT_I_FP(mcd);
FILE* const fp = VL_CVT_I_FP(mcd);
VL_VPI_ERROR_RESET_();
// cppcheck-suppress nullPointer
if (VL_UNLIKELY(!fp)) return 0;
int chars = vfprintf(fp, format, ap);
const int chars = vfprintf(fp, format, ap);
return chars;
}
@@ -2238,7 +2244,7 @@ PLI_INT32 vpi_flush(void) {
PLI_INT32 vpi_mcd_flush(PLI_UINT32 mcd) {
VerilatedVpiImp::assertOneCheck();
FILE* fp = VL_CVT_I_FP(mcd);
FILE* const fp = VL_CVT_I_FP(mcd);
VL_VPI_ERROR_RESET_();
if (VL_UNLIKELY(!fp)) return 1;
std::fflush(fp);
@@ -2255,7 +2261,7 @@ PLI_INT32 vpi_chk_error(p_vpi_error_info error_info_p) {
// executing vpi_chk_error does not reset error
// error_info_p can be nullptr, so only return level in that case
VerilatedVpiImp::assertOneCheck();
p_vpi_error_info _error_info_p = VerilatedVpiImp::error_info()->getError();
p_vpi_error_info const _error_info_p = VerilatedVpiImp::error_info()->getError();
if (error_info_p && _error_info_p) *error_info_p = *_error_info_p;
if (!_error_info_p) return 0; // no error occured
return _error_info_p->level; // return error severity level
@@ -2271,7 +2277,7 @@ PLI_INT32 vpi_free_object(vpiHandle object) {
PLI_INT32 vpi_release_handle(vpiHandle object) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_release_handle %p\n", object););
VerilatedVpiImp::assertOneCheck();
VerilatedVpio* vop = VerilatedVpio::castp(object);
VerilatedVpio* const vop = VerilatedVpio::castp(object);
VL_VPI_ERROR_RESET_();
if (VL_UNLIKELY(!vop)) return 0;
VL_DO_DANGLING(delete vop, vop);
@@ -2281,7 +2287,7 @@ PLI_INT32 vpi_release_handle(vpiHandle object) {
PLI_INT32 vpi_get_vlog_info(p_vpi_vlog_info vlog_info_p) VL_MT_SAFE {
VerilatedVpiImp::assertOneCheck();
VL_VPI_ERROR_RESET_();
auto argc_argv = Verilated::threadContextp()->impp()->argc_argv();
const auto argc_argv = Verilated::threadContextp()->impp()->argc_argv();
vlog_info_p->argc = argc_argv.first;
vlog_info_p->argv = argc_argv.second;
vlog_info_p->product = const_cast<PLI_BYTE8*>(Verilated::productName());
+1 -1
View File
@@ -47,7 +47,7 @@ public:
static bool callValueCbs() VL_MT_UNSAFE_ONE;
/// Call callbacks of arbitrary types.
/// User wrapper code should call this from their main loops.
static bool callCbs(vluint32_t reason) VL_MT_UNSAFE_ONE;
static bool callCbs(const vluint32_t reason) VL_MT_UNSAFE_ONE;
/// Returns time of the next registered VPI callback, or
/// ~(0ULL) if none are registered
static QData cbNextDeadline() VL_MT_UNSAFE_ONE;
+10 -3
View File
@@ -333,7 +333,8 @@ typedef signed __int32 ssize_t; ///< signed size_t; returned from read()
# include <stdint.h> // Linux and most flavors
# include <sys/types.h> // __WORDSIZE
# include <unistd.h> // ssize_t
typedef char vlsint8_t; ///< 8-bit signed type
// Arm64 gcc 9.3.0 defaults to unsigned char, not signed char
typedef signed char vlsint8_t; ///< 8-bit signed type
typedef uint8_t vluint8_t; ///< 8-bit unsigned type
typedef short vlsint16_t; ///< 16-bit signed type
typedef uint16_t vluint16_t; ///< 16-bit unsigned type
@@ -421,7 +422,8 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
// Verilated function size macros
#define VL_MULS_MAX_WORDS 16 ///< Max size in words of MULS operation
#define VL_TO_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation
#define VL_VALUE_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation
#define VL_VALUE_STRING_MAX_CHARS (VL_VALUE_STRING_MAX_WORDS * VL_EDATASIZE / VL_BYTESIZE)
//=========================================================================
// Base macros
@@ -471,7 +473,12 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
(val) = ((vluint64_t)lo) | (((vluint64_t)hi) << 32); \
}
#elif defined(__aarch64__)
# define VL_RDTSC(val) asm volatile("mrs %[rt],PMCCNTR_EL0" : [rt] "=r"(val));
// 1 GHz virtual system timer on SBSA level 5 compliant systems, else often 100 MHz
# define VL_RDTSC(val) \
{ \
asm volatile("isb" : : : "memory"); \
asm volatile("mrs %[rt],CNTVCT_EL0" : [rt] "=r"(val)); \
}
#else
// We just silently ignore unknown OSes, as only leads to missing statistics
# define VL_RDTSC(val) (val) = 0;
+1 -17
View File
@@ -38,23 +38,7 @@ def test():
ci_fold_start("configure")
print("Stage 1: configure (coverage on)")
run("autoconf")
cxx_flags = (
"--coverage"
# Otherwise inline may not show as uncovered
# If we use this then e.g. verilated.h functions properly show up
# if unused.
# However, VerilatedSerialize::write then changes from covered
# to uncovered (in G++ 9.3.0) even with all inlining turned off.
# Having false negative coverage is more effort then missing negatives.
# Also this seems to explode the runtime (since a lot more data).
# + " -fkeep-inline-functions"
# Otherwise static may not show as uncovered
+ " -fkeep-static-functions"
# Exceptions can pollute the branch coverage data
+ " -fno-exceptions"
# Define-out some impossible stuff
+ " -DVL_GCOV")
run("./configure --enable-longtests CXX='g++ " + cxx_flags + "'")
run("./configure --enable-longtests --enable-coverage CXX=g++")
ci_fold_end()
if Args.stage_enabled[2]:
-4
View File
@@ -23,10 +23,6 @@ source_globs("include/*/*.c")
# Note *'s are removed when using fastcov
remove_source("/usr/*")
remove_source("*/include/sysc/*")
remove_source("*/V3ClkGater.cpp")
remove_source("*/V3ClkGater.h")
remove_source("*/V3GraphDfa.cpp")
remove_source("*/V3GraphDfa.h")
remove_source("*/V3Lexer_pregen.yy.cpp")
remove_source("*/V3PreLex_pregen.yy.cpp")
remove_source("*/verilog.c")
+2 -2
View File
@@ -66,11 +66,11 @@ def cwrite(filename):
fh.write(" auto* gp = &m_graph;\n")
for ver in sorted(Vertexes, key=lambda ver: ver['num']):
fh.write(
" auto* %s = new V3GraphTestVertex(gp, \"%s\"); if (%s) {}\n"
" auto* %s = new V3GraphTestVertex{gp, \"%s\"}; if (%s) {}\n"
% (ver['name'], ver['name'], ver['name']))
fh.write("\n")
for edge in Edges:
fh.write(" new V3GraphEdge(gp, %s, %s, %s, %s);\n" %
fh.write(" new V3GraphEdge{gp, %s, %s, %s, %s};\n" %
(edge['from'], edge['to'], edge['weight'],
"true" if edge['cutable'] else "false"))
fh.write("}\n")
+13 -10
View File
@@ -77,13 +77,11 @@ TGT = ../../verilator_bin$(EXEEXT)
#################
ifeq ($(VL_DEBUG),)
# Optimize
COPT = -O2
CPPFLAGS += -O2
else
# Debug
COPT = -ggdb -DVL_DEBUG -D_GLIBCXX_DEBUG
# Debug & Profile:
#LDFLAGS += -pg -g
#COPT = -ggdb -pg -g
CPPFLAGS += @CFG_CXXFLAGS_DEBUG@ -DVL_DEBUG -D_GLIBCXX_DEBUG
LDFLAGS += @CFG_LDFLAGS_DEBUG@
endif
#################
@@ -96,7 +94,6 @@ LIBS = $(CFG_LIBS) -lm
CPPFLAGS += -MMD
CPPFLAGS += -I. -I$(bldsrc) -I$(srcdir) -I$(incdir) -I../../include
#CPPFLAGS += -DVL_LEAK_CHECKS # If running valgrind or other hunting tool
CPPFLAGS += $(COPT)
CPPFLAGS += -MP # Only works on recent GCC versions
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
CPPFLAGS += -W -Wall $(CFG_CXXFLAGS_WEXTRA) $(CFG_CXXFLAGS_SRC) -Werror
@@ -172,6 +169,7 @@ RAW_OBJS = \
V3Clean.o \
V3Clock.o \
V3Combine.o \
V3Common.o \
V3Config.o \
V3Const__gen.o \
V3Coverage.o \
@@ -182,11 +180,16 @@ RAW_OBJS = \
V3DepthBlock.o \
V3Descope.o \
V3DupFinder.o \
V3EmitC.o \
V3EmitCBase.o \
V3EmitCConstPool.o \
V3EmitCFunc.o \
V3EmitCHeaders.o \
V3EmitCImp.o \
V3EmitCInlines.o \
V3EmitCSyms.o \
V3EmitCMake.o \
V3EmitCMain.o \
V3EmitCMake.o \
V3EmitCModel.o \
V3EmitCSyms.o \
V3EmitMk.o \
V3EmitV.o \
V3EmitXml.o \
@@ -200,7 +203,6 @@ RAW_OBJS = \
V3Graph.o \
V3GraphAlg.o \
V3GraphAcyc.o \
V3GraphDfa.o \
V3GraphPathChecker.o \
V3GraphTest.o \
V3Hash.o \
@@ -253,6 +255,7 @@ RAW_OBJS = \
V3Undriven.o \
V3Unknown.o \
V3Unroll.o \
V3VariableOrder.o \
V3Waiver.o \
V3Width.o \
V3WidthSel.o \
+46 -47
View File
@@ -50,8 +50,8 @@ public:
enum VertexType : uint8_t { VT_BLOCK, VT_BRANCH, VT_OUTPUT };
private:
string m_name; // Only used for .dot file generation
VertexType m_type; // Vertex type (BLOCK/BRANCH/OUTPUT)
const string m_name; // Only used for .dot file generation
const VertexType m_type; // Vertex type (BLOCK/BRANCH/OUTPUT)
string typestr() const { // "
switch (m_type) {
@@ -64,11 +64,11 @@ private:
public:
LatchDetectGraphVertex(V3Graph* graphp, const string& name, VertexType type = VT_BLOCK)
: V3GraphVertex(graphp)
, m_name(name)
, m_type(type) {}
virtual string name() const { return m_name + " " + typestr(); }
virtual string dotColor() const { return user() ? "green" : "black"; }
: V3GraphVertex{graphp}
, m_name{name}
, m_type{type} {}
virtual string name() const override { return m_name + " " + typestr(); }
virtual string dotColor() const override { return user() ? "green" : "black"; }
virtual int type() const { return m_type; }
};
@@ -108,9 +108,10 @@ protected:
}
break;
case LatchDetectGraphVertex::VT_BRANCH: // (AND of both sibling)
// A BRANCH vertex always has exactly 2 siblings
LatchDetectGraphVertex* ifp = castVertexp(vertexp->outBeginp()->top());
LatchDetectGraphVertex* elsp = castVertexp(vertexp->outBeginp()->outNextp()->top());
// A BRANCH vertex always has exactly 2 siblings
LatchDetectGraphVertex* const ifp = castVertexp(vertexp->outBeginp()->top());
LatchDetectGraphVertex* const elsp
= castVertexp(vertexp->outBeginp()->outNextp()->top());
result = latchCheckInternal(ifp) && latchCheckInternal(elsp);
break;
}
@@ -120,7 +121,7 @@ protected:
public:
LatchDetectGraph() { clear(); }
~LatchDetectGraph() { clear(); }
virtual ~LatchDetectGraph() override { clear(); }
// ACCESSORS
LatchDetectGraphVertex* currentp() { return m_curVertexp; }
void currentp(LatchDetectGraphVertex* vertex) { m_curVertexp = vertex; }
@@ -142,17 +143,17 @@ public:
// Add a new control path and connect it to its parent
LatchDetectGraphVertex* addPathVertex(LatchDetectGraphVertex* parent, const string& name,
bool branch = false) {
m_curVertexp = new LatchDetectGraphVertex(this, name,
m_curVertexp = new LatchDetectGraphVertex{this, name,
branch ? LatchDetectGraphVertex::VT_BRANCH
: LatchDetectGraphVertex::VT_BLOCK);
new V3GraphEdge(this, parent, m_curVertexp, 1);
: LatchDetectGraphVertex::VT_BLOCK};
new V3GraphEdge{this, parent, m_curVertexp, 1};
return m_curVertexp;
}
// Add a new output variable vertex and store a pointer to it in the user1 field of the
// variables AstNode
LatchDetectGraphVertex* addOutputVertex(AstVarRef* nodep) {
LatchDetectGraphVertex* outVertexp
= new LatchDetectGraphVertex(this, nodep->name(), LatchDetectGraphVertex::VT_OUTPUT);
LatchDetectGraphVertex* const outVertexp
= new LatchDetectGraphVertex{this, nodep->name(), LatchDetectGraphVertex::VT_OUTPUT};
nodep->varp()->user1p(outVertexp);
m_outputs.push_back(nodep);
return outVertexp;
@@ -172,7 +173,7 @@ public:
void latchCheck(AstNode* nodep, bool latch_expected) {
bool latch_detected = false;
for (const auto& vrp : m_outputs) {
LatchDetectGraphVertex* vertp = castVertexp(vrp->varp()->user1p());
LatchDetectGraphVertex* const vertp = castVertexp(vrp->varp()->user1p());
vertp->user(true); // Identify the output vertex we are checking paths _to_
if (!latchCheckInternal(castVertexp(verticesBeginp()))) latch_detected = true;
if (latch_detected && !latch_expected) {
@@ -262,7 +263,7 @@ public:
// Return a sentree in this scope that matches given sense list.
AstActive* activep = nullptr;
AstSenTree* activeSenp = m_activeSens.find(sensesp);
AstSenTree* const activeSenp = m_activeSens.find(sensesp);
if (activeSenp) {
const auto it = m_activeMap.find(activeSenp);
UASSERT(it != m_activeMap.end(), "Corrupt active map");
@@ -271,7 +272,7 @@ public:
// Not found, form a new one
if (!activep) {
AstSenTree* newsenp = sensesp->cloneTree(false);
AstSenTree* const newsenp = sensesp->cloneTree(false);
activep = new AstActive(fl, "sequent", newsenp);
activep->sensesStorep(activep->sensesp());
UINFO(8, " New ACTIVE " << activep << endl);
@@ -298,20 +299,20 @@ private:
// NODE STATE
// Input:
// AstVar::user1p // V2LatchGraphVertex* The vertex handling this node
AstUser1InUse m_inuser1;
const AstUser1InUse m_inuser1;
// STATE
LatchDetectGraph m_graph; // Graph used to detect latches in combo always
// VISITORS
virtual void visit(AstVarRef* nodep) {
AstVar* varp = nodep->varp();
const AstVar* const varp = nodep->varp();
if (nodep->access().isWriteOrRW() && varp->isSignal() && !varp->isUsedLoopIdx()) {
m_graph.addAssignment(nodep);
}
}
virtual void visit(AstNodeIf* nodep) {
if (!nodep->isBoundsCheck()) {
LatchDetectGraphVertex* parentp = m_graph.currentp();
LatchDetectGraphVertex* branchp = m_graph.addPathVertex(parentp, "BRANCH", true);
LatchDetectGraphVertex* const parentp = m_graph.currentp();
LatchDetectGraphVertex* const branchp = m_graph.addPathVertex(parentp, "BRANCH", true);
m_graph.addPathVertex(branchp, "IF");
iterateAndNextNull(nodep->ifsp());
m_graph.addPathVertex(branchp, "ELSE");
@@ -340,9 +341,9 @@ public:
enum CheckType : uint8_t { CT_SEQ, CT_COMBO, CT_INITIAL, CT_LATCH };
private:
CheckType m_check; // Combo logic or other
AstNode* m_alwaysp; // Always we're under
AstNode* m_assignp = nullptr; // In assign
const CheckType m_check; // Combo logic or other
const AstNode* const m_alwaysp; // Always we're under
const AstNode* m_assignp = nullptr; // In assign
// VISITORS
virtual void visit(AstAssignDly* nodep) override {
if (m_check != CT_SEQ) {
@@ -355,7 +356,7 @@ private:
} else if (m_check == CT_LATCH) {
// Suppress. Shouldn't matter that the interior of the latch races
} else if (!(VN_IS(nodep->lhsp(), VarRef)
&& VN_CAST(nodep->lhsp(), VarRef)->varp()->isLatched())) {
&& VN_AS(nodep->lhsp(), VarRef)->varp()->isLatched())) {
nodep->v3warn(COMBDLY, "Delayed assignments (<=) in non-clocked"
" (non flop or latch) block\n"
<< nodep->warnMore()
@@ -363,8 +364,8 @@ private:
// Conversely, we could also suggest latches use delayed assignments, as
// recommended by Cliff Cummings?
}
AstNode* newp = new AstAssign(nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
nodep->rhsp()->unlinkFrBack());
AstNode* const newp = new AstAssign(nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
nodep->rhsp()->unlinkFrBack());
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
@@ -377,7 +378,7 @@ private:
}
}
virtual void visit(AstVarRef* nodep) override {
AstVar* varp = nodep->varp();
const AstVar* const varp = nodep->varp();
if (m_check == CT_SEQ && m_assignp && !varp->isUsedLoopIdx() // Ignore loop indices
&& !varp->isTemp()) {
// Allow turning off warnings on the always, or the variable also
@@ -437,29 +438,29 @@ private:
virtual void visit(AstInitial* nodep) override {
// Relink to IACTIVE, unless already under it
UINFO(4, " INITIAL " << nodep << endl);
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_INITIAL);
AstActive* wantactivep = m_namer.getIActive(nodep->fileline());
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
AstActive* const wantactivep = m_namer.getIActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstAssignAlias* nodep) override {
// Relink to CACTIVE, unless already under it
UINFO(4, " ASSIGNW " << nodep << endl);
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
AstActive* const wantactivep = m_namer.getCActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstAssignW* nodep) override {
// Relink to CACTIVE, unless already under it
UINFO(4, " ASSIGNW " << nodep << endl);
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
AstActive* const wantactivep = m_namer.getCActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstCoverToggle* nodep) override {
// Relink to CACTIVE, unless already under it
UINFO(4, " COVERTOGGLE " << nodep << endl);
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
AstActive* const wantactivep = m_namer.getCActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
@@ -470,15 +471,14 @@ private:
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_INITIAL);
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
if (!m_scopeFinalp) {
m_scopeFinalp = new AstCFunc(
nodep->fileline(), "_final_" + m_namer.scopep()->nameDotless(), m_namer.scopep());
m_scopeFinalp->argTypes(EmitCBaseVisitor::symClassVar());
m_scopeFinalp->addInitsp(
new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign() + "\n"));
m_scopeFinalp->dontCombine(true);
m_scopeFinalp->formCallTree(true);
m_scopeFinalp->isStatic(false);
m_scopeFinalp->isLoose(true);
m_scopeFinalp->slow(true);
m_namer.scopep()->addActivep(m_scopeFinalp);
}
@@ -490,8 +490,7 @@ private:
// METHODS
void visitAlways(AstNode* nodep, AstSenTree* oldsensesp, VAlwaysKwd kwd) {
// Move always to appropriate ACTIVE based on its sense list
if (oldsensesp && oldsensesp->sensesp() && VN_IS(oldsensesp->sensesp(), SenItem)
&& VN_CAST(oldsensesp->sensesp(), SenItem)->isNever()) {
if (oldsensesp && oldsensesp->sensesp() && oldsensesp->sensesp()->isNever()) {
// Never executing. Kill it.
UASSERT_OBJ(!oldsensesp->sensesp()->nextp(), nodep,
"Never senitem should be alone, else the never should be eliminated.");
@@ -541,14 +540,14 @@ private:
// Warn and/or convert any delayed assignments
if (combo && !sequent) {
ActiveLatchCheckVisitor latchvisitor(nodep, kwd);
const ActiveLatchCheckVisitor latchvisitor{nodep, kwd};
if (kwd == VAlwaysKwd::ALWAYS_LATCH) {
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_LATCH);
ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_LATCH};
} else {
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_COMBO);
ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_COMBO};
}
} else if (!combo && sequent) {
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_SEQ);
ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_SEQ};
}
}
virtual void visit(AstAlways* nodep) override {
@@ -579,7 +578,7 @@ private:
}
virtual void visit(AstSenItem* nodep) override {
if (nodep->varrefp()) {
if (AstBasicDType* basicp = nodep->varrefp()->dtypep()->basicp()) {
if (const AstBasicDType* const basicp = nodep->varrefp()->dtypep()->basicp()) {
if (basicp->isEventValue()) {
// Events need to be treated as active high so we only activate on event being
// 1
@@ -621,6 +620,6 @@ public:
void V3Active::activeAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ActiveVisitor visitor(nodep); } // Destruct before checking
{ ActiveVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("active", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+8 -16
View File
@@ -42,22 +42,15 @@ private:
// AstNode::user() bool. True if processed
// Each call to V3Const::constify
// AstNode::user4() Used by V3Const::constify, called below
AstUser1InUse m_inuser1;
const AstUser1InUse m_inuser1;
// STATE
AstTopScope* m_topscopep = nullptr; // Top scope for adding sentrees under
SenTreeFinder m_finder; // Find global sentree's and add them
SenTreeFinder m_finder; // Find global sentree's / add them under the AstTopScope
// METHODS
VL_DEBUG_FUNC; // Declare debug()
// VISITORS
virtual void visit(AstTopScope* nodep) override {
m_topscopep = nodep;
m_finder.init(m_topscopep);
iterateChildren(nodep);
m_topscopep = nullptr;
}
virtual void visit(AstNodeModule* nodep) override {
// Create required actives and add to module
// We can start ordering at a module, or a scope
@@ -68,10 +61,9 @@ private:
UINFO(4, " ACTIVE " << nodep << endl);
// Remove duplicate clocks and such; sensesp() may change!
V3Const::constifyExpensiveEdit(nodep);
AstSenTree* sensesp = nodep->sensesp();
AstSenTree* const sensesp = nodep->sensesp();
UASSERT_OBJ(sensesp, nodep, "nullptr");
if (sensesp->sensesp() && VN_IS(sensesp->sensesp(), SenItem)
&& VN_CAST(sensesp->sensesp(), SenItem)->isNever()) {
if (sensesp->sensesp() && sensesp->sensesp()->isNever()) {
// Never executing. Kill it.
UASSERT_OBJ(!sensesp->sensesp()->nextp(), nodep,
"Never senitem should be alone, else the never should be eliminated.");
@@ -80,16 +72,16 @@ private:
}
// Copy combo tree to settlement tree with duplicated statements
if (sensesp->hasCombo()) {
AstSenTree* newsentreep = new AstSenTree(
AstSenTree* const newsentreep = new AstSenTree(
nodep->fileline(), new AstSenItem(nodep->fileline(), AstSenItem::Settle()));
AstActive* newp = new AstActive(nodep->fileline(), "settle", newsentreep);
AstActive* const newp = new AstActive(nodep->fileline(), "settle", newsentreep);
newp->sensesStorep(newsentreep);
if (nodep->stmtsp()) newp->addStmtsp(nodep->stmtsp()->cloneTree(true));
nodep->addNextHere(newp);
}
// Move the SENTREE for each active up to the global level.
// This way we'll easily see what clock domains are identical
AstSenTree* wantp = m_finder.getSenTree(sensesp);
AstSenTree* const wantp = m_finder.getSenTree(sensesp);
UINFO(4, " lookdone\n");
if (wantp != sensesp) {
// Move the active's contents to the other active
@@ -136,6 +128,6 @@ public:
void V3ActiveTop::activeTopAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ActiveTopVisitor visitor(nodep); } // Destruct before checking
{ ActiveTopVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("activetop", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+46 -45
View File
@@ -20,7 +20,6 @@
#include "V3Global.h"
#include "V3Assert.h"
#include "V3Ast.h"
#include "V3GraphDfa.h"
#include "V3Stats.h"
//######################################################################
@@ -31,11 +30,11 @@ private:
// NODE STATE/TYPES
// Cleared on netlist
// AstNode::user() -> bool. True if processed
AstUser1InUse m_inuser1;
const AstUser1InUse m_inuser1;
// STATE
AstNodeModule* m_modp = nullptr; // Last module
AstBegin* m_beginp = nullptr; // Last begin
const AstBegin* m_beginp = nullptr; // Last begin
unsigned m_monitorNum = 0; // Global $monitor numbering (not per module)
AstVar* m_monitorNumVarp = nullptr; // $monitor number variable
AstVar* m_monitorOffVarp = nullptr; // $monitoroff variable
@@ -56,14 +55,14 @@ private:
nodep->displayType(AstDisplayType::DT_WRITE);
nodep->fmtp()->text(assertDisplayMessage(nodep, prefix, nodep->fmtp()->text()));
// cppcheck-suppress nullPointer
AstNode* timenewp = new AstTime(nodep->fileline(), m_modp->timeunit());
if (AstNode* timesp = nodep->fmtp()->exprsp()) {
AstNode* const timenewp = new AstTime(nodep->fileline(), m_modp->timeunit());
if (AstNode* const timesp = nodep->fmtp()->exprsp()) {
timesp->unlinkFrBackWithNext();
timenewp->addNext(timesp);
}
nodep->fmtp()->addExprsp(timenewp);
if (!nodep->fmtp()->scopeNamep() && nodep->fmtp()->formatScopeTracking()) {
nodep->fmtp()->scopeNamep(new AstScopeName(nodep->fileline()));
nodep->fmtp()->scopeNamep(new AstScopeName{nodep->fileline(), true});
}
}
AstVarRef* newMonitorNumVarRefp(AstNode* nodep, VAccess access) {
@@ -89,8 +88,8 @@ private:
AstNode* newIfAssertOn(AstNode* nodep, bool force) {
// Add a internal if to check assertions are on.
// Don't make this a AND term, as it's unlikely to need to test this.
FileLine* fl = nodep->fileline();
AstNode* newp = new AstIf(
FileLine* const fl = nodep->fileline();
AstNode* const newp = new AstIf(
fl,
(force ? new AstConst(fl, AstConst::BitTrue())
: // If assertions are off, have constant propagation rip them out later
@@ -106,10 +105,10 @@ private:
AstNode* newFireAssertUnchecked(AstNode* nodep, const string& message) {
// Like newFireAssert() but omits the asserts-on check
AstDisplay* dispp = new AstDisplay(nodep->fileline(), AstDisplayType::DT_ERROR, message,
nullptr, nullptr);
AstDisplay* const dispp = new AstDisplay(nodep->fileline(), AstDisplayType::DT_ERROR,
message, nullptr, nullptr);
dispp->fmtp()->timeunit(m_modp->timeunit());
AstNode* bodysp = dispp;
AstNode* const bodysp = dispp;
replaceDisplay(dispp, "%%Error"); // Convert to standard DISPLAY format
bodysp->addNext(new AstStop(nodep->fileline(), true));
return bodysp;
@@ -124,8 +123,8 @@ private:
void newPslAssertion(AstNodeCoverOrAssert* nodep, AstNode* failsp) {
if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name());
AstNode* propp = nodep->propp()->unlinkFrBackWithNext();
AstSenTree* sentreep = nodep->sentreep();
AstNode* const propp = nodep->propp()->unlinkFrBackWithNext();
AstSenTree* const sentreep = nodep->sentreep();
const string& message = nodep->name();
AstNode* passsp = nodep->passsp();
if (passsp) passsp->unlinkFrBackWithNext();
@@ -141,13 +140,13 @@ private:
AstNode* bodysp = nullptr;
bool selfDestruct = false;
AstIf* ifp = nullptr;
if (AstCover* snodep = VN_CAST(nodep, Cover)) {
if (const AstCover* const snodep = VN_CAST(nodep, Cover)) {
++m_statCover;
if (!v3Global.opt.coverageUser()) {
selfDestruct = true;
} else {
// V3Coverage assigned us a bucket to increment.
AstCoverInc* covincp = VN_CAST(snodep->coverincp(), CoverInc);
AstCoverInc* const covincp = VN_AS(snodep->coverincp(), CoverInc);
UASSERT_OBJ(covincp, snodep, "Missing AstCoverInc under assertion");
covincp->unlinkFrBackWithNext(); // next() might have AstAssign for trace
if (message != "") covincp->declp()->comment(message);
@@ -163,7 +162,7 @@ private:
} else {
++m_statAsNotImm;
}
bool force = VN_IS(nodep, AssertIntrinsic);
const bool force = VN_IS(nodep, AssertIntrinsic);
if (passsp) passsp = newIfAssertOn(passsp, force);
if (failsp) failsp = newIfAssertOn(failsp, force);
if (!failsp) failsp = newFireAssertUnchecked(nodep, "'assert' failed.");
@@ -199,13 +198,13 @@ private:
virtual void visit(AstIf* nodep) override {
if (nodep->user1SetOnce()) return;
if (nodep->uniquePragma() || nodep->unique0Pragma()) {
AstNodeIf* ifp = nodep;
const AstNodeIf* ifp = nodep;
AstNode* propp = nullptr;
bool hasDefaultElse = false;
do {
// If this statement ends with 'else if', then nextIf will point to the
// nextIf statement. Otherwise it will be null.
AstNodeIf* nextifp = dynamic_cast<AstNodeIf*>(ifp->elsesp());
const AstNodeIf* const nextifp = dynamic_cast<AstNodeIf*>(ifp->elsesp());
iterateAndNextNull(ifp->condp());
// Recurse into the true case.
@@ -217,7 +216,7 @@ private:
}
// Build a bitmask of the true predicates
AstNode* predp = ifp->condp()->cloneTree(false);
AstNode* const predp = ifp->condp()->cloneTree(false);
if (propp) {
propp = new AstConcat(nodep->fileline(), predp, propp);
} else {
@@ -230,18 +229,19 @@ private:
ifp = nextifp;
} while (ifp);
AstNode* newifp = nodep->cloneTree(false);
bool allow_none = nodep->unique0Pragma();
AstNode* const newifp = nodep->cloneTree(false);
const bool allow_none = nodep->unique0Pragma();
// Empty case means no property
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::BitFalse());
// Note: if this ends with an 'else', then we don't need to validate that one of the
// predicates evaluates to true.
AstNode* ohot = ((allow_none || hasDefaultElse)
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
: static_cast<AstNode*>(new AstOneHot(nodep->fileline(), propp)));
AstIf* checkifp
AstNode* const ohot
= ((allow_none || hasDefaultElse)
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
: static_cast<AstNode*>(new AstOneHot(nodep->fileline(), propp)));
AstIf* const checkifp
= new AstIf(nodep->fileline(), new AstLogNot(nodep->fileline(), ohot),
newFireAssert(nodep, "'unique if' statement violated"), newifp);
checkifp->branchPred(VBranchPred::BP_UNLIKELY);
@@ -258,7 +258,7 @@ private:
if (!nodep->user1SetOnce()) {
bool has_default = false;
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
itemp = VN_CAST(itemp->nextp(), CaseItem)) {
itemp = VN_AS(itemp->nextp(), CaseItem)) {
if (itemp->isDefault()) has_default = true;
}
if (nodep->fullPragma() || nodep->priorityPragma()) {
@@ -279,10 +279,10 @@ private:
} else {
AstNode* propp = nullptr;
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
itemp = VN_CAST(itemp->nextp(), CaseItem)) {
itemp = VN_AS(itemp->nextp(), CaseItem)) {
for (AstNode* icondp = itemp->condsp(); icondp; icondp = icondp->nextp()) {
AstNode* onep;
if (AstInsideRange* rcondp = VN_CAST(icondp, InsideRange)) {
if (AstInsideRange* const rcondp = VN_CAST(icondp, InsideRange)) {
onep = rcondp->newAndFromInside(nodep->exprp(),
rcondp->lhsp()->cloneTree(true),
rcondp->rhsp()->cloneTree(true));
@@ -305,12 +305,12 @@ private:
// Empty case means no property
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::BitFalse());
bool allow_none = has_default || nodep->unique0Pragma();
AstNode* ohot
const bool allow_none = has_default || nodep->unique0Pragma();
AstNode* const ohot
= (allow_none
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
: static_cast<AstNode*>(new AstOneHot(nodep->fileline(), propp)));
AstIf* ifp = new AstIf(
AstIf* const ifp = new AstIf(
nodep->fileline(), new AstLogNot(nodep->fileline(), ohot),
newFireAssert(nodep,
"synthesis parallel_case, but multiple matches found"),
@@ -329,22 +329,22 @@ private:
if (nodep->ticksp()) {
UASSERT_OBJ(VN_IS(nodep->ticksp(), Const), nodep,
"Expected constant ticks, checked in V3Width");
ticks = VN_CAST(nodep->ticksp(), Const)->toUInt();
ticks = VN_AS(nodep->ticksp(), Const)->toUInt();
}
UASSERT_OBJ(ticks >= 1, nodep, "0 tick should have been checked in V3Width");
AstNode* inp = nodep->exprp()->unlinkFrBack();
AstVar* invarp = nullptr;
AstSenTree* sentreep = nodep->sentreep();
AstSenTree* const sentreep = nodep->sentreep();
sentreep->unlinkFrBack();
AstAlways* alwaysp
AstAlways* const alwaysp
= new AstAlways(nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, nullptr);
m_modp->addStmtp(alwaysp);
for (uint32_t i = 0; i < ticks; ++i) {
AstVar* outvarp = new AstVar(nodep->fileline(), AstVarType::MODULETEMP,
"_Vpast_" + cvtToStr(m_modPastNum++) + "_" + cvtToStr(i),
inp->dtypep());
AstVar* const outvarp = new AstVar(
nodep->fileline(), AstVarType::MODULETEMP,
"_Vpast_" + cvtToStr(m_modPastNum++) + "_" + cvtToStr(i), inp->dtypep());
m_modp->addStmtp(outvarp);
AstNode* assp = new AstAssignDly(
AstNode* const assp = new AstAssignDly(
nodep->fileline(), new AstVarRef(nodep->fileline(), outvarp, VAccess::WRITE), inp);
alwaysp->addStmtp(assp);
// if (debug() >= 9) assp->dumpTree(cout, "-ass: ");
@@ -378,15 +378,15 @@ private:
new AstConst{fl, monNum}};
nodep->replaceWith(newsetp);
// Add "always_comb if (__VmonitorOn && __VmonitorNum==N) $display(...);"
AstNode* stmtsp = nodep;
AstIf* ifp = new AstIf{
AstNode* const stmtsp = nodep;
AstIf* const ifp = new AstIf{
fl,
new AstLogAnd{fl, new AstLogNot{fl, newMonitorOffVarRefp(nodep, VAccess::READ)},
new AstEq{fl, new AstConst{fl, monNum},
newMonitorNumVarRefp(nodep, VAccess::READ)}},
stmtsp, nullptr};
ifp->branchPred(VBranchPred::BP_UNLIKELY);
AstNode* newp = new AstAlwaysPostponed{fl, ifp};
AstNode* const newp = new AstAlwaysPostponed{fl, ifp};
m_modp->addStmtp(newp);
} else if (nodep->displayType() == AstDisplayType::DT_STROBE) {
nodep->displayType(AstDisplayType::DT_DISPLAY);
@@ -401,10 +401,11 @@ private:
new AstConst{fl, AstConst::BitTrue{}}};
nodep->replaceWith(newsetp);
// Add "always_comb if (__Vstrobe) begin $display(...); __Vstrobe = '0; end"
AstNode* stmtsp = nodep;
AstIf* ifp = new AstIf{fl, new AstVarRef{fl, varp, VAccess::READ}, stmtsp, nullptr};
AstNode* const stmtsp = nodep;
AstIf* const ifp
= new AstIf{fl, new AstVarRef{fl, varp, VAccess::READ}, stmtsp, nullptr};
ifp->branchPred(VBranchPred::BP_UNLIKELY);
AstNode* newp = new AstAlwaysPostponed{fl, ifp};
AstNode* const newp = new AstAlwaysPostponed{fl, ifp};
stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
new AstConst{fl, AstConst::BitFalse{}}});
m_modp->addStmtp(newp);
@@ -474,6 +475,6 @@ public:
void V3Assert::assertAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ AssertVisitor visitor(nodep); } // Destruct before checking
{ AssertVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("assert", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+11 -11
View File
@@ -97,10 +97,10 @@ private:
virtual void visit(AstFell* nodep) override {
if (nodep->sentreep()) return; // Already processed
iterateChildren(nodep);
FileLine* fl = nodep->fileline();
FileLine* const fl = nodep->fileline();
AstNode* exprp = nodep->exprp()->unlinkFrBack();
if (exprp->width() > 1) exprp = new AstSel(fl, exprp, 0, 1);
AstNode* past = new AstPast(fl, exprp, nullptr);
AstNode* const past = new AstPast(fl, exprp, nullptr);
past->dtypeFrom(exprp);
exprp = new AstAnd(fl, past, new AstNot(fl, exprp->cloneTree(false)));
exprp->dtypeSetBit();
@@ -116,10 +116,10 @@ private:
virtual void visit(AstRose* nodep) override {
if (nodep->sentreep()) return; // Already processed
iterateChildren(nodep);
FileLine* fl = nodep->fileline();
FileLine* const fl = nodep->fileline();
AstNode* exprp = nodep->exprp()->unlinkFrBack();
if (exprp->width() > 1) exprp = new AstSel(fl, exprp, 0, 1);
AstNode* past = new AstPast(fl, exprp, nullptr);
AstNode* const past = new AstPast(fl, exprp, nullptr);
past->dtypeFrom(exprp);
exprp = new AstAnd(fl, new AstNot(fl, past), exprp->cloneTree(false));
exprp->dtypeSetBit();
@@ -130,9 +130,9 @@ private:
virtual void visit(AstStable* nodep) override {
if (nodep->sentreep()) return; // Already processed
iterateChildren(nodep);
FileLine* fl = nodep->fileline();
FileLine* const fl = nodep->fileline();
AstNode* exprp = nodep->exprp()->unlinkFrBack();
AstNode* past = new AstPast(fl, exprp, nullptr);
AstNode* const past = new AstPast(fl, exprp, nullptr);
past->dtypeFrom(exprp);
exprp = new AstEq(fl, past, exprp->cloneTree(false));
exprp->dtypeSetBit();
@@ -144,15 +144,15 @@ private:
virtual void visit(AstImplication* nodep) override {
if (nodep->sentreep()) return; // Already processed
FileLine* fl = nodep->fileline();
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
FileLine* const fl = nodep->fileline();
AstNode* const rhsp = nodep->rhsp()->unlinkFrBack();
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
if (m_disablep) lhsp = new AstAnd(fl, new AstNot(fl, m_disablep), lhsp);
AstNode* past = new AstPast(fl, lhsp, nullptr);
AstNode* const past = new AstPast(fl, lhsp, nullptr);
past->dtypeFrom(lhsp);
AstNode* exprp = new AstOr(fl, new AstNot(fl, past), rhsp);
AstNode* const exprp = new AstOr(fl, new AstNot(fl, past), rhsp);
exprp->dtypeSetBit();
nodep->replaceWith(exprp);
nodep->sentreep(newSenTree(nodep));
@@ -205,6 +205,6 @@ public:
void V3AssertPre::assertPreAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ AssertPreVisitor visitor(nodep); } // Destruct before checking
{ AssertPreVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("assertpre", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+77 -70
View File
@@ -74,9 +74,10 @@ AstNode::AstNode(AstType t, FileLine* fl)
m_clonep = nullptr;
m_cloneCnt = 0;
// Attributes
m_didWidth = false;
m_doingWidth = false;
m_protect = true;
m_flags.didWidth = false;
m_flags.doingWidth = false;
m_flags.protect = true;
m_flags.unused = 0; // Initializing this avoids a read-modify-write on construction
m_user1u = VNUser(0);
m_user1Cnt = 0;
m_user2u = VNUser(0);
@@ -94,7 +95,7 @@ AstNode* AstNode::abovep() const {
// Avoid supporting at other locations as would require walking
// list which is likely to cause performance issues.
UASSERT_OBJ(!m_nextp || firstAbovep(), this, "abovep() not allowed when in midlist");
const AstNode* firstp = firstAbovep() ? this : m_headtailp;
const AstNode* const firstp = firstAbovep() ? this : m_headtailp;
return firstp->backp();
}
@@ -119,7 +120,7 @@ string AstNode::encodeName(const string& namein) {
// a user identifier nor a temp we create in Verilator.
// We also do *NOT* use __DOT__ etc, as we search for those
// in some replacements, and don't want to mangle the user's names.
unsigned val = pos[0] & 0xff; // Mask to avoid sign extension
const unsigned val = pos[0] & 0xff; // Mask to avoid sign extension
std::stringstream hex;
hex << std::setfill('0') << std::setw(2) << std::hex << val;
out += "__0" + hex.str();
@@ -229,7 +230,8 @@ string AstNode::prettyTypeName() const {
//######################################################################
// Insertion
inline void AstNode::debugTreeChange(const char* prefix, int lineno, bool next){
inline void AstNode::debugTreeChange(const AstNode* nodep, const char* prefix, int lineno,
bool next){
#ifdef VL_DEBUG
// Called on all major tree changers.
// Only for use for those really nasty bugs relating to internals
@@ -253,8 +255,8 @@ inline void AstNode::debugTreeChange(const char* prefix, int lineno, bool next){
AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) {
// Add to m_nextp, returns this
UDEBUGONLY(UASSERT_OBJ(newp, nodep, "Null item passed to addNext"););
nodep->debugTreeChange("-addNextThs: ", __LINE__, false);
newp->debugTreeChange("-addNextNew: ", __LINE__, true);
debugTreeChange(nodep, "-addNextThs: ", __LINE__, false);
debugTreeChange(newp, "-addNextNew: ", __LINE__, true);
if (!nodep) { // verilog.y and lots of other places assume this
return newp;
} else {
@@ -275,8 +277,8 @@ AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) {
oldtailp->m_nextp = newp;
newp->m_backp = oldtailp;
// New tail needs the head
AstNode* newtailp = newp->m_headtailp;
AstNode* headp = oldtailp->m_headtailp;
AstNode* const newtailp = newp->m_headtailp;
AstNode* const headp = oldtailp->m_headtailp;
oldtailp->m_headtailp = nullptr; // May be written again as new head
newp->m_headtailp = nullptr; // May be written again as new tail
newtailp->m_headtailp = headp;
@@ -284,7 +286,7 @@ AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) {
newp->editCountInc();
if (oldtailp->m_iterpp) *(oldtailp->m_iterpp) = newp; // Iterate on new item
}
nodep->debugTreeChange("-addNextOut:", __LINE__, true);
debugTreeChange(nodep, "-addNextOut:", __LINE__, true);
return nodep;
}
@@ -299,15 +301,15 @@ void AstNode::addNextHere(AstNode* newp) {
// New could be head of single node, or list
UASSERT(newp, "Null item passed to addNext");
UASSERT(!newp->backp(), "New node (back) already assigned?");
this->debugTreeChange("-addHereThs: ", __LINE__, false);
newp->debugTreeChange("-addHereNew: ", __LINE__, true);
debugTreeChange(this, "-addHereThs: ", __LINE__, false);
debugTreeChange(newp, "-addHereNew: ", __LINE__, true);
newp->editCountInc();
AstNode* addlastp = newp->m_headtailp; // Last node in list to be added
AstNode* const addlastp = newp->m_headtailp; // Last node in list to be added
UASSERT(!addlastp->m_nextp, "Headtailp tail isn't at the tail");
// Forward links
AstNode* oldnextp = this->m_nextp;
AstNode* const oldnextp = this->m_nextp;
this->m_nextp = newp;
addlastp->m_nextp = oldnextp; // Perhaps null if 'this' is not list
@@ -316,7 +318,7 @@ void AstNode::addNextHere(AstNode* newp) {
newp->m_backp = this;
// Head/tail
AstNode* oldheadtailp = this->m_headtailp;
AstNode* const oldheadtailp = this->m_headtailp;
// (!oldheadtailp) // this was&is middle of list
// (oldheadtailp==this && !oldnext)// this was head AND tail (one node long list)
// (oldheadtailp && oldnextp) // this was&is head of list of not just one node, not
@@ -338,7 +340,7 @@ void AstNode::addNextHere(AstNode* newp) {
}
if (this->m_iterpp) *(this->m_iterpp) = newp; // Iterate on new item
this->debugTreeChange("-addHereOut: ", __LINE__, true);
debugTreeChange(this, "-addHereOut: ", __LINE__, true);
}
void AstNode::setOp1p(AstNode* newp) {
@@ -346,12 +348,12 @@ void AstNode::setOp1p(AstNode* newp) {
UDEBUGONLY(UASSERT_OBJ(!m_op1p, this, "Adding to non-empty, non-list op1"););
UDEBUGONLY(UASSERT_OBJ(!newp->m_backp, newp, "Adding already linked node"););
UDEBUGONLY(UASSERT_OBJ(!newp->m_nextp, newp, "Adding list to non-list op1"););
this->debugTreeChange("-setOp1pThs: ", __LINE__, false);
newp->debugTreeChange("-setOp1pNew: ", __LINE__, true);
debugTreeChange(this, "-setOp1pThs: ", __LINE__, false);
debugTreeChange(newp, "-setOp1pNew: ", __LINE__, true);
m_op1p = newp;
newp->editCountInc();
newp->m_backp = this;
this->debugTreeChange("-setOp1pOut: ", __LINE__, false);
debugTreeChange(this, "-setOp1pOut: ", __LINE__, false);
}
void AstNode::setOp2p(AstNode* newp) {
@@ -359,12 +361,12 @@ void AstNode::setOp2p(AstNode* newp) {
UDEBUGONLY(UASSERT_OBJ(!m_op2p, this, "Adding to non-empty, non-list op2"););
UDEBUGONLY(UASSERT_OBJ(!newp->m_backp, newp, "Adding already linked node"););
UDEBUGONLY(UASSERT_OBJ(!newp->m_nextp, newp, "Adding list to non-list op2"););
this->debugTreeChange("-setOp2pThs: ", __LINE__, false);
newp->debugTreeChange("-setOp2pNew: ", __LINE__, true);
debugTreeChange(this, "-setOp2pThs: ", __LINE__, false);
debugTreeChange(newp, "-setOp2pNew: ", __LINE__, true);
m_op2p = newp;
newp->editCountInc();
newp->m_backp = this;
this->debugTreeChange("-setOp2pOut: ", __LINE__, false);
debugTreeChange(this, "-setOp2pOut: ", __LINE__, false);
}
void AstNode::setOp3p(AstNode* newp) {
@@ -372,12 +374,12 @@ void AstNode::setOp3p(AstNode* newp) {
UDEBUGONLY(UASSERT_OBJ(!m_op3p, this, "Adding to non-empty, non-list op3"););
UDEBUGONLY(UASSERT_OBJ(!newp->m_backp, newp, "Adding already linked node"););
UDEBUGONLY(UASSERT_OBJ(!newp->m_nextp, newp, "Adding list to non-list op3"););
this->debugTreeChange("-setOp3pThs: ", __LINE__, false);
newp->debugTreeChange("-setOp3pNew: ", __LINE__, true);
debugTreeChange(this, "-setOp3pThs: ", __LINE__, false);
debugTreeChange(newp, "-setOp3pNew: ", __LINE__, true);
m_op3p = newp;
newp->editCountInc();
newp->m_backp = this;
this->debugTreeChange("-setOp3pOut: ", __LINE__, false);
debugTreeChange(this, "-setOp3pOut: ", __LINE__, false);
}
void AstNode::setOp4p(AstNode* newp) {
@@ -385,12 +387,12 @@ void AstNode::setOp4p(AstNode* newp) {
UDEBUGONLY(UASSERT_OBJ(!m_op4p, this, "Adding to non-empty, non-list op4"););
UDEBUGONLY(UASSERT_OBJ(!newp->m_backp, newp, "Adding already linked node"););
UDEBUGONLY(UASSERT_OBJ(!newp->m_nextp, newp, "Adding list to non-list op4"););
this->debugTreeChange("-setOp4pThs: ", __LINE__, false);
newp->debugTreeChange("-setOp4pNew: ", __LINE__, true);
debugTreeChange(this, "-setOp4pThs: ", __LINE__, false);
debugTreeChange(newp, "-setOp4pNew: ", __LINE__, true);
m_op4p = newp;
newp->editCountInc();
newp->m_backp = this;
this->debugTreeChange("-setOp4pOut: ", __LINE__, false);
debugTreeChange(this, "-setOp4pOut: ", __LINE__, false);
}
void AstNode::addOp1p(AstNode* newp) {
@@ -452,11 +454,11 @@ void AstNRelinker::dump(std::ostream& str) const {
}
AstNode* AstNode::unlinkFrBackWithNext(AstNRelinker* linkerp) {
this->debugTreeChange("-unlinkWNextThs: ", __LINE__, true);
AstNode* oldp = this;
debugTreeChange(this, "-unlinkWNextThs: ", __LINE__, true);
AstNode* const oldp = this;
UASSERT(oldp->m_backp, "Node has no back, already unlinked?");
oldp->editCountInc();
AstNode* backp = oldp->m_backp;
AstNode* const backp = oldp->m_backp;
if (linkerp) {
linkerp->m_oldp = oldp;
linkerp->m_backp = backp;
@@ -485,7 +487,7 @@ AstNode* AstNode::unlinkFrBackWithNext(AstNRelinker* linkerp) {
AstNode* oldtailp = oldp;
while (oldtailp->m_nextp) oldtailp = oldtailp->m_nextp;
// Create new head/tail of old list
AstNode* oldheadp = oldtailp->m_headtailp;
AstNode* const oldheadp = oldtailp->m_headtailp;
oldheadp->m_headtailp = oldp->m_backp;
oldheadp->m_headtailp->m_headtailp = oldheadp;
// Create new head/tail of extracted list
@@ -507,16 +509,16 @@ AstNode* AstNode::unlinkFrBackWithNext(AstNRelinker* linkerp) {
// Iterator fixup
if (oldp->m_iterpp) *(oldp->m_iterpp) = nullptr;
oldp->m_iterpp = nullptr;
oldp->debugTreeChange("-unlinkWNextOut: ", __LINE__, true);
debugTreeChange(oldp, "-unlinkWNextOut: ", __LINE__, true);
return oldp;
}
AstNode* AstNode::unlinkFrBack(AstNRelinker* linkerp) {
this->debugTreeChange("-unlinkFrBkThs: ", __LINE__, true);
AstNode* oldp = this;
debugTreeChange(this, "-unlinkFrBkThs: ", __LINE__, true);
AstNode* const oldp = this;
UASSERT(oldp->m_backp, "Node has no back, already unlinked?");
oldp->editCountInc();
AstNode* backp = oldp->m_backp;
AstNode* const backp = oldp->m_backp;
if (linkerp) {
linkerp->m_oldp = oldp;
linkerp->m_backp = backp;
@@ -558,7 +560,7 @@ AstNode* AstNode::unlinkFrBack(AstNRelinker* linkerp) {
this->v3fatalSrc("Unlink of node with back not pointing to it.");
}
if (oldp->m_nextp) {
AstNode* newheadp = oldp->m_nextp;
AstNode* const newheadp = oldp->m_nextp;
newheadp->m_backp = backp;
newheadp->m_headtailp = oldp->m_headtailp;
newheadp->m_headtailp->m_headtailp = newheadp;
@@ -571,7 +573,7 @@ AstNode* AstNode::unlinkFrBack(AstNRelinker* linkerp) {
oldp->m_backp = nullptr;
oldp->m_headtailp = this;
oldp->m_iterpp = nullptr;
oldp->debugTreeChange("-unlinkFrBkOut: ", __LINE__, true);
debugTreeChange(oldp, "-unlinkFrBkOut: ", __LINE__, true);
return oldp;
}
@@ -580,7 +582,7 @@ void AstNode::relink(AstNRelinker* linkerp) {
UINFO(0, " EDIT: relink: ");
dumpPtrs();
}
AstNode* newp = this;
AstNode* const newp = this;
UASSERT(linkerp && linkerp->m_backp, "Need non-empty linker");
UASSERT(!newp->backp(), "New node already linked?");
newp->editCountInc();
@@ -590,9 +592,9 @@ void AstNode::relink(AstNRelinker* linkerp) {
cout << endl;
}
AstNode* backp = linkerp->m_backp;
this->debugTreeChange("-relinkNew: ", __LINE__, true);
backp->debugTreeChange("-relinkTre: ", __LINE__, true);
AstNode* const backp = linkerp->m_backp;
debugTreeChange(this, "-relinkNew: ", __LINE__, true);
debugTreeChange(backp, "-relinkTre: ", __LINE__, true);
switch (linkerp->m_chg) {
case AstNRelinker::RELINK_NEXT: backp->addNextHere(newp); break;
@@ -617,7 +619,7 @@ void AstNode::relink(AstNRelinker* linkerp) {
}
// Empty the linker so not used twice accidentally
linkerp->m_backp = nullptr;
this->debugTreeChange("-relinkOut: ", __LINE__, true);
debugTreeChange(this, "-relinkOut: ", __LINE__, true);
}
void AstNode::relinkOneLink(AstNode*& pointpr, // Ref to pointer that gets set to newp
@@ -629,10 +631,10 @@ void AstNode::relinkOneLink(AstNode*& pointpr, // Ref to pointer that gets set
// Likewise there may be a old list.
// Insert the whole old list following the new node's list.
// Thus a unlink without next, followed by relink, gives the same list.
AstNode* newlistlastp = newp->m_headtailp;
AstNode* const newlistlastp = newp->m_headtailp;
UASSERT_OBJ(!(newlistlastp->m_nextp && newlistlastp != newp), newp,
"Headtailp tail isn't at the tail");
AstNode* oldlistlastp = pointpr->m_headtailp;
AstNode* const oldlistlastp = pointpr->m_headtailp;
UASSERT_OBJ(!(oldlistlastp->m_nextp && oldlistlastp != pointpr), newp,
"Old headtailp tail isn't at the tail");
// Next links
@@ -669,7 +671,7 @@ void AstNode::swapWith(AstNode* bp) {
AstNode* AstNode::cloneTreeIter() {
// private: Clone single node and children
AstNode* newp = this->clone();
AstNode* const newp = this->clone();
if (this->m_op1p) newp->op1p(this->m_op1p->cloneTreeIterList());
if (this->m_op2p) newp->op2p(this->m_op2p->cloneTreeIterList());
if (this->m_op3p) newp->op3p(this->m_op3p->cloneTreeIterList());
@@ -686,7 +688,7 @@ AstNode* AstNode::cloneTreeIterList() {
AstNode* newtailp = nullptr;
// Audited to make sure this is never nullptr
for (AstNode* oldp = this; oldp; oldp = oldp->m_nextp) {
AstNode* newp = oldp->cloneTreeIter();
AstNode* const newp = oldp->cloneTreeIter();
newp->m_headtailp = nullptr;
newp->m_backp = newtailp;
if (newtailp) newtailp->m_nextp = newp;
@@ -699,7 +701,7 @@ AstNode* AstNode::cloneTreeIterList() {
}
AstNode* AstNode::cloneTree(bool cloneNextLink) {
this->debugTreeChange("-cloneThs: ", __LINE__, cloneNextLink);
debugTreeChange(this, "-cloneThs: ", __LINE__, cloneNextLink);
cloneClearTree();
AstNode* newp;
if (cloneNextLink && this->m_nextp) {
@@ -711,7 +713,7 @@ AstNode* AstNode::cloneTree(bool cloneNextLink) {
}
newp->m_backp = nullptr;
newp->cloneRelinkTree();
newp->debugTreeChange("-cloneOut: ", __LINE__, true);
debugTreeChange(newp, "-cloneOut: ", __LINE__, true);
return newp;
}
@@ -763,7 +765,7 @@ void AstNode::deleteTree() {
// deleteTree always deletes the next link, because you must have called
// unlinkFromBack or unlinkFromBackWithNext as appropriate before calling this.
UASSERT(!m_backp, "Delete called on node with backlink still set");
this->debugTreeChange("-delTree: ", __LINE__, true);
debugTreeChange(this, "-delTree: ", __LINE__, true);
this->editCountInc();
// MUST be depth first!
deleteTreeIter();
@@ -775,14 +777,14 @@ void AstNode::deleteTree() {
#ifdef VL_LEAK_CHECKS
void* AstNode::operator new(size_t size) {
// Optimization note: Aligning to cache line is a loss, due to lost packing
AstNode* objp = static_cast<AstNode*>(::operator new(size));
const AstNode* const objp = static_cast<AstNode*>(::operator new(size));
V3Broken::addNewed(objp);
return objp;
}
void AstNode::operator delete(void* objp, size_t size) {
if (!objp) return;
AstNode* nodep = static_cast<AstNode*>(objp);
const AstNode* const nodep = static_cast<AstNode*>(objp);
V3Broken::deleted(nodep);
::operator delete(objp);
}
@@ -875,7 +877,7 @@ void AstNode::iterateAndNextConst(AstNVisitor& v) {
// Keep following the current list even if edits change it
AstNode* nodep = this;
do {
AstNode* nnextp = nodep->m_nextp;
AstNode* const nnextp = nodep->m_nextp;
ASTNODE_PREFETCH(nnextp);
nodep->accept(v);
nodep = nnextp;
@@ -896,7 +898,7 @@ AstNode* AstNode::iterateSubtreeReturnEdits(AstNVisitor& v) {
} else if (!nodep->backp()) {
// Calling on standalone tree; insert a shim node so we can keep
// track, then delete it on completion
AstBegin* tempp = new AstBegin(nodep->fileline(), "[EditWrapper]", nodep);
AstBegin* const tempp = new AstBegin(nodep->fileline(), "[EditWrapper]", nodep);
{
VL_DO_DANGLING(tempp->stmtsp()->accept(v),
nodep); // nodep to null as may be replaced
@@ -932,7 +934,9 @@ AstNode* AstNode::iterateSubtreeReturnEdits(AstNVisitor& v) {
void AstNode::cloneRelinkTree() {
// private: Cleanup clone() operation on whole tree. Publicly call cloneTree() instead.
for (AstNode* nodep = this; nodep; nodep = nodep->m_nextp) {
if (m_dtypep && m_dtypep->clonep()) m_dtypep = m_dtypep->clonep();
if (nodep->m_dtypep && nodep->m_dtypep->clonep()) {
nodep->m_dtypep = nodep->m_dtypep->clonep();
}
nodep->cloneRelink();
if (nodep->m_op1p) nodep->m_op1p->cloneRelinkTree();
if (nodep->m_op2p) nodep->m_op2p->cloneRelinkTree();
@@ -990,8 +994,8 @@ void AstNode::checkTreeIter(AstNode* backp) {
void AstNode::checkTreeIterList(AstNode* backp) {
// private: Check a (possible) list of nodes, this is always the head of the list
// Audited to make sure this is never nullptr
AstNode* headp = this;
AstNode* tailp = this;
AstNode* const headp = this;
const AstNode* tailp = this;
for (AstNode* nodep = headp; nodep; nodep = nodep->nextp()) {
nodep->checkTreeIter(backp);
UASSERT_OBJ(headp == this || !nextp(), this,
@@ -1045,7 +1049,7 @@ void AstNode::dumpTreeFileGdb(const AstNode* nodep, // LCOV_EXCL_START
cout << "<nullptr>" << endl;
return;
}
string filename = filenamep ? filenamep : v3Global.debugFilename("debug.tree", 98);
const string filename = filenamep ? filenamep : v3Global.debugFilename("debug.tree", 98);
v3Global.rootp()->dumpTreeFile(filename);
} // LCOV_EXCL_STOP
@@ -1121,7 +1125,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo
if (doDump) {
{ // Write log & close
UINFO(2, "Dumping " << filename << endl);
const std::unique_ptr<std::ofstream> logsp(V3File::new_ofstream(filename, append));
const std::unique_ptr<std::ofstream> logsp{V3File::new_ofstream(filename, append)};
if (logsp->fail()) v3fatal("Can't write " << filename);
*logsp << "Verilator Tree Dump (format 0x3900) from <e" << std::dec << editCountLast();
*logsp << "> to <e" << std::dec << editCountGbl() << ">\n";
@@ -1139,7 +1143,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo
checkTree();
// Broken isn't part of check tree because it can munge iterp's
// set by other steps if it is called in the middle of other operations
if (AstNetlist* netp = VN_CAST(this, Netlist)) V3Broken::brokenAll(netp);
if (AstNetlist* const netp = VN_CAST(this, Netlist)) V3Broken::brokenAll(netp);
}
}
@@ -1160,7 +1164,7 @@ string AstNode::locationStr() const {
return ""; // LCOV_EXCL_LINE
}
const AstScope* scopep;
if ((scopep = VN_CAST_CONST(backp, Scope))) {
if ((scopep = VN_CAST(backp, Scope))) {
// The design is flattened and there are no useful scopes
// This is probably because of inlining
if (scopep->isTop()) break;
@@ -1174,11 +1178,11 @@ string AstNode::locationStr() const {
while (backp) {
const AstModule* modp;
const AstNodeVarRef* nvrp;
if ((modp = VN_CAST_CONST(backp, Module)) && !modp->hierName().empty()) {
if ((modp = VN_CAST(backp, Module)) && !modp->hierName().empty()) {
str += modp->hierName();
return str;
} else if ((nvrp = VN_CAST_CONST(backp, NodeVarRef))) {
string prettyName = nvrp->prettyName();
} else if ((nvrp = VN_CAST(backp, NodeVarRef))) {
const string prettyName = nvrp->prettyName();
// VarRefs have not been flattened yet and do not contain location information
if (prettyName != nvrp->name()) {
str += prettyName;
@@ -1261,17 +1265,20 @@ AstNodeDType* AstNode::findBitRangeDType(const VNumRange& range, int widthMin,
AstBasicDType* AstNode::findInsertSameDType(AstBasicDType* nodep) {
return v3Global.rootp()->typeTablep()->findInsertSameDType(nodep);
}
AstNodeDType* AstNode::findVoidDType() const {
return v3Global.rootp()->typeTablep()->findVoidDType(fileline());
AstNodeDType* AstNode::findEmptyQueueDType() const {
return v3Global.rootp()->typeTablep()->findEmptyQueueDType(fileline());
}
AstNodeDType* AstNode::findQueueIndexDType() const {
return v3Global.rootp()->typeTablep()->findQueueIndexDType(fileline());
}
AstNodeDType* AstNode::findVoidDType() const {
return v3Global.rootp()->typeTablep()->findVoidDType(fileline());
}
//######################################################################
// AstNVisitor
// AstNDeleter
void AstNVisitor::doDeletes() {
for (AstNode* nodep : m_deleteps) nodep->deleteTree();
void AstNDeleter::doDeletes() {
for (AstNode* const nodep : m_deleteps) nodep->deleteTree();
m_deleteps.clear();
}
+240 -172
View File
@@ -24,8 +24,10 @@
#include "V3FileLine.h"
#include "V3Number.h"
#include "V3Global.h"
#include "V3Broken.h"
#include <cmath>
#include <type_traits>
#include <unordered_set>
#include "V3Ast__gen_classes.h" // From ./astgen
@@ -49,24 +51,33 @@ using MTaskIdSet = std::set<int>; // Set of mtaskIds for Var sorting
// For broken() function, return error string if have a match
#define BROKEN_RTN(test) \
do { \
if (VL_UNCOVERABLE(test)) return #test; \
if (VL_UNCOVERABLE(test)) return "'" #test "' @ " __FILE__ ":" VL_STRINGIFY(__LINE__); \
} while (false)
// For broken() function, return error string if a base of this class has a match
#define BROKEN_BASE_RTN(test) \
do { \
const char* reasonp = (test); \
const char* const reasonp = (test); \
if (VL_UNCOVERABLE(reasonp)) return reasonp; \
} while (false)
// (V)erilator (N)ode is: True if AstNode is of a a given AstType
#define VN_IS(nodep, nodetypename) (AstNode::privateIs<Ast##nodetypename>(nodep))
// (V)erilator (N)ode is: Returns true if and only if AstNode is of the given AstNode subtype,
// and not nullptr.
#define VN_IS(nodep, nodetypename) (AstNode::privateIs<Ast##nodetypename, decltype(nodep)>(nodep))
// (V)erilator (N)ode cast: Cast to given type if can; effectively
// dynamic_cast<nodetypename>(nodep)
#define VN_CAST(nodep, nodetypename) (AstNode::privateCast<Ast##nodetypename>(nodep))
#define VN_CAST_CONST(nodep, nodetypename) (AstNode::privateConstCast<Ast##nodetypename>(nodep))
// (V)erilator (N)ode cast: Similar to dynamic_cast, but more efficient, use this instead.
// Cast to given type if node is of such type, otherwise returns nullptr. If 'nodep' is nullptr,
// return nullptr. Pointer constness is preserved, i.e.: given a 'const AstNode*',
// a 'const Ast<nodetypename>*' is returned.
#define VN_CAST(nodep, nodetypename) \
(AstNode::privateCast<Ast##nodetypename, decltype(nodep)>(nodep))
// (V)erilator (N)ode deleted: Reference to deleted child (for assertions only)
// (V)erilator (N)ode as: Assert node is of given type then cast to that type. Use this to
// downcast instead of VN_CAST when you know the true type of the node. If 'nodep' is nullptr,
// return nullptr. Pointer constness is preserved, i.e.: given a 'const AstNode*', a 'const
// Ast<nodetypename>*' is returned.
#define VN_AS(nodep, nodetypename) (AstNode::privateAs<Ast##nodetypename, decltype(nodep)>(nodep))
// (V)erilator (N)ode deleted: Pointer to deleted AstNode (for assertions only)
#define VN_DELETED(nodep) VL_UNLIKELY((vluint64_t)(nodep) == 0x1)
//######################################################################
@@ -155,6 +166,7 @@ public:
return (m_e == READWRITE) ? VAccess(m_e) : (m_e == WRITE ? VAccess(READ) : VAccess(WRITE));
}
bool isReadOnly() const { return m_e == READ; } // False with READWRITE
bool isWriteOnly() const { return m_e == WRITE; } // False with READWRITE
bool isReadOrRW() const { return m_e == READ || m_e == READWRITE; }
bool isWriteOrRW() const { return m_e == WRITE || m_e == READWRITE; }
bool isRW() const { return m_e == READWRITE; }
@@ -236,39 +248,6 @@ inline bool operator==(AstPragmaType::en lhs, const AstPragmaType& rhs) { return
//######################################################################
class AstCFuncType final {
public:
enum en : uint8_t {
FT_NORMAL,
TRACE_REGISTER,
TRACE_INIT,
TRACE_INIT_SUB,
TRACE_FULL,
TRACE_FULL_SUB,
TRACE_CHANGE,
TRACE_CHANGE_SUB,
TRACE_CLEANUP
};
enum en m_e;
inline AstCFuncType()
: m_e{FT_NORMAL} {}
// cppcheck-suppress noExplicitConstructor
inline AstCFuncType(en _e)
: m_e{_e} {}
explicit inline AstCFuncType(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
operator en() const { return m_e; }
// METHODS
bool isTrace() const { return m_e != FT_NORMAL; }
};
inline bool operator==(const AstCFuncType& lhs, const AstCFuncType& rhs) {
return lhs.m_e == rhs.m_e;
}
inline bool operator==(const AstCFuncType& lhs, AstCFuncType::en rhs) { return lhs.m_e == rhs; }
inline bool operator==(AstCFuncType::en lhs, const AstCFuncType& rhs) { return lhs == rhs.m_e; }
//######################################################################
class VEdgeType final {
public:
// REMEMBER to edit the strings below too
@@ -457,6 +436,7 @@ public:
// Internal types for mid-steps
SCOPEPTR,
CHARPTR,
MTASKSTATE,
// Unsigned and two state; fundamental types
UINT32,
UINT64,
@@ -467,18 +447,19 @@ public:
};
enum en m_e;
const char* ascii() const {
static const char* const names[] = {
"%E-unk", "bit", "byte", "chandle", "event", "int", "integer",
"logic", "longint", "real", "shortint", "time", "string", "VerilatedScope*",
"char*", "IData", "QData", "LOGIC_IMPLICIT", " MAX"};
static const char* const names[]
= {"%E-unk", "bit", "byte", "chandle", "event",
"int", "integer", "logic", "longint", "real",
"shortint", "time", "string", "VerilatedScope*", "char*",
"VlMTaskState", "IData", "QData", "LOGIC_IMPLICIT", " MAX"};
return names[m_e];
}
const char* dpiType() const {
static const char* const names[]
= {"%E-unk", "svBit", "char", "void*", "char", "int",
"%E-integer", "svLogic", "long long", "double", "short", "%E-time",
"const char*", "dpiScope", "const char*", "IData", "QData", "%E-logic-implicit",
" MAX"};
= {"%E-unk", "svBit", "char", "void*", "char",
"int", "%E-integer", "svLogic", "long long", "double",
"short", "%E-time", "const char*", "dpiScope", "const char*",
"%E-mtaskstate", "IData", "QData", "%E-logic-implct", " MAX"};
return names[m_e];
}
static void selfTest() {
@@ -511,6 +492,7 @@ public:
case STRING: return 64; // opaque // Just the pointer, for today
case SCOPEPTR: return 0; // opaque
case CHARPTR: return 0; // opaque
case MTASKSTATE: return 0; // opaque
case UINT32: return 32;
case UINT64: return 64;
default: return 0;
@@ -522,7 +504,8 @@ public:
}
bool isUnsigned() const {
return m_e == CHANDLE || m_e == EVENTVALUE || m_e == STRING || m_e == SCOPEPTR
|| m_e == CHARPTR || m_e == UINT32 || m_e == UINT64;
|| m_e == CHARPTR || m_e == UINT32 || m_e == UINT64 || m_e == BIT || m_e == LOGIC
|| m_e == TIME;
}
bool isFourstate() const {
return m_e == INTEGER || m_e == LOGIC || m_e == LOGIC_IMPLICIT || m_e == TIME;
@@ -535,9 +518,6 @@ public:
return (m_e == BIT || m_e == BYTE || m_e == INT || m_e == INTEGER || m_e == LOGIC
|| m_e == LONGINT || m_e == SHORTINT || m_e == UINT32 || m_e == UINT64);
}
bool isSloppy() const { // Don't be as anal about width warnings
return !(m_e == LOGIC || m_e == BIT);
}
bool isBitLogic() const { // Bit/logic vector types; can form a packed array
return (m_e == LOGIC || m_e == BIT);
}
@@ -549,11 +529,32 @@ public:
|| m_e == DOUBLE || m_e == SHORTINT || m_e == UINT32 || m_e == UINT64);
}
bool isOpaque() const { // IE not a simple number we can bit optimize
return (m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR || m_e == DOUBLE);
return (m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR || m_e == MTASKSTATE
|| m_e == DOUBLE);
}
bool isDouble() const { return m_e == DOUBLE; }
bool isEventValue() const { return m_e == EVENTVALUE; }
bool isString() const { return m_e == STRING; }
bool isMTaskState() const { return m_e == MTASKSTATE; }
// Does this represent a C++ LiteralType? (can be constexpr)
bool isLiteralType() const {
switch (m_e) {
case BIT:
case BYTE:
case CHANDLE:
case INT:
case INTEGER:
case LOGIC:
case LONGINT:
case DOUBLE:
case SHORTINT:
case SCOPEPTR:
case CHARPTR:
case UINT32:
case UINT64: return true;
default: return false;
}
}
};
inline bool operator==(const AstBasicDTypeKwd& lhs, const AstBasicDTypeKwd& rhs) {
return lhs.m_e == rhs.m_e;
@@ -1007,7 +1008,7 @@ public:
// MEMBERS
void init(int hi, int lo, bool littleEndian) {
if (lo > hi) {
int t = hi;
const int t = hi;
hi = lo;
lo = t;
}
@@ -1026,9 +1027,6 @@ public:
int hiMaxSelect() const {
return (lo() < 0 ? hi() - lo() : hi());
} // Maximum value a [] select may index
bool representableByWidth() const { // Could be represented by just width=1, or [width-1:0]
return (!m_ranged || (m_right == 0 && m_left >= 1));
}
void dump(std::ostream& str) const {
if (ranged()) {
str << "[" << left() << ":" << right() << "]";
@@ -1079,11 +1077,11 @@ inline std::ostream& operator<<(std::ostream& os, const VUseType& rhs) {
class VBasicTypeKey final {
public:
int m_width; // From AstNodeDType: Bit width of operation
int m_widthMin; // From AstNodeDType: If unsized, bitwidth of minimum implementation
VSigning m_numeric; // From AstNodeDType: Node is signed
AstBasicDTypeKwd m_keyword; // From AstBasicDType: What keyword created basic type
VNumRange m_nrange; // From AstBasicDType: Numeric msb/lsb (if non-opaque keyword)
const int m_width; // From AstNodeDType: Bit width of operation
const int m_widthMin; // From AstNodeDType: If unsized, bitwidth of minimum implementation
const VSigning m_numeric; // From AstNodeDType: Node is signed
const AstBasicDTypeKwd m_keyword; // From AstBasicDType: What keyword created basic type
const VNumRange m_nrange; // From AstBasicDType: Numeric msb/lsb (if non-opaque keyword)
bool operator==(const VBasicTypeKey& rhs) const {
return m_width == rhs.m_width && m_widthMin == rhs.m_widthMin && m_numeric == rhs.m_numeric
&& m_keyword == rhs.m_keyword && m_nrange == rhs.m_nrange;
@@ -1136,10 +1134,14 @@ public:
explicit VNUser(void* p) { m_u.up = p; }
~VNUser() = default;
// Casters
WidthVP* c() const { return reinterpret_cast<WidthVP*>(m_u.up); }
VSymEnt* toSymEnt() const { return reinterpret_cast<VSymEnt*>(m_u.up); }
AstNode* toNodep() const { return reinterpret_cast<AstNode*>(m_u.up); }
V3GraphVertex* toGraphVertex() const { return reinterpret_cast<V3GraphVertex*>(m_u.up); }
template <class T> //
typename std::enable_if<std::is_pointer<T>::value, T>::type to() const {
return reinterpret_cast<T>(m_u.up);
}
WidthVP* c() const { return to<WidthVP*>(); }
VSymEnt* toSymEnt() const { return to<VSymEnt*>(); }
AstNode* toNodep() const { return to<AstNode*>(); }
V3GraphVertex* toGraphVertex() const { return to<V3GraphVertex*>(); }
int toInt() const { return m_u.ui; }
static VNUser fromInt(int i) { return VNUser(i); }
};
@@ -1244,31 +1246,39 @@ public:
// clang-format on
//######################################################################
// AstNVisitor -- Allows new functions to be called on each node
// type without changing the base classes. See "Modern C++ Design".
// Node deleter, deletes all enqueued AstNode* on destruction, or when
// explicitly told to do so. This is useful when the deletion of removed
// nodes needs to be deferred to a later time, because pointers to the
// removed nodes might still exist.
class AstNVisitor VL_NOT_FINAL {
private:
class AstNDeleter VL_NOT_FINAL {
// MEMBERS
std::vector<AstNode*> m_deleteps; // Nodes to delete when doDeletes() called
protected:
friend class AstNode;
std::vector<AstNode*> m_deleteps; // Nodes to delete
public:
// METHODS
/// At the end of the visitor (or doDeletes()), delete this pushed node
/// along with all children and next(s). This is often better to use
/// than an immediate deleteTree, as any pointers into this node will
/// persist for the lifetime of the visitor
// Enqueue node for deletion on next 'doDelete' (or destruction)
void pushDeletep(AstNode* nodep) {
UASSERT_STATIC(nodep, "Cannot delete nullptr node");
m_deleteps.push_back(nodep);
}
/// Call deleteTree on all previously pushDeletep()'ed nodes
// Delete all previously pushed nodes (by callint deleteTree)
void doDeletes();
// Do the deletions on destruction
virtual ~AstNDeleter() { doDeletes(); }
};
//######################################################################
// AstNVisitor -- Allows new functions to be called on each node
// type without changing the base classes. See "Modern C++ Design".
class AstNVisitor VL_NOT_FINAL : public AstNDeleter {
friend class AstNode;
public:
virtual ~AstNVisitor() { doDeletes(); }
/// Call visit()s on nodep
void iterate(AstNode* nodep);
/// Call visit()s on nodep
@@ -1356,8 +1366,32 @@ class AstNode VL_NOT_FINAL {
const AstType m_type; // Node sub-type identifier
// ^ ASTNODE_PREFETCH depends on above ordering of members
// padding - 2 extra bytes here after m_type
int m_cloneCnt; // Mark of when userp was set
// AstType is 2 bytes, so we can stick another 6 bytes after it to utilize what would
// otherwise be padding (on a 64-bit system). We stick the attribute flags, broken state,
// and the clone count here.
struct {
bool didWidth : 1; // Did V3Width computation
bool doingWidth : 1; // Inside V3Width
bool protect : 1; // Protect name if protection is on
// Space for more flags here (there must be 8 bits in total)
uint8_t unused : 5;
} m_flags; // Attribute flags
// State variable used by V3Broken for consistency checking. The top bit of this is byte is a
// flag, representing V3Broken is currently proceeding under this node. The bottom 7 bits are
// a generation number. This is hot when --debug-checks so we access as a whole to avoid bit
// field masking resulting in unnecessary read-modify-write ops.
uint8_t m_brokenState = 0;
int m_cloneCnt; // Sequence number for when last clone was made
#if defined(__x86_64__) && defined(__gnu_linux__)
// Only assert this on known platforms, as it only affects performance, not correctness
static_assert(sizeof(m_type) + sizeof(m_flags) + sizeof(m_brokenState) + sizeof(m_cloneCnt)
<= sizeof(void*),
"packing requires padding");
#endif
AstNodeDType* m_dtypep; // Data type of output or assignment (etc)
AstNode* m_headtailp; // When at begin/end of list, the opposite end of the list
@@ -1370,12 +1404,6 @@ class AstNode VL_NOT_FINAL {
AstNode* m_clonep; // Pointer to clone of/ source of this module (for *LAST* cloneTree() ONLY)
static int s_cloneCntGbl; // Count of which userp is set
// Attributes
bool m_didWidth : 1; // Did V3Width computation
bool m_doingWidth : 1; // Inside V3Width
bool m_protect : 1; // Protect name if protection is on
// // Space for more bools here
// This member ordering both allows 64 bit alignment and puts associated data together
VNUser m_user1u; // Contains any information the user iteration routine wants
uint32_t m_user1Cnt; // Mark of when userp was set
@@ -1421,7 +1449,7 @@ private:
public:
static void relinkOneLink(AstNode*& pointpr, AstNode* newp);
// cppcheck-suppress functionConst
void debugTreeChange(const char* prefix, int lineno, bool next);
static void debugTreeChange(const AstNode* nodep, const char* prefix, int lineno, bool next);
protected:
// CONSTRUCTORS
@@ -1478,9 +1506,14 @@ public:
AstNode* firstAbovep() const { // Returns nullptr when second or later in list
return ((backp() && backp()->nextp() != this) ? backp() : nullptr);
}
bool brokeExists() const;
bool brokeExistsAbove() const;
bool brokeExistsBelow() const;
uint8_t brokenState() const { return m_brokenState; }
void brokenState(uint8_t value) { m_brokenState = value; }
// Used by AstNode::broken()
bool brokeExists() const { return V3Broken::isLinkable(this); }
bool brokeExistsAbove() const { return brokeExists() && (m_brokenState >> 7); }
bool brokeExistsBelow() const { return brokeExists() && !(m_brokenState >> 7); }
// Note: brokeExistsBelow is not quite precise, as it is true for sibling nodes as well
// CONSTRUCTORS
virtual ~AstNode() = default;
@@ -1489,21 +1522,20 @@ public:
static void operator delete(void* obj, size_t size);
#endif
// CONSTANT ACCESSORS
static int instrCountBranch() { return 4; } ///< Instruction cycles to branch
static int instrCountDiv() { return 10; } ///< Instruction cycles to divide
static int instrCountDpi() { return 1000; } ///< Instruction cycles to call user function
static int instrCountLd() { return 2; } ///< Instruction cycles to load memory
static int instrCountMul() { return 3; } ///< Instruction cycles to multiply integers
static int instrCountPli() { return 20; } ///< Instruction cycles to call pli routines
static int instrCountDouble() { return 8; } ///< Instruction cycles to convert or do floats
static int instrCountDoubleDiv() { return 40; } ///< Instruction cycles to divide floats
static int instrCountDoubleTrig() { return 200; } ///< Instruction cycles to do trigonomics
static int instrCountString() { return 100; } ///< Instruction cycles to do string ops
/// Instruction cycles to call subroutine
static int instrCountCall() { return instrCountBranch() + 10; }
/// Instruction cycles to determine simulation time
static int instrCountTime() { return instrCountCall() + 5; }
// CONSTANTS
// The following are relative dynamic costs (~ execution cycle count) of various operations.
// They are used by V3InstCount to estimate the relative execution time of code fragments.
static constexpr int INSTR_COUNT_BRANCH = 4; // Branch
static constexpr int INSTR_COUNT_CALL = INSTR_COUNT_BRANCH + 10; // Subroutine call
static constexpr int INSTR_COUNT_LD = 2; // Load memory
static constexpr int INSTR_COUNT_INT_MUL = 3; // Integer multiply
static constexpr int INSTR_COUNT_INT_DIV = 10; // Integer divide
static constexpr int INSTR_COUNT_DBL = 8; // Convert or do float ops
static constexpr int INSTR_COUNT_DBL_DIV = 40; // Double divide
static constexpr int INSTR_COUNT_DBL_TRIG = 200; // Double trigonometric ops
static constexpr int INSTR_COUNT_STR = 100; // String ops
static constexpr int INSTR_COUNT_TIME = INSTR_COUNT_CALL + 5; // Determine simulation time
static constexpr int INSTR_COUNT_PLI = 20; // PLI routines
// ACCESSORS
virtual string name() const { return ""; }
@@ -1534,17 +1566,17 @@ public:
void fileline(FileLine* fl) { m_fileline = fl; }
bool width1() const;
int widthInstrs() const;
void didWidth(bool flag) { m_didWidth = flag; }
bool didWidth() const { return m_didWidth; }
void didWidth(bool flag) { m_flags.didWidth = flag; }
bool didWidth() const { return m_flags.didWidth; }
bool didWidthAndSet() {
if (didWidth()) return true;
didWidth(true);
return false;
}
bool doingWidth() const { return m_doingWidth; }
void doingWidth(bool flag) { m_doingWidth = flag; }
bool protect() const { return m_protect; }
void protect(bool flag) { m_protect = flag; }
bool doingWidth() const { return m_flags.doingWidth; }
void doingWidth(bool flag) { m_flags.doingWidth = flag; }
bool protect() const { return m_flags.protect; }
void protect(bool flag) { m_flags.protect = flag; }
// TODO stomp these width functions out, and call via dtypep() instead
int width() const;
@@ -1678,6 +1710,7 @@ public:
void dtypeSetSigned32() { dtypep(findSigned32DType()); }
void dtypeSetUInt32() { dtypep(findUInt32DType()); } // Twostate
void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate
void dtypeSetEmptyQueue() { dtypep(findEmptyQueueDType()); }
void dtypeSetVoid() { dtypep(findVoidDType()); }
// Data type locators
@@ -1687,6 +1720,8 @@ public:
AstNodeDType* findSigned32DType() { return findBasicDType(AstBasicDTypeKwd::INTEGER); }
AstNodeDType* findUInt32DType() { return findBasicDType(AstBasicDTypeKwd::UINT32); }
AstNodeDType* findUInt64DType() { return findBasicDType(AstBasicDTypeKwd::UINT64); }
AstNodeDType* findCHandleDType() { return findBasicDType(AstBasicDTypeKwd::CHANDLE); }
AstNodeDType* findEmptyQueueDType() const;
AstNodeDType* findVoidDType() const;
AstNodeDType* findQueueIndexDType() const;
AstNodeDType* findBitDType(int width, int widthMin, VSigning numeric) const;
@@ -1695,7 +1730,7 @@ public:
VSigning numeric) const;
AstNodeDType* findBitRangeDType(const VNumRange& range, int widthMin, VSigning numeric) const;
AstNodeDType* findBasicDType(AstBasicDTypeKwd kwd) const;
AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
static AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
// METHODS - dump and error
void v3errorEnd(std::ostringstream& str) const;
@@ -1770,8 +1805,6 @@ public:
virtual bool isGateOptimizable() const { return true; }
// GateDedupable is a slightly larger superset of GateOptimzable (eg, AstNodeIf)
virtual bool isGateDedupable() const { return isGateOptimizable(); }
// Needs verilated_heavy.h (uses std::string or some others)
virtual bool isHeavy() const { return false; }
// Else creates output or exits, etc, not unconsumed
virtual bool isOutputter() const { return false; }
// Else a AstTime etc which output can't be predicted from input
@@ -1817,16 +1850,62 @@ protected:
private:
void iterateListBackwards(AstNVisitor& v);
// CONVERSION
// For internal use only.
template <typename T> inline static bool privateTypeTest(const AstNode* nodep);
template <typename TargetType, typename DeclType> constexpr static bool uselessCast() {
using NonRef = typename std::remove_reference<DeclType>::type;
using NonPtr = typename std::remove_pointer<NonRef>::type;
using NonCV = typename std::remove_cv<NonPtr>::type;
return std::is_base_of<TargetType, NonCV>::value;
}
template <typename TargetType, typename DeclType> constexpr static bool impossibleCast() {
using NonRef = typename std::remove_reference<DeclType>::type;
using NonPtr = typename std::remove_pointer<NonRef>::type;
using NonCV = typename std::remove_cv<NonPtr>::type;
return !std::is_base_of<NonCV, TargetType>::value;
}
public:
// These for use by VN_IS macro only
template <class T> static bool privateIs(const AstNode* nodep);
// For use via the VN_IS macro only
template <typename T, typename E> inline static bool privateIs(const AstNode* nodep) {
static_assert(!uselessCast<T, E>(), "Unnecessary VN_IS, node known to have target type.");
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_IS, node cannot be this type.");
return nodep && privateTypeTest<T>(nodep);
}
// These for use by VN_CAST macro only
template <class T> static T* privateCast(AstNode* nodep);
// For use via the VN_CAST macro only
template <typename T, typename E> inline static T* privateCast(AstNode* nodep) {
static_assert(!uselessCast<T, E>(),
"Unnecessary VN_CAST, node known to have target type.");
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_CAST, node cannot be this type.");
return nodep && privateTypeTest<T>(nodep) ? reinterpret_cast<T*>(nodep) : nullptr;
}
template <typename T, typename E> inline static const T* privateCast(const AstNode* nodep) {
static_assert(!uselessCast<T, E>(),
"Unnecessary VN_CAST, node known to have target type.");
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_CAST, node cannot be this type.");
return nodep && privateTypeTest<T>(nodep) ? reinterpret_cast<const T*>(nodep) : nullptr;
}
// These for use by VN_CAST_CONST macro only
template <class T> static const T* privateConstCast(const AstNode* nodep);
// For use via the VN_AS macro only
template <typename T, typename E> inline static T* privateAs(AstNode* nodep) {
static_assert(!uselessCast<T, E>(), "Unnecessary VN_AS, node known to have target type.");
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_AS, node cannot be this type.");
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
<< "'");
return reinterpret_cast<T*>(nodep);
}
template <typename T, typename E> inline static const T* privateAs(const AstNode* nodep) {
static_assert(!uselessCast<T, E>(), "Unnecessary VN_AS, node known to have target type.");
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_AS, node cannot be this type.");
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
<< "'");
return reinterpret_cast<const T*>(nodep);
}
};
// Specialisations of privateIs/privateCast
@@ -1868,14 +1947,14 @@ public:
virtual void dump(std::ostream& str) const override;
virtual bool hasDType() const override { return true; }
virtual string emitVerilog() = 0; /// Format string for verilog writing; see V3EmitV
// For documentation on emitC format see EmitCStmts::emitOpName
// For documentation on emitC format see EmitCFunc::emitOpName
virtual string emitC() = 0;
virtual string emitSimpleOperator() { return ""; } // "" means not ok to use
virtual bool emitCheckMaxWords() { return false; } // Check VL_MULS_MAX_WORDS
virtual bool cleanOut() const = 0; // True if output has extra upper bits zero
// Someday we will generically support data types on every math node
// Until then isOpaque indicates we shouldn't constant optimize this node type
bool isOpaque() { return VN_IS(this, CvtPackString); }
bool isOpaque() const { return VN_IS(this, CvtPackString); }
};
class AstNodeTermop VL_NOT_FINAL : public AstNodeMath {
@@ -2064,9 +2143,7 @@ public:
AstNode* expr1p() const { return op2p(); } // op2 = If true...
AstNode* expr2p() const { return op3p(); } // op3 = If false...
virtual string emitVerilog() override { return "%k(%l %f? %r %k: %t)"; }
virtual string emitC() override {
return "VL_COND_%nq%lq%rq%tq(%nw,%lw,%rw,%tw, %P, %li, %ri, %ti)";
}
virtual string emitC() override { return "VL_COND_%nq%lq%rq%tq(%nw, %P, %li, %ri, %ti)"; }
virtual bool cleanOut() const override { return false; } // clean if e1 & e2 clean
virtual bool cleanLhs() const override { return true; }
virtual bool cleanRhs() const override { return false; }
@@ -2074,7 +2151,7 @@ public:
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual bool sizeMattersThs() const override { return false; }
virtual int instrCount() const override { return instrCountBranch(); }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual AstNode* cloneType(AstNode* condp, AstNode* expr1p, AstNode* expr2p) = 0;
};
@@ -2119,7 +2196,7 @@ public:
AstNode* fromp() const { return op1p(); }
AstNode* rhsp() const { return op2p(); }
AstNode* thsp() const { return op3p(); }
AstAttrOf* attrp() const { return VN_CAST(op4p(), AttrOf); }
AstAttrOf* attrp() const { return VN_AS(op4p(), AttrOf); }
void fromp(AstNode* nodep) { return setOp1p(nodep); }
void rhsp(AstNode* nodep) { return setOp2p(nodep); }
void thsp(AstNode* nodep) { return setOp3p(nodep); }
@@ -2209,7 +2286,7 @@ public:
AstNode* incsp() const { return op3p(); } // op3 = increment statements
AstNode* bodysp() const { return op4p(); } // op4 = body of loop
virtual bool isGateOptimizable() const override { return false; }
virtual int instrCount() const override { return instrCountBranch(); }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual bool same(const AstNode* samep) const override { return true; }
};
@@ -2236,7 +2313,7 @@ public:
void addElsesp(AstNode* newp) { addOp3p(newp); }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isGateDedupable() const override { return true; }
virtual int instrCount() const override { return instrCountBranch(); }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual bool same(const AstNode* samep) const override { return true; }
void branchPred(VBranchPred flag) { m_branchPred = flag; }
VBranchPred branchPred() const { return m_branchPred; }
@@ -2254,10 +2331,10 @@ protected:
public:
ASTNODE_BASE_FUNCS(NodeCase)
virtual int instrCount() const override { return instrCountBranch(); }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
AstNode* exprp() const { return op1p(); } // op1 = case condition <expression>
AstCaseItem* itemsp() const {
return VN_CAST(op2p(), CaseItem);
return VN_AS(op2p(), CaseItem);
} // op2 = list of case expressions
AstNode* notParallelp() const { return op3p(); } // op3 = assertion code for non-full case's
void addItemsp(AstNode* nodep) { addOp2p(nodep); }
@@ -2272,9 +2349,7 @@ private:
AstVarScope* m_varScopep = nullptr; // Varscope for hierarchy
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
string m_name; // Name of variable
string m_hiernameToProt; // Scope converted into name-> for emitting
string m_hiernameToUnprot; // Scope converted into name-> for emitting
bool m_hierThis = false; // Hiername points to "this" function
string m_selfPointer; // Output code object pointer (e.g.: 'this')
protected:
AstNodeVarRef(AstType t, FileLine* fl, const string& name, const VAccess& access)
@@ -2306,13 +2381,9 @@ public:
void varp(AstVar* varp);
AstVarScope* varScopep() const { return m_varScopep; }
void varScopep(AstVarScope* varscp) { m_varScopep = varscp; }
string hiernameToProt() const { return m_hiernameToProt; }
void hiernameToProt(const string& hn) { m_hiernameToProt = hn; }
string hiernameToUnprot() const { return m_hiernameToUnprot; }
void hiernameToUnprot(const string& hn) { m_hiernameToUnprot = hn; }
string hiernameProtect() const;
bool hierThis() const { return m_hierThis; }
void hierThis(bool flag) { m_hierThis = flag; }
string selfPointer() const { return m_selfPointer; }
void selfPointer(const string& value) { m_selfPointer = value; }
string selfPointerProtect(bool useSelfForThis) const;
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
// Know no children, and hot function, so skip iterator for speed
@@ -2439,6 +2510,7 @@ public:
return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I");
}
string cType(const string& name, bool forFunc, bool isRef) const;
bool isLiteralType() const; // Does this represent a C++ LiteralType? (can be constexpr)
private:
class CTypeRecursed;
@@ -2476,10 +2548,10 @@ public:
// For basicp() we reuse the size to indicate a "fake" basic type of same size
virtual AstBasicDType* basicp() const override {
return (isFourstate()
? VN_CAST(findLogicRangeDType(VNumRange{width() - 1, 0}, width(), numeric()),
BasicDType)
: VN_CAST(findBitRangeDType(VNumRange{width() - 1, 0}, width(), numeric()),
BasicDType));
? VN_AS(findLogicRangeDType(VNumRange{width() - 1, 0}, width(), numeric()),
BasicDType)
: VN_AS(findBitRangeDType(VNumRange{width() - 1, 0}, width(), numeric()),
BasicDType));
}
virtual AstNodeDType* skipRefp() const override { return (AstNodeDType*)this; }
virtual AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
@@ -2495,7 +2567,7 @@ public:
virtual string name() const override { return m_name; }
virtual void name(const string& flag) override { m_name = flag; }
AstMemberDType* membersp() const {
return VN_CAST(op1p(), MemberDType);
return VN_AS(op1p(), MemberDType);
} // op1 = AstMember list
void addMembersp(AstNode* nodep) { addNOp1p(nodep); }
bool packed() const { return m_packed; }
@@ -2538,18 +2610,18 @@ public:
if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep();
}
virtual bool same(const AstNode* samep) const override {
const AstNodeArrayDType* asamep = static_cast<const AstNodeArrayDType*>(samep);
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
return (hi() == asamep->hi() && subDTypep() == asamep->subDTypep()
&& rangenp()->sameTree(asamep->rangenp()));
} // HashedDT doesn't recurse, so need to check children
virtual bool similarDType(AstNodeDType* samep) const override {
const AstNodeArrayDType* asamep = static_cast<const AstNodeArrayDType*>(samep);
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
return (asamep && type() == samep->type() && hi() == asamep->hi()
&& rangenp()->sameTree(asamep->rangenp())
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp()));
}
virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); }
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); }
AstNodeDType* childDTypep() const { return VN_AS(op1p(), NodeDType); }
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
virtual AstNodeDType* subDTypep() const override {
return m_refDTypep ? m_refDTypep : childDTypep();
@@ -2557,7 +2629,7 @@ public:
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
virtual AstNodeDType* virtRefDTypep() const override { return m_refDTypep; }
virtual void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
AstRange* rangep() const { return VN_CAST(op2p(), Range); } // op2 = Array(s) of variable
AstRange* rangep() const { return VN_AS(op2p(), Range); } // op2 = Array(s) of variable
void rangep(AstRange* nodep);
// METHODS
virtual AstBasicDType* basicp() const override {
@@ -2615,8 +2687,6 @@ class AstNodeCCall VL_NOT_FINAL : public AstNodeStmt {
// A call of a C++ function, perhaps a AstCFunc or perhaps globally named
// Functions are not statements, while tasks are. AstNodeStmt needs isStatement() to deal.
AstCFunc* m_funcp;
string m_hiernameToProt;
string m_hiernameToUnprot;
string m_argTypes;
protected:
@@ -2631,9 +2701,9 @@ public:
virtual void dump(std::ostream& str = std::cout) const override;
virtual void cloneRelink() override;
virtual const char* broken() const override;
virtual int instrCount() const override { return instrCountCall(); }
virtual int instrCount() const override { return INSTR_COUNT_CALL; }
virtual bool same(const AstNode* samep) const override {
const AstNodeCCall* asamep = static_cast<const AstNodeCCall*>(samep);
const AstNodeCCall* const asamep = static_cast<const AstNodeCCall*>(samep);
return (funcp() == asamep->funcp() && argTypes() == asamep->argTypes());
}
AstNode* exprsp() const { return op2p(); } // op2 = expressions to print
@@ -2642,11 +2712,6 @@ public:
virtual bool isPure() const override;
virtual bool isOutputter() const override { return !isPure(); }
AstCFunc* funcp() const { return m_funcp; }
string hiernameToProt() const { return m_hiernameToProt; }
void hiernameToProt(const string& hn) { m_hiernameToProt = hn; }
string hiernameToUnprot() const { return m_hiernameToUnprot; }
void hiernameToUnprot(const string& hn) { m_hiernameToUnprot = hn; }
string hiernameProtect() const;
void argTypes(const string& str) { m_argTypes = str; }
string argTypes() const { return m_argTypes; }
// op1p reserved for AstCMethodCall
@@ -2670,11 +2735,13 @@ private:
bool m_dpiContext : 1; // DPI import context
bool m_dpiOpenChild : 1; // DPI import open array child wrapper
bool m_dpiTask : 1; // DPI import task (vs. void function)
bool m_dpiTraceInit : 1; // DPI trace_init
bool m_isConstructor : 1; // Class constructor
bool m_isHideLocal : 1; // Verilog local
bool m_isHideProtected : 1; // Verilog protected
bool m_pure : 1; // DPI import pure (vs. virtual pure)
bool m_pureVirtual : 1; // Pure virtual
bool m_underGenerate : 1; // Under generate (for warning)
bool m_virtual : 1; // Virtual method in class
VLifetime m_lifetime; // Lifetime
protected:
@@ -2692,11 +2759,13 @@ protected:
, m_dpiContext{false}
, m_dpiOpenChild{false}
, m_dpiTask{false}
, m_dpiTraceInit{false}
, m_isConstructor{false}
, m_isHideLocal{false}
, m_isHideProtected{false}
, m_pure{false}
, m_pureVirtual{false}
, m_underGenerate{false}
, m_virtual{false} {
addNOp3p(stmtsp);
cname(name); // Might be overridden by dpi import/export
@@ -2725,7 +2794,7 @@ public:
AstNode* stmtsp() const { return op3p(); } // op3 = List of statements
void addStmtsp(AstNode* nodep) { addNOp3p(nodep); }
// op4 = scope name
AstScopeName* scopeNamep() const { return VN_CAST(op4p(), ScopeName); }
AstScopeName* scopeNamep() const { return VN_AS(op4p(), ScopeName); }
// MORE ACCESSORS
void dpiOpenParentInc() { ++m_dpiOpenParent; }
void dpiOpenParentClear() { m_dpiOpenParent = 0; }
@@ -2753,6 +2822,8 @@ public:
bool dpiOpenChild() const { return m_dpiOpenChild; }
void dpiTask(bool flag) { m_dpiTask = flag; }
bool dpiTask() const { return m_dpiTask; }
void dpiTraceInit(bool flag) { m_dpiTraceInit = flag; }
bool dpiTraceInit() const { return m_dpiTraceInit; }
void isConstructor(bool flag) { m_isConstructor = flag; }
bool isConstructor() const { return m_isConstructor; }
bool isHideLocal() const { return m_isHideLocal; }
@@ -2763,6 +2834,8 @@ public:
bool pure() const { return m_pure; }
void pureVirtual(bool flag) { m_pureVirtual = flag; }
bool pureVirtual() const { return m_pureVirtual; }
void underGenerate(bool flag) { m_underGenerate = flag; }
bool underGenerate() const { return m_underGenerate; }
void isVirtual(bool flag) { m_virtual = flag; }
bool isVirtual() const { return m_virtual; }
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
@@ -2803,7 +2876,6 @@ public:
return m_taskp && m_taskp->isGateOptimizable();
}
string dotted() const { return m_dotted; } // * = Scope name or ""
string prettyDotted() const { return prettyName(dotted()); }
string inlinedDots() const { return m_inlinedDots; }
void inlinedDots(const string& flag) { m_inlinedDots = flag; }
AstNodeFTask* taskp() const { return m_taskp; } // [After Link] Pointer to variable
@@ -2821,7 +2893,7 @@ public:
AstNode* pinsp() const { return op3p(); }
void addPinsp(AstNode* nodep) { addOp3p(nodep); }
// op4 = scope tracking
AstScopeName* scopeNamep() const { return VN_CAST(op4p(), ScopeName); }
AstScopeName* scopeNamep() const { return VN_AS(op4p(), ScopeName); }
void scopeNamep(AstNode* nodep) { setNOp4p(nodep); }
};
@@ -2831,7 +2903,7 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
// excluding $unit package stuff
private:
string m_name; // Name of the module
string m_origName; // Name of the module, ignoring name() changes, for dot lookup
const string m_origName; // Name of the module, ignoring name() changes, for dot lookup
string m_hierName; // Hierarchical name for errors, etc.
bool m_modPublic : 1; // Module has public references
bool m_modTrace : 1; // Tracing this module
@@ -2842,8 +2914,6 @@ private:
bool m_recursive : 1; // Recursive module
bool m_recursiveClone : 1; // If recursive, what module it clones, otherwise nullptr
int m_level = 0; // 1=top module, 2=cell off top module, ...
int m_varNum = 0; // Incrementing variable number
int m_typeNum = 0; // Incrementing implicit type number
VLifetime m_lifetime; // Lifetime
VTimescale m_timeunit; // Global time unit
VOptionBool m_unconnectedDrive; // State of `unconnected_drive
@@ -2868,7 +2938,7 @@ public:
virtual string name() const override { return m_name; }
virtual bool timescaleMatters() const = 0;
AstNode* stmtsp() const { return op2p(); } // op2 = List of statements
AstActive* activesp() const { return VN_CAST(op3p(), Active); } // op3 = List of i/sblocks
AstActive* activesp() const { return VN_AS(op3p(), Active); } // op3 = List of i/sblocks
// METHODS
void addInlinesp(AstNode* nodep) { addOp1p(nodep); }
void addStmtp(AstNode* nodep) { addNOp2p(nodep); }
@@ -2883,8 +2953,6 @@ public:
void level(int level) { m_level = level; }
int level() const { return m_level; }
bool isTop() const { return level() == 1; }
int varNumGetInc() { return ++m_varNum; }
int typeNumGetInc() { return ++m_typeNum; }
void modPublic(bool flag) { m_modPublic = flag; }
bool modPublic() const { return m_modPublic; }
void modTrace(bool flag) { m_modTrace = flag; }
+6 -6
View File
@@ -34,7 +34,7 @@ inline int AstNode::widthInstrs() const {
return (!dtypep() ? 1 : (dtypep()->isWide() ? dtypep()->widthWords() : 1));
}
inline bool AstNode::isDouble() const {
return dtypep() && VN_IS(dtypep(), BasicDType) && VN_CAST(dtypep(), BasicDType)->isDouble();
return dtypep() && VN_IS(dtypep(), BasicDType) && VN_AS(dtypep(), BasicDType)->isDouble();
}
inline bool AstNode::isString() const {
return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isString();
@@ -42,19 +42,19 @@ inline bool AstNode::isString() const {
inline bool AstNode::isSigned() const { return dtypep() && dtypep()->isSigned(); }
inline bool AstNode::isZero() const {
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isEqZero());
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqZero());
}
inline bool AstNode::isNeqZero() const {
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isNeqZero());
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isNeqZero());
}
inline bool AstNode::isOne() const {
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isEqOne());
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqOne());
}
inline bool AstNode::isAllOnes() const {
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnes());
return (VN_IS(this, Const) && VN_AS(this, Const)->isEqAllOnes());
}
inline bool AstNode::isAllOnesV() const {
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnesV());
return (VN_IS(this, Const) && VN_AS(this, Const)->isEqAllOnesV());
}
inline bool AstNode::sameTree(const AstNode* node2p) const {
return sameTreeIter(this, node2p, true, false);
+276 -129
View File
@@ -18,16 +18,18 @@
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3EmitCBase.h"
#include "V3File.h"
#include "V3Global.h"
#include "V3Graph.h"
#include "V3Hasher.h"
#include "V3PartitionGraph.h" // Just for mtask dumping
#include "V3String.h"
#include "V3EmitCBase.h"
#include "V3AstNodes__gen_macros.h" // Generated by 'astgen'
#include <iomanip>
#include <iterator>
#include <vector>
//======================================================================
@@ -42,7 +44,7 @@ const char* AstIfaceRefDType::broken() const {
}
AstIface* AstIfaceRefDType::ifaceViaCellp() const {
return ((m_cellp && m_cellp->modp()) ? VN_CAST(m_cellp->modp(), Iface) : m_ifacep);
return ((m_cellp && m_cellp->modp()) ? VN_AS(m_cellp->modp(), Iface) : m_ifacep);
}
const char* AstNodeVarRef::broken() const {
@@ -55,18 +57,29 @@ void AstNodeVarRef::cloneRelink() {
if (m_varp && m_varp->clonep()) m_varp = m_varp->clonep();
}
string AstNodeVarRef::hiernameProtect() const {
return hiernameToUnprot() + VIdProtect::protectWordsIf(hiernameToProt(), protect());
string AstNodeVarRef::selfPointerProtect(bool useSelfForThis) const {
const string& sp
= useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer();
return VIdProtect::protectWordsIf(sp, protect());
}
void AstAddrOfCFunc::cloneRelink() {
if (m_funcp && m_funcp->clonep()) m_funcp = m_funcp->clonep();
}
const char* AstAddrOfCFunc::broken() const {
BROKEN_RTN(m_funcp && !m_funcp->brokeExists());
return nullptr;
}
int AstNodeSel::bitConst() const {
AstConst* constp = VN_CAST(bitp(), Const);
const AstConst* const constp = VN_AS(bitp(), Const);
return (constp ? constp->toSInt() : 0);
}
void AstNodeUOrStructDType::repairMemberCache() {
clearCache();
for (AstMemberDType* itemp = membersp(); itemp; itemp = VN_CAST(itemp->nextp(), MemberDType)) {
for (AstMemberDType* itemp = membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) {
if (m_members.find(itemp->name()) != m_members.end()) {
itemp->v3error("Duplicate declaration of member name: " << itemp->prettyNameQ());
} else {
@@ -77,7 +90,7 @@ void AstNodeUOrStructDType::repairMemberCache() {
const char* AstNodeUOrStructDType::broken() const {
std::unordered_set<AstMemberDType*> exists;
for (AstMemberDType* itemp = membersp(); itemp; itemp = VN_CAST(itemp->nextp(), MemberDType)) {
for (AstMemberDType* itemp = membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) {
exists.insert(itemp);
}
for (MemberNameMap::const_iterator it = m_members.begin(); it != m_members.end(); ++it) {
@@ -108,8 +121,11 @@ const char* AstNodeCCall::broken() const {
return nullptr;
}
bool AstNodeCCall::isPure() const { return funcp()->pure(); }
string AstNodeCCall::hiernameProtect() const {
return hiernameToUnprot() + VIdProtect::protectWordsIf(hiernameToProt(), protect());
string AstCCall::selfPointerProtect(bool useSelfForThis) const {
const string& sp
= useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer();
return VIdProtect::protectWordsIf(sp, protect());
}
void AstNodeCond::numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
@@ -181,30 +197,6 @@ AstNodeBiop* AstEq::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
}
}
AstNodeBiop* AstGte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
if (lhsp->isString() && rhsp->isString()) {
return new AstGteN(fl, lhsp, rhsp);
} else if (lhsp->isDouble() && rhsp->isDouble()) {
return new AstGteD(fl, lhsp, rhsp);
} else if (lhsp->isSigned() && rhsp->isSigned()) {
return new AstGteS(fl, lhsp, rhsp);
} else {
return new AstGte(fl, lhsp, rhsp);
}
}
AstNodeBiop* AstLte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
if (lhsp->isString() && rhsp->isString()) {
return new AstLteN(fl, lhsp, rhsp);
} else if (lhsp->isDouble() && rhsp->isDouble()) {
return new AstLteD(fl, lhsp, rhsp);
} else if (lhsp->isSigned() && rhsp->isSigned()) {
return new AstLteS(fl, lhsp, rhsp);
} else {
return new AstLte(fl, lhsp, rhsp);
}
}
AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
if (lhsp->isString() && rhsp->isString()) {
return new AstEqN(fl, lhsp, rhsp);
@@ -216,17 +208,17 @@ AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
}
AstExecGraph::AstExecGraph(FileLine* fileline)
: ASTGEN_SUPER_ExecGraph(fileline) {
m_depGraphp = new V3Graph;
}
: ASTGEN_SUPER_ExecGraph(fileline)
, m_depGraphp{new V3Graph} {}
AstExecGraph::~AstExecGraph() { VL_DO_DANGLING(delete m_depGraphp, m_depGraphp); }
AstNode* AstInsideRange::newAndFromInside(AstNode* exprp, AstNode* lhsp, AstNode* rhsp) {
AstNode* ap = new AstGte(fileline(), exprp->cloneTree(true), lhsp);
AstNode* bp = new AstLte(fileline(), exprp->cloneTree(true), rhsp);
AstNode* const ap = new AstGte(fileline(), exprp->cloneTree(true), lhsp);
AstNode* const bp = new AstLte(fileline(), exprp->cloneTree(true), rhsp);
ap->fileline()->modifyWarnOff(V3ErrorCode::UNSIGNED, true);
bp->fileline()->modifyWarnOff(V3ErrorCode::CMPCONST, true);
AstNode* newp = new AstAnd(fileline(), ap, bp);
AstNode* const newp = new AstAnd(fileline(), ap, bp);
return newp;
}
@@ -234,11 +226,11 @@ AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) {
bool success = false;
if (literal[0] == '"') {
// This is a string
string v = literal.substr(1, literal.find('"', 1) - 1);
const string v = literal.substr(1, literal.find('"', 1) - 1);
return new AstConst(fl, AstConst::VerilogStringLiteral(), v);
} else if (literal.find_first_of(".eEpP") != string::npos) {
// This may be a real
double v = VString::parseDouble(literal, &success);
const double v = VString::parseDouble(literal, &success);
if (success) return new AstConst(fl, AstConst::RealDouble(), v);
}
if (!success) {
@@ -250,9 +242,9 @@ AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) {
// the string after the number, this is invalid C and we try
// the Verilog literal parser.
char* endp;
int v = strtol(literal.c_str(), &endp, 0);
const int v = strtol(literal.c_str(), &endp, 0);
if ((v != 0) && (endp[0] == 0)) { // C literal
return new AstConst(fl, AstConst::WidthedValue(), 32, v);
return new AstConst(fl, AstConst::Signed32(), v);
} else { // Try a Verilog literal (fatals if not)
return new AstConst(fl, AstConst::StringToParse(), literal.c_str());
}
@@ -260,8 +252,16 @@ AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) {
return nullptr;
}
AstNetlist::AstNetlist()
: ASTGEN_SUPER_Netlist(new FileLine(FileLine::builtInFilename()))
, m_typeTablep{new AstTypeTable(fileline())}
, m_constPoolp{new AstConstPool(fileline())} {
addMiscsp(m_typeTablep);
addMiscsp(m_constPoolp);
}
void AstNetlist::timeprecisionMerge(FileLine*, const VTimescale& value) {
VTimescale prec = v3Global.opt.timeComputePrec(value);
const VTimescale prec = v3Global.opt.timeComputePrec(value);
if (prec.isNone() || prec == m_timeprecision) {
} else if (m_timeprecision.isNone()) {
m_timeprecision = prec;
@@ -314,13 +314,15 @@ string AstVar::verilogKwd() const {
}
}
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc) const {
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc,
bool asRef) const {
UASSERT_OBJ(!forReturn, this,
"Internal data is never passed as return, but as first argument");
string ostatic;
if (isStatic() && namespc.empty()) ostatic = "static ";
bool isRef = isDpiOpenArray() || (forFunc && (isWritable() || direction().isRefOrConstRef()));
const bool isRef = isDpiOpenArray()
|| (forFunc && (isWritable() || direction().isRefOrConstRef())) || asRef;
if (forFunc && isReadOnly() && isRef) ostatic = ostatic + "const ";
@@ -334,8 +336,8 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string&
string AstVar::vlEnumType() const {
string arg;
AstBasicDType* bdtypep = basicp();
bool strtype = bdtypep && bdtypep->keyword() == AstBasicDTypeKwd::STRING;
const AstBasicDType* const bdtypep = basicp();
const bool strtype = bdtypep && bdtypep->keyword() == AstBasicDTypeKwd::STRING;
if (bdtypep && bdtypep->keyword() == AstBasicDTypeKwd::CHARPTR) {
return "VLVT_PTR";
} else if (bdtypep && bdtypep->keyword() == AstBasicDTypeKwd::SCOPEPTR) {
@@ -375,7 +377,7 @@ string AstVar::vlEnumDir() const {
out += "|VLVF_PUB_RD";
}
//
if (AstBasicDType* bdtypep = basicp()) {
if (const AstBasicDType* const bdtypep = basicp()) {
if (bdtypep->keyword().isDpiCLayout()) out += "|VLVF_DPI_CLAY";
}
return out;
@@ -387,7 +389,7 @@ string AstVar::vlPropDecl(const string& propName) const {
std::vector<int> ulims; // Unpacked dimension limits
for (const AstNodeDType* dtp = dtypep(); dtp;) {
dtp = dtp->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
if (const AstNodeArrayDType* const adtypep = VN_CAST_CONST(dtp, NodeArrayDType)) {
if (const AstNodeArrayDType* const adtypep = VN_CAST(dtp, NodeArrayDType)) {
ulims.push_back(adtypep->declRange().left());
ulims.push_back(adtypep->declRange().right());
dtp = adtypep->subDTypep();
@@ -473,7 +475,7 @@ public:
string convert(const AstVar* varp) const {
if (varp->isDpiOpenArray()) {
return openArray(varp);
} else if (const AstBasicDType* basicp = varp->basicp()) {
} else if (const AstBasicDType* const basicp = varp->basicp()) {
if (basicp->isDpiBitVec() || basicp->isDpiLogicVec()) {
return bitLogicVector(varp, basicp->isDpiBitVec());
} else {
@@ -513,9 +515,9 @@ string AstVar::dpiArgType(bool named, bool forReturn) const {
string AstVar::dpiTmpVarType(const string& varName) const {
class converter final : public dpiTypesToStringConverter {
string m_name;
const string m_name;
string arraySuffix(const AstVar* varp, size_t n) const {
if (AstUnpackArrayDType* unpackp
if (AstUnpackArrayDType* const unpackp
= VN_CAST(varp->dtypep()->skipRefp(), UnpackArrayDType)) {
// Convert multi dimensional unpacked array to 1D array
if (n == 0) n = 1;
@@ -581,15 +583,15 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) {
// See if this is a SC assignment; if so return that type
// Historically sc variables are identified by a variable
// attribute. TODO it would better be a data type attribute.
if (AstVar* anodep = VN_CAST(nodep, Var)) {
if (AstVar* const anodep = VN_CAST(nodep, Var)) {
if (anodep->isSc()) {
return anodep;
} else {
return nullptr;
}
} else if (VN_IS(nodep, VarRef)) {
if (VN_CAST(nodep, VarRef)->varp()->isSc()) {
return VN_CAST(nodep, VarRef)->varp();
if (VN_AS(nodep, VarRef)->varp()->isSc()) {
return VN_AS(nodep, VarRef)->varp();
} else {
return nullptr;
}
@@ -610,13 +612,6 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) {
return nullptr;
}
string AstVar::mtasksString() const {
std::ostringstream os;
os << "all: ";
for (const auto& id : m_mtaskIds) os << id << " ";
return os.str();
}
class AstNodeDType::CTypeRecursed final {
public:
string m_type; // The base type, e.g.: "Foo_t"s
@@ -624,15 +619,22 @@ public:
string render(const string& name, bool isRef) const {
string out;
out += m_type;
if (name != "") out += " ";
out += isRef ? "(&" + name + ")" : name;
if (!name.empty()) out += " ";
if (isRef) {
if (!m_dims.empty()) out += "(";
out += "&";
out += name;
if (!m_dims.empty()) out += ")";
} else {
out += name;
}
out += m_dims;
return out;
}
};
string AstNodeDType::cType(const string& name, bool forFunc, bool isRef) const {
CTypeRecursed info = cTypeRecurse(false);
const CTypeRecursed info = cTypeRecurse(false);
return info.render(name, isRef);
}
@@ -640,29 +642,29 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
// Legacy compound argument currently just passed through and unused
CTypeRecursed info;
const AstNodeDType* dtypep = this->skipRefp();
if (const auto* adtypep = VN_CAST_CONST(dtypep, AssocArrayDType)) {
const AstNodeDType* const dtypep = this->skipRefp();
if (const auto* const adtypep = VN_CAST(dtypep, AssocArrayDType)) {
const CTypeRecursed key = adtypep->keyDTypep()->cTypeRecurse(true);
const CTypeRecursed val = adtypep->subDTypep()->cTypeRecurse(true);
info.m_type = "VlAssocArray<" + key.m_type + ", " + val.m_type + ">";
} else if (const auto* adtypep = VN_CAST_CONST(dtypep, DynArrayDType)) {
} else if (const auto* const adtypep = VN_CAST(dtypep, DynArrayDType)) {
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
info.m_type = "VlQueue<" + sub.m_type + ">";
} else if (const auto* adtypep = VN_CAST_CONST(dtypep, QueueDType)) {
} else if (const auto* const adtypep = VN_CAST(dtypep, QueueDType)) {
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
info.m_type = "VlQueue<" + sub.m_type;
// + 1 below as VlQueue uses 0 to mean unlimited, 1 to mean size() max is 1
if (adtypep->boundp()) info.m_type += ", " + cvtToStr(adtypep->boundConst() + 1);
info.m_type += ">";
} else if (const auto* adtypep = VN_CAST_CONST(dtypep, ClassRefDType)) {
} else if (const auto* const adtypep = VN_CAST(dtypep, ClassRefDType)) {
info.m_type = "VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(adtypep) + ">";
} else if (const auto* adtypep = VN_CAST_CONST(dtypep, UnpackArrayDType)) {
} else if (const auto* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
if (adtypep->isCompound()) compound = true;
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(compound);
info.m_type = "VlUnpacked<" + sub.m_type;
info.m_type += ", " + cvtToStr(adtypep->declRange().elements());
info.m_type += ">";
} else if (const AstBasicDType* bdtypep = dtypep->basicp()) {
} else if (const AstBasicDType* const bdtypep = dtypep->basicp()) {
// We don't print msb()/lsb() as multidim packed would require recursion,
// and may confuse users as C++ data is stored always with bit 0 used
const string bitvec = (!bdtypep->isOpaque() && !v3Global.opt.protectIds())
@@ -672,10 +674,12 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
info.m_type = "const char*";
} else if (bdtypep->keyword() == AstBasicDTypeKwd::SCOPEPTR) {
info.m_type = "const VerilatedScope*";
} else if (bdtypep->keyword() == AstBasicDTypeKwd::DOUBLE) {
} else if (bdtypep->keyword().isDouble()) {
info.m_type = "double";
} else if (bdtypep->keyword() == AstBasicDTypeKwd::STRING) {
} else if (bdtypep->keyword().isString()) {
info.m_type = "std::string";
} else if (bdtypep->keyword().isMTaskState()) {
info.m_type = "VlMTaskVertex";
} else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width
info.m_type = "CData" + bitvec;
} else if (dtypep->widthMin() <= 16) {
@@ -700,7 +704,7 @@ uint32_t AstNodeDType::arrayUnpackedElements() {
uint32_t entries = 1;
for (AstNodeDType* dtypep = this; dtypep;) {
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
if (AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
entries *= adtypep->elementsConst();
dtypep = adtypep->subDTypep();
} else {
@@ -717,7 +721,7 @@ std::pair<uint32_t, uint32_t> AstNodeDType::dimensions(bool includeBasic) {
uint32_t unpacked = 0;
for (AstNodeDType* dtypep = this; dtypep;) {
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
if (const AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) {
if (const AstNodeArrayDType* const adtypep = VN_CAST(dtypep, NodeArrayDType)) {
if (VN_IS(adtypep, PackArrayDType)) {
++packed;
} else {
@@ -725,11 +729,11 @@ std::pair<uint32_t, uint32_t> AstNodeDType::dimensions(bool includeBasic) {
}
dtypep = adtypep->subDTypep();
continue;
} else if (const AstQueueDType* qdtypep = VN_CAST(dtypep, QueueDType)) {
} else if (const AstQueueDType* const qdtypep = VN_CAST(dtypep, QueueDType)) {
unpacked++;
dtypep = qdtypep->subDTypep();
continue;
} else if (const AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) {
} else if (const AstBasicDType* const adtypep = VN_CAST(dtypep, BasicDType)) {
if (includeBasic && (adtypep->isRanged() || adtypep->isString())) packed++;
} else if (VN_IS(dtypep, StructDType)) {
packed++;
@@ -741,37 +745,51 @@ std::pair<uint32_t, uint32_t> AstNodeDType::dimensions(bool includeBasic) {
int AstNodeDType::widthPow2() const {
// I.e. width 30 returns 32, width 32 returns 32.
uint32_t width = this->width();
const uint32_t width = this->width();
for (int p2 = 30; p2 >= 0; p2--) {
if (width > (1UL << p2)) return (1UL << (p2 + 1));
}
return 1;
}
bool AstNodeDType::isLiteralType() const {
if (const auto* const dtypep = VN_CAST(skipRefp(), BasicDType)) {
return dtypep->keyword().isLiteralType();
} else if (const auto* const dtypep = VN_CAST(skipRefp(), UnpackArrayDType)) {
return dtypep->basicp()->isLiteralType();
} else if (const auto* const dtypep = VN_CAST(skipRefp(), StructDType)) {
// Currently all structs are packed, later this can be expanded to
// 'forall members _.isLiteralType()'
return dtypep->packed();
} else {
return false;
}
}
/// What is the base variable (or const) this dereferences?
AstNode* AstArraySel::baseFromp(AstNode* nodep, bool overMembers) {
// Else AstArraySel etc; search for the base
while (nodep) {
if (VN_IS(nodep, ArraySel)) {
nodep = VN_CAST(nodep, ArraySel)->fromp();
nodep = VN_AS(nodep, ArraySel)->fromp();
continue;
} else if (VN_IS(nodep, Sel)) {
nodep = VN_CAST(nodep, Sel)->fromp();
nodep = VN_AS(nodep, Sel)->fromp();
continue;
} else if (overMembers && VN_IS(nodep, MemberSel)) {
nodep = VN_CAST(nodep, MemberSel)->fromp();
nodep = VN_AS(nodep, MemberSel)->fromp();
continue;
}
// AstNodeSelPre stashes the associated variable under an ATTROF
// of AstAttrType::VAR_BASE/MEMBER_BASE so it isn't constified
else if (VN_IS(nodep, AttrOf)) {
nodep = VN_CAST(nodep, AttrOf)->fromp();
nodep = VN_AS(nodep, AttrOf)->fromp();
continue;
} else if (VN_IS(nodep, NodePreSel)) {
if (VN_CAST(nodep, NodePreSel)->attrp()) {
nodep = VN_CAST(nodep, NodePreSel)->attrp();
if (VN_AS(nodep, NodePreSel)->attrp()) {
nodep = VN_AS(nodep, NodePreSel)->attrp();
} else {
nodep = VN_CAST(nodep, NodePreSel)->fromp();
nodep = VN_AS(nodep, NodePreSel)->fromp();
}
continue;
} else {
@@ -812,7 +830,7 @@ string AstScope::nameDotless() const {
string AstScopeName::scopePrettyNameFormatter(AstText* scopeTextp) const {
string out;
for (AstText* textp = scopeTextp; textp; textp = VN_CAST(textp->nextp(), Text)) {
for (AstText* textp = scopeTextp; textp; textp = VN_AS(textp->nextp(), Text)) {
out += textp->text();
}
// TOP will be replaced by top->name()
@@ -823,7 +841,7 @@ string AstScopeName::scopePrettyNameFormatter(AstText* scopeTextp) const {
}
string AstScopeName::scopeNameFormatter(AstText* scopeTextp) const {
string out;
for (AstText* textp = scopeTextp; textp; textp = VN_CAST(textp->nextp(), Text)) {
for (AstText* textp = scopeTextp; textp; textp = VN_AS(textp->nextp(), Text)) {
out += textp->text();
}
if (out.substr(0, 10) == "__DOT__TOP") out.replace(0, 10, "");
@@ -837,33 +855,38 @@ string AstScopeName::scopeNameFormatter(AstText* scopeTextp) const {
bool AstSenTree::hasClocked() const {
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
for (AstSenItem* senp = sensesp(); senp; senp = VN_CAST(senp->nextp(), SenItem)) {
for (AstSenItem* senp = sensesp(); senp; senp = VN_AS(senp->nextp(), SenItem)) {
if (senp->isClocked()) return true;
}
return false;
}
bool AstSenTree::hasSettle() const {
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
for (AstSenItem* senp = sensesp(); senp; senp = VN_CAST(senp->nextp(), SenItem)) {
for (AstSenItem* senp = sensesp(); senp; senp = VN_AS(senp->nextp(), SenItem)) {
if (senp->isSettle()) return true;
}
return false;
}
bool AstSenTree::hasInitial() const {
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
for (AstSenItem* senp = sensesp(); senp; senp = VN_CAST(senp->nextp(), SenItem)) {
for (AstSenItem* senp = sensesp(); senp; senp = VN_AS(senp->nextp(), SenItem)) {
if (senp->isInitial()) return true;
}
return false;
}
bool AstSenTree::hasCombo() const {
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
for (AstSenItem* senp = sensesp(); senp; senp = VN_CAST(senp->nextp(), SenItem)) {
for (AstSenItem* senp = sensesp(); senp; senp = VN_AS(senp->nextp(), SenItem)) {
if (senp->isCombo()) return true;
}
return false;
}
AstTypeTable::AstTypeTable(FileLine* fl)
: ASTGEN_SUPER_TypeTable(fl) {
for (int i = 0; i < AstBasicDTypeKwd::_ENUM_MAX; ++i) m_basicps[i] = nullptr;
}
void AstTypeTable::clearCache() {
// When we mass-change widthMin in V3WidthCommit, we need to correct the table.
// Just clear out the maps; the search functions will be used to rebuild the map
@@ -871,7 +894,7 @@ void AstTypeTable::clearCache() {
m_detailedMap.clear();
// Clear generic()'s so dead detection will work
for (AstNode* nodep = typesp(); nodep; nodep = nodep->nextp()) {
if (AstBasicDType* bdtypep = VN_CAST(nodep, BasicDType)) bdtypep->generic(false);
if (AstBasicDType* const bdtypep = VN_CAST(nodep, BasicDType)) bdtypep->generic(false);
}
}
@@ -879,15 +902,24 @@ void AstTypeTable::repairCache() {
// After we mass-change widthMin in V3WidthCommit, we need to correct the table.
clearCache();
for (AstNode* nodep = typesp(); nodep; nodep = nodep->nextp()) {
if (AstBasicDType* bdtypep = VN_CAST(nodep, BasicDType)) {
if (AstBasicDType* const bdtypep = VN_CAST(nodep, BasicDType)) {
(void)findInsertSameDType(bdtypep);
}
}
}
AstEmptyQueueDType* AstTypeTable::findEmptyQueueDType(FileLine* fl) {
if (VL_UNLIKELY(!m_emptyQueuep)) {
AstEmptyQueueDType* const newp = new AstEmptyQueueDType{fl};
addTypesp(newp);
m_emptyQueuep = newp;
}
return m_emptyQueuep;
}
AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) {
if (VL_UNLIKELY(!m_voidp)) {
AstVoidDType* newp = new AstVoidDType(fl);
AstVoidDType* const newp = new AstVoidDType{fl};
addTypesp(newp);
m_voidp = newp;
}
@@ -896,7 +928,7 @@ AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) {
AstQueueDType* AstTypeTable::findQueueIndexDType(FileLine* fl) {
if (VL_UNLIKELY(!m_queueIndexp)) {
AstQueueDType* newp = new AstQueueDType(fl, AstNode::findUInt32DType(), nullptr);
AstQueueDType* const newp = new AstQueueDType(fl, AstNode::findUInt32DType(), nullptr);
addTypesp(newp);
m_queueIndexp = newp;
}
@@ -906,11 +938,11 @@ AstQueueDType* AstTypeTable::findQueueIndexDType(FileLine* fl) {
AstBasicDType* AstTypeTable::findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd) {
if (m_basicps[kwd]) return m_basicps[kwd];
//
AstBasicDType* new1p = new AstBasicDType(fl, kwd);
AstBasicDType* const new1p = new AstBasicDType(fl, kwd);
// Because the detailed map doesn't update this map,
// check the detailed map for this same node
// Also adds this new node to the detailed map
AstBasicDType* newp = findInsertSameDType(new1p);
AstBasicDType* const newp = findInsertSameDType(new1p);
if (newp != new1p) {
VL_DO_DANGLING(new1p->deleteTree(), new1p);
} else {
@@ -923,8 +955,8 @@ AstBasicDType* AstTypeTable::findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd)
AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd, int width,
int widthMin, VSigning numeric) {
AstBasicDType* new1p = new AstBasicDType(fl, kwd, numeric, width, widthMin);
AstBasicDType* newp = findInsertSameDType(new1p);
AstBasicDType* const new1p = new AstBasicDType(fl, kwd, numeric, width, widthMin);
AstBasicDType* const newp = findInsertSameDType(new1p);
if (newp != new1p) {
VL_DO_DANGLING(new1p->deleteTree(), new1p);
} else {
@@ -936,8 +968,8 @@ AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kw
AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd,
const VNumRange& range, int widthMin,
VSigning numeric) {
AstBasicDType* new1p = new AstBasicDType(fl, kwd, numeric, range, widthMin);
AstBasicDType* newp = findInsertSameDType(new1p);
AstBasicDType* const new1p = new AstBasicDType(fl, kwd, numeric, range, widthMin);
AstBasicDType* const newp = findInsertSameDType(new1p);
if (newp != new1p) {
VL_DO_DANGLING(new1p->deleteTree(), new1p);
} else {
@@ -947,8 +979,8 @@ AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kw
}
AstBasicDType* AstTypeTable::findInsertSameDType(AstBasicDType* nodep) {
VBasicTypeKey key(nodep->width(), nodep->widthMin(), nodep->numeric(), nodep->keyword(),
nodep->nrange());
const VBasicTypeKey key(nodep->width(), nodep->widthMin(), nodep->numeric(), nodep->keyword(),
nodep->nrange());
DetailedMap& mapr = m_detailedMap;
const auto it = mapr.find(key);
if (it != mapr.end()) return it->second;
@@ -958,6 +990,113 @@ AstBasicDType* AstTypeTable::findInsertSameDType(AstBasicDType* nodep) {
return nodep;
}
AstConstPool::AstConstPool(FileLine* fl)
: ASTGEN_SUPER_ConstPool(fl)
, m_modp{new AstModule(fl, "@CONST-POOL@")}
, m_scopep{new AstScope(fl, m_modp, "@CONST-POOL@", nullptr, nullptr)} {
addOp1p(m_modp);
m_modp->addStmtp(m_scopep);
}
AstVarScope* AstConstPool::createNewEntry(const string& name, AstNode* initp) {
FileLine* const fl = initp->fileline();
AstVar* const varp = new AstVar(fl, AstVarType::MODULETEMP, name, initp->dtypep());
varp->isConst(true);
varp->isStatic(true);
varp->valuep(initp->cloneTree(false));
m_modp->addStmtp(varp);
AstVarScope* const varScopep = new AstVarScope(fl, m_scopep, varp);
m_scopep->addVarp(varScopep);
return varScopep;
}
static bool sameInit(const AstInitArray* ap, const AstInitArray* bp) {
// Unpacked array initializers must have equivalent values
// Note, sadly we can't just call ap->sameTree(pb), because both:
// - the dtypes might be different instances
// - the default/inititem children might be in different order yet still yield the same table
// See note in AstInitArray::same about the same. This function instead compares by initializer
// value, rather than by tree structure.
const AstUnpackArrayDType* const aDTypep = VN_AS(ap->dtypep(), UnpackArrayDType);
const AstUnpackArrayDType* const bDTypep = VN_AS(bp->dtypep(), UnpackArrayDType);
if (!aDTypep->subDTypep()->sameTree(bDTypep->subDTypep())) { // Element types differ
return false;
}
if (!aDTypep->rangep()->sameTree(bDTypep->rangep())) { // Ranges differ
return false;
}
// Compare initializer arrays by value. Note this is only called when they hash the same,
// so they likely run at most once per call to 'AstConstPool::findTable'.
const uint32_t size = aDTypep->elementsConst();
for (uint32_t n = 0; n < size; ++n) {
const AstNode* const valAp = ap->getIndexDefaultedValuep(n);
const AstNode* const valBp = bp->getIndexDefaultedValuep(n);
if (!valAp->sameTree(valBp)) { return false; }
}
return true;
}
AstVarScope* AstConstPool::findTable(AstInitArray* initp) {
const AstNode* const defaultp = initp->defaultp();
// Verify initializer is well formed
UASSERT_OBJ(VN_IS(initp->dtypep(), UnpackArrayDType), initp,
"Const pool table must have AstUnpackArrayDType dtype");
UASSERT_OBJ(!defaultp || VN_IS(defaultp, Const), initp,
"Const pool table default must be Const");
for (AstNode* nodep = initp->initsp(); nodep; nodep = nodep->nextp()) {
const AstNode* const valuep = VN_AS(nodep, InitItem)->valuep();
UASSERT_OBJ(VN_IS(valuep, Const), valuep, "Const pool table entry must be Const");
}
// Try to find an existing table with the same content
const V3Hash hash = V3Hasher::uncachedHash(initp);
const auto& er = m_tables.equal_range(hash.value());
for (auto it = er.first; it != er.second; ++it) {
AstVarScope* const varScopep = it->second;
const AstInitArray* const init2p = VN_AS(varScopep->varp()->valuep(), InitArray);
if (sameInit(initp, init2p)) {
return varScopep; // Found identical table
}
}
// No such table yet, create it.
string name = "TABLE_";
name += hash.toString();
name += "_";
name += cvtToStr(std::distance(er.first, er.second));
AstVarScope* const varScopep = createNewEntry(name, initp);
m_tables.emplace(hash.value(), varScopep);
return varScopep;
}
static bool sameInit(const AstConst* ap, const AstConst* bp) {
// Similarly to the AstInitArray comparison, we ignore the dtype instance, so long as they
// are compatible. For now, we assume the dtype is not relevant, as we only call this from
// V3Prelim which is a late pass.
// Compare initializers by value. This checks widths as well.
return ap->num().isCaseEq(bp->num());
}
AstVarScope* AstConstPool::findConst(AstConst* initp, bool mergeDType) {
// Try to find an existing constant with the same value
const V3Hash hash = initp->num().toHash();
const auto& er = m_consts.equal_range(hash.value());
for (auto it = er.first; it != er.second; ++it) {
AstVarScope* const varScopep = it->second;
const AstConst* const init2p = VN_AS(varScopep->varp()->valuep(), Const);
if (sameInit(initp, init2p)
&& (mergeDType || varScopep->dtypep()->sameTree(initp->dtypep()))) {
return varScopep; // Found identical constant
}
}
// No such constant yet, create it.
string name = "CONST_";
name += hash.toString();
name += "_";
name += cvtToStr(std::distance(er.first, er.second));
AstVarScope* const varScopep = createNewEntry(name, initp);
m_consts.emplace(hash.value(), varScopep);
return varScopep;
}
//======================================================================
// Special walking tree inserters
@@ -1053,7 +1192,7 @@ void AstNode::dump(std::ostream& str) const {
} else {
str << " @dt=" << nodeAddr(dtypep()) << "@";
}
if (AstNodeDType* dtp = dtypep()) dtp->dumpSmall(str);
if (AstNodeDType* const dtp = dtypep()) dtp->dumpSmall(str);
} else { // V3Broken will throw an error
if (dtypep()) str << " %Error-dtype-exp=null,got=" << nodeAddr(dtypep());
}
@@ -1119,8 +1258,8 @@ const char* AstClassPackage::broken() const {
return nullptr;
}
void AstClass::insertCache(AstNode* nodep) {
bool doit = (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|| (VN_IS(nodep, NodeFTask) && !VN_CAST(nodep, NodeFTask)->isExternProto()));
const bool doit = (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|| (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto()));
if (doit) {
if (m_members.find(nodep->name()) != m_members.end()) {
nodep->v3error("Duplicate declaration of member name: " << nodep->prettyNameQ());
@@ -1146,9 +1285,9 @@ void AstClass::dump(std::ostream& str) const {
if (isVirtual()) str << " [VIRT]";
}
AstClass* AstClassExtends::classp() const {
AstClassRefDType* refp = VN_CAST(dtypep(), ClassRefDType);
const AstClassRefDType* refp = VN_CAST(dtypep(), ClassRefDType);
if (VL_UNLIKELY(!refp)) { // LinkDot uses this for 'super.'
refp = VN_CAST(childDTypep(), ClassRefDType);
refp = VN_AS(childDTypep(), ClassRefDType);
}
UASSERT_OBJ(refp, this, "class extends non-ref");
return refp->classp();
@@ -1172,7 +1311,7 @@ void AstNodeCoverOrAssert::dump(std::ostream& str) const {
}
void AstDisplay::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
// str<<" "<<displayType().ascii();
// str << " " << displayType().ascii();
}
void AstEnumItemRef::dump(std::ostream& str) const {
this->AstNodeMath::dump(str);
@@ -1316,10 +1455,10 @@ void AstRefDType::dump(std::ostream& str) const {
if (!s_recursing) { // Prevent infinite dump if circular typedefs
s_recursing = true;
str << " -> ";
if (typedefp()) {
typedefp()->dump(str);
} else if (subDTypep()) {
subDTypep()->dump(str);
if (const auto subp = typedefp()) {
subp->dump(str);
} else if (const auto subp = subDTypep()) {
subp->dump(str);
}
s_recursing = false;
}
@@ -1335,7 +1474,7 @@ void AstNodeUOrStructDType::dump(std::ostream& str) const {
void AstNodeDType::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (generic()) str << " [GENERIC]";
if (AstNodeDType* dtp = virtRefDTypep()) {
if (AstNodeDType* const dtp = virtRefDTypep()) {
str << " refdt=" << nodeAddr(dtp);
dtp->dumpSmall(str);
}
@@ -1349,7 +1488,7 @@ void AstNodeDType::dumpSmall(std::ostream& str) const {
}
void AstNodeArrayDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
if (auto* adtypep = VN_CAST_CONST(this, UnpackArrayDType)) {
if (auto* const adtypep = VN_CAST(this, UnpackArrayDType)) {
// uc = packed compound object, u = unpacked POD
str << (adtypep->isCompound() ? "uc" : "u");
} else {
@@ -1364,7 +1503,8 @@ void AstNodeArrayDType::dump(std::ostream& str) const {
}
string AstPackArrayDType::prettyDTypeName() const {
std::ostringstream os;
os << subDTypep()->prettyDTypeName() << declRange();
if (const auto subp = subDTypep()) os << subp->prettyDTypeName();
os << declRange();
return os.str();
}
string AstUnpackArrayDType::prettyDTypeName() const {
@@ -1384,7 +1524,7 @@ std::vector<AstUnpackArrayDType*> AstUnpackArrayDType::unpackDimensions() {
std::vector<AstUnpackArrayDType*> dims;
for (AstUnpackArrayDType* unpackp = this; unpackp;) {
dims.push_back(unpackp);
if (AstNodeDType* subp = unpackp->subDTypep()) {
if (AstNodeDType* const subp = unpackp->subDTypep()) {
unpackp = VN_CAST(subp, UnpackArrayDType);
} else {
unpackp = nullptr;
@@ -1441,7 +1581,7 @@ void AstMTaskBody::dump(std::ostream& str) const {
void AstTypeTable::dump(std::ostream& str) const {
this->AstNode::dump(str);
for (int i = 0; i < static_cast<int>(AstBasicDTypeKwd::_ENUM_MAX); ++i) {
if (AstBasicDType* subnodep = m_basicps[i]) {
if (AstBasicDType* const subnodep = m_basicps[i]) {
str << '\n'; // Newline from caller, so newline first
str << "\t\t" << std::setw(8) << AstBasicDTypeKwd(i).ascii();
str << " -> ";
@@ -1451,7 +1591,7 @@ void AstTypeTable::dump(std::ostream& str) const {
{
const DetailedMap& mapr = m_detailedMap;
for (const auto& itr : mapr) {
AstBasicDType* dtypep = itr.second;
AstBasicDType* const dtypep = itr.second;
str << '\n'; // Newline from caller, so newline first
str << "\t\tdetailed -> ";
dtypep->dump(str);
@@ -1484,6 +1624,10 @@ void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "[]";
}
void AstEmptyQueueDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "emptyq";
}
void AstVoidDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "void";
@@ -1556,6 +1700,11 @@ void AstScope::dump(std::ostream& str) const {
str << " [cellp=" << reinterpret_cast<const void*>(aboveCellp()) << "]";
str << " [modp=" << reinterpret_cast<const void*>(modp()) << "]";
}
void AstScopeName::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (dpiExport()) str << " [DPIEX]";
if (forFormat()) str << " [FMT]";
}
void AstSenTree::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (isMulti()) str << " [MULTI]";
@@ -1679,14 +1828,12 @@ void AstCFunc::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (slow()) str << " [SLOW]";
if (pure()) str << " [PURE]";
if (isStatic().unknown()) {
str << " [STATICU]";
} else if (isStatic().trueUnknown()) {
str << " [STATIC]";
}
if (dpiImport()) str << " [DPII]";
if (dpiExport()) str << " [DPIX]";
if (dpiExportWrapper()) str << " [DPIXWR]";
if (isStatic()) str << " [STATIC]";
if (dpiExportDispatcher()) str << " [DPIED]";
if (dpiExportImpl()) str << " [DPIEI]";
if (dpiImportPrototype()) str << " [DPIIP]";
if (dpiImportWrapper()) str << " [DPIIW]";
if (dpiContext()) str << " [DPICTX]";
if (isConstructor()) str << " [CTOR]";
if (isDestructor()) str << " [DTOR]";
if (isVirtual()) str << " [VIRT]";
+451 -337
View File
File diff suppressed because it is too large Load Diff
+126
View File
@@ -0,0 +1,126 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Utility to hang advanced data structures of
// AstNode::user*p() pointers with automatic memory management.
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef VERILATOR_V3ASTUSERALLOCATOR_H_
#define VERILATOR_V3ASTUSERALLOCATOR_H_
#include "config_build.h"
#include "verilatedos.h"
#include "V3Ast.h"
#include <type_traits>
#include <vector>
template <class T_Node, class T_Data, int T_UserN> class AstUserAllocatorBase VL_NOT_FINAL {
static_assert(1 <= T_UserN && T_UserN <= 5, "Wrong user pointer number");
static_assert(std::is_base_of<AstNode, T_Node>::value, "T_Node must be an AstNode type");
private:
std::vector<T_Data*> m_allocated;
inline T_Data* getUserp(const T_Node* nodep) const {
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
if (T_UserN == 1) {
const VNUser user = nodep->user1u();
return user.to<T_Data*>();
} else if (T_UserN == 2) {
const VNUser user = nodep->user2u();
return user.to<T_Data*>();
} else if (T_UserN == 3) {
const VNUser user = nodep->user3u();
return user.to<T_Data*>();
} else if (T_UserN == 4) {
const VNUser user = nodep->user4u();
return user.to<T_Data*>();
} else {
const VNUser user = nodep->user5u();
return user.to<T_Data*>();
}
}
inline void setUserp(T_Node* nodep, T_Data* userp) const {
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
if (T_UserN == 1) {
nodep->user1u(VNUser(userp));
} else if (T_UserN == 2) {
nodep->user2u(VNUser(userp));
} else if (T_UserN == 3) {
nodep->user3u(VNUser(userp));
} else if (T_UserN == 4) {
nodep->user4u(VNUser(userp));
} else {
nodep->user5u(VNUser(userp));
}
}
protected:
AstUserAllocatorBase() {
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
if (T_UserN == 1) {
AstUser1InUse::check();
} else if (T_UserN == 2) {
AstUser2InUse::check();
} else if (T_UserN == 3) {
AstUser3InUse::check();
} else if (T_UserN == 4) {
AstUser4InUse::check();
} else {
AstUser5InUse::check();
}
}
virtual ~AstUserAllocatorBase() {
// Delete all allocated data structures
for (T_Data* const p : m_allocated) { delete p; }
}
public:
// Get a reference to the user data
T_Data& operator()(T_Node* nodep) {
T_Data* userp = getUserp(nodep);
if (!userp) {
userp = new T_Data;
m_allocated.push_back(userp);
setUserp(nodep, userp);
}
return *userp;
}
// Get a reference to the user data
T_Data& operator()(const T_Node* nodep) {
T_Data* const userp = getUserp(nodep);
UASSERT_OBJ(userp, nodep, "Missing User data on const AstNode");
return *userp;
}
};
// User pointer allocator classes. T_Node is the type of node the allocator should be applied to
// and is simply there for a bit of extra type safety. T_Data is the type of the data structure
// managed by the allocator.
template <class T_Node, class T_Data>
class AstUser1Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 1> {};
template <class T_Node, class T_Data>
class AstUser2Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 2> {};
template <class T_Node, class T_Data>
class AstUser3Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 3> {};
template <class T_Node, class T_Data>
class AstUser4Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 4> {};
template <class T_Node, class T_Data>
class AstUser5Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 5> {};
#endif // Guard
+51 -27
View File
@@ -41,7 +41,7 @@ private:
// NODE STATE
// Entire netlist:
// AstNodeFTask::user1 -> bool, 1=processed
AstUser1InUse m_inuser1;
const AstUser1InUse m_inuser1;
bool m_anyFuncInBegin = false;
public:
@@ -59,9 +59,11 @@ public:
class BeginVisitor final : public AstNVisitor {
private:
// STATE
BeginState* m_statep; // Current global state
BeginState* const m_statep; // Current global state
AstNodeModule* m_modp = nullptr; // Current module
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
AstNode* m_liftedp = nullptr; // Local nodes we are lifting into m_ftaskp
string m_displayScope; // Name of %m in $display/AstScopeName
string m_namedScope; // Name of begin blocks above us
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
int m_ifDepth = 0; // Current if depth
@@ -74,6 +76,21 @@ private:
return a + "__DOT__" + b;
}
void liftNode(AstNode* nodep) {
nodep->unlinkFrBack();
if (m_ftaskp) {
// AstBegin under ftask, just move into the ftask
if (!m_liftedp) {
m_liftedp = nodep;
} else {
m_liftedp->addNext(nodep);
}
} else {
// Move to module
m_modp->addStmtp(nodep);
}
}
// VISITORS
virtual void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
@@ -95,19 +112,32 @@ private:
// naming; so that any begin's inside the function will rename
// inside the function.
// Process children
VL_RESTORER(m_displayScope);
VL_RESTORER(m_namedScope);
VL_RESTORER(m_unnamedScope);
{
m_displayScope = dot(m_displayScope, nodep->name());
m_namedScope = "";
m_unnamedScope = "";
m_ftaskp = nodep;
m_liftedp = nullptr;
iterateChildren(nodep);
if (m_liftedp) {
// Place lifted nodes at beginning of stmtsp, so Var nodes appear before referenced
if (AstNode* const stmtsp = nodep->stmtsp()) {
stmtsp->unlinkFrBackWithNext();
m_liftedp->addNext(stmtsp);
}
nodep->addStmtsp(m_liftedp);
m_liftedp = nullptr;
}
m_ftaskp = nullptr;
}
}
virtual void visit(AstBegin* nodep) override {
// Begin blocks were only useful in variable creation, change names and delete
UINFO(8, " " << nodep << endl);
VL_RESTORER(m_displayScope);
VL_RESTORER(m_namedScope);
VL_RESTORER(m_unnamedScope);
{
@@ -117,13 +147,16 @@ private:
string dottedname = nodep->name() + "__DOT__"; // So always found
string::size_type pos;
while ((pos = dottedname.find("__DOT__")) != string::npos) {
string ident = dottedname.substr(0, pos);
const string ident = dottedname.substr(0, pos);
dottedname = dottedname.substr(pos + strlen("__DOT__"));
if (nodep->name() != "") m_namedScope = dot(m_namedScope, ident);
if (nodep->name() != "") {
m_displayScope = dot(m_displayScope, ident);
m_namedScope = dot(m_namedScope, ident);
}
m_unnamedScope = dot(m_unnamedScope, ident);
// Create CellInline for dotted var resolution
if (!m_ftaskp) {
AstCellInline* inlinep = new AstCellInline(
AstCellInline* const inlinep = new AstCellInline(
nodep->fileline(), m_unnamedScope, "__BEGIN__", m_modp->timeunit());
m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells
}
@@ -136,7 +169,7 @@ private:
// Cleanup
AstNode* addsp = nullptr;
if (AstNode* stmtsp = nodep->stmtsp()) {
if (AstNode* const stmtsp = nodep->stmtsp()) {
stmtsp->unlinkFrBackWithNext();
if (addsp) {
addsp = addsp->addNextNull(stmtsp);
@@ -157,13 +190,8 @@ private:
// Rename it
nodep->name(dot(m_unnamedScope, nodep->name()));
m_statep->userMarkChanged(nodep);
// Move to module
nodep->unlinkFrBack();
if (m_ftaskp) {
m_ftaskp->addStmtsp(nodep); // Begins under funcs just move into the func
} else {
m_modp->addStmtp(nodep);
}
// Move it under enclosing tree
liftNode(nodep);
}
}
virtual void visit(AstTypedef* nodep) override {
@@ -171,14 +199,8 @@ private:
// Rename it
nodep->name(dot(m_unnamedScope, nodep->name()));
m_statep->userMarkChanged(nodep);
// Move to module
nodep->unlinkFrBack();
// Begins under funcs just move into the func
if (m_ftaskp) {
m_ftaskp->addStmtsp(nodep);
} else {
m_modp->addStmtp(nodep);
}
// Move it under enclosing tree
liftNode(nodep);
}
}
virtual void visit(AstCell* nodep) override {
@@ -205,11 +227,13 @@ private:
// If there's a %m in the display text, we add a special node that will contain the name()
// Similar code in V3Inline
if (nodep->user1SetOnce()) return; // Don't double-add text's
if (m_namedScope != "") {
// DPI svGetScope doesn't include function name, but %m does
const string scname = nodep->forFormat() ? m_displayScope : m_namedScope;
if (!scname.empty()) {
// To keep correct visual order, must add before other Text's
AstNode* afterp = nodep->scopeAttrp();
AstNode* const afterp = nodep->scopeAttrp();
if (afterp) afterp->unlinkFrBackWithNext();
nodep->scopeAttrp(new AstText(nodep->fileline(), string("__DOT__") + m_namedScope));
nodep->scopeAttrp(new AstText{nodep->fileline(), string("__DOT__") + scname});
if (afterp) nodep->scopeAttrp(afterp);
}
iterateChildren(nodep);
@@ -293,8 +317,8 @@ void V3Begin::debeginAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{
BeginState state;
{ BeginVisitor bvisitor(nodep, &state); }
if (state.anyFuncInBegin()) { BeginRelinkVisitor brvisitor(nodep, &state); }
{ BeginVisitor{nodep, &state}; }
if (state.anyFuncInBegin()) { BeginRelinkVisitor{nodep, &state}; }
} // Destruct before checking
V3Global::dumpCheckGlobalTree("begin", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+9 -9
View File
@@ -40,11 +40,11 @@ private:
// NODE STATE
// Entire netlist:
// AstFTask::user1() -> int. Number of references
AstUser1InUse m_inuser1;
const AstUser1InUse m_inuser1;
// STATE
int m_likely; // Excuses for branch likely taken
int m_unlikely; // Excuses for branch likely not taken
int m_likely = false; // Excuses for branch likely taken
int m_unlikely = false; // Excuses for branch likely not taken
std::vector<AstCFunc*> m_cfuncsp; // List of all tasks
// METHODS
@@ -70,15 +70,15 @@ private:
// Do if
reset();
iterateAndNextNull(nodep->ifsp());
int ifLikely = m_likely;
int ifUnlikely = m_unlikely;
const int ifLikely = m_likely;
const int ifUnlikely = m_unlikely;
// Do else
reset();
iterateAndNextNull(nodep->elsesp());
int elseLikely = m_likely;
int elseUnlikely = m_unlikely;
const int elseLikely = m_likely;
const int elseUnlikely = m_unlikely;
// Compute
int likeness = ifLikely - ifUnlikely - (elseLikely - elseUnlikely);
const int likeness = ifLikely - ifUnlikely - (elseLikely - elseUnlikely);
if (likeness > 0) {
nodep->branchPred(VBranchPred::BP_LIKELY);
} else if (likeness < 0) {
@@ -123,5 +123,5 @@ public:
void V3Branch::branchAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
BranchVisitor visitor(nodep);
{ BranchVisitor{nodep}; }
}
+210 -186
View File
@@ -18,6 +18,7 @@
// Entire netlist
// Mark all nodes
// Check all links point to marked nodes
// Check local variables in CFuncs appear before they are referenced
//
//*************************************************************************
@@ -31,201 +32,129 @@
// This visitor does not edit nodes, and is called at error-exit, so should use constant iterators
#include "V3AstConstOnly.h"
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
//######################################################################
// Generation counter for AstNode::m_brokenState
class BrokenTable VL_NOT_FINAL : public AstNVisitor {
// Table of brokenExists node pointers
static class BrokenCntGlobal {
// This is a 7 bit generation counter, stored in the bottom 7 bits of AstNode::m_brokenState,
// used to mark a node as being present under the root AstNetlist in the current traversal. A
// value 0 is invalid, as the AstNode constructor uses that to initialize m_brokenState
static constexpr uint8_t MIN_VALUE = 1;
static constexpr uint8_t MAX_VALUE = 127;
uint8_t m_count = MIN_VALUE;
public:
uint8_t get() {
UASSERT(MIN_VALUE <= m_count && m_count <= MAX_VALUE, "Invalid generation number");
return m_count;
}
void inc() {
++m_count;
if (m_count > MAX_VALUE) m_count = MIN_VALUE;
}
} s_brokenCntGlobal;
//######################################################################
// Table of allocated AstNode pointers
static class AllocTable final {
private:
// MEMBERS
// For each node, we keep if it exists or not.
using NodeMap = std::unordered_map<const AstNode*, int>; // Performance matters (when --debug)
static NodeMap s_nodes; // Set of all nodes that exist
// BITMASK
enum { FLAG_ALLOCATED = 0x01 }; // new() and not delete()ed
enum { FLAG_IN_TREE = 0x02 }; // Is in netlist tree
enum { FLAG_LINKABLE = 0x04 }; // Is in netlist tree, can be linked to
enum { FLAG_LEAKED = 0x08 }; // Known to have been leaked
enum { FLAG_UNDER_NOW = 0x10 }; // Is in tree as parent of current node
std::unordered_set<const AstNode*> m_allocated; // Set of all nodes allocated but not freed
public:
// METHODS
static void deleted(const AstNode* nodep) {
// Called by operator delete on any node - only if VL_LEAK_CHECKS
if (debug() >= 9) cout << "-nodeDel: " << cvtToHex(nodep) << endl;
const auto iter = s_nodes.find(nodep);
UASSERT_OBJ(!(iter == s_nodes.end() || !(iter->second & FLAG_ALLOCATED)),
reinterpret_cast<const AstNode*>(nodep),
"Deleting AstNode object that was never tracked or already deleted");
if (iter != s_nodes.end()) s_nodes.erase(iter);
}
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 4
// GCC 4.4.* compiler warning bug, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39390
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#endif
static void addNewed(const AstNode* nodep) {
void addNewed(const AstNode* nodep) {
// Called by operator new on any node - only if VL_LEAK_CHECKS
if (debug() >= 9) cout << "-nodeNew: " << cvtToHex(nodep) << endl;
const auto iter = s_nodes.find(nodep);
UASSERT_OBJ(!(iter != s_nodes.end() && (iter->second & FLAG_ALLOCATED)), nodep,
"Newing AstNode object that is already allocated");
if (iter == s_nodes.end()) {
int flags = FLAG_ALLOCATED; // This int needed to appease GCC 4.1.2
s_nodes.emplace(nodep, flags);
// LCOV_EXCL_START
if (VL_UNCOVERABLE(!m_allocated.emplace(nodep).second)) {
nodep->v3fatalSrc("Newing AstNode object that is already allocated");
}
// LCOV_EXCL_STOP
}
static void setUnder(const AstNode* nodep, bool flag) {
// Called by BrokenCheckVisitor when each node entered/exited
if (!okIfLinkedTo(nodep)) return;
const auto iter = s_nodes.find(nodep);
if (iter != s_nodes.end()) {
iter->second &= ~FLAG_UNDER_NOW;
if (flag) iter->second |= FLAG_UNDER_NOW;
void deleted(const AstNode* nodep) {
// Called by operator delete on any node - only if VL_LEAK_CHECKS
// LCOV_EXCL_START
if (VL_UNCOVERABLE(m_allocated.erase(nodep) == 0)) {
nodep->v3fatalSrc("Deleting AstNode object that was not allocated or already freed");
}
// LCOV_EXCL_STOP
}
static void addInTree(AstNode* nodep, bool linkable) {
#ifndef VL_LEAK_CHECKS
// cppcheck-suppress knownConditionTrueFalse
if (!linkable) return; // save some time, else the map will get huge!
#endif
const auto iter = s_nodes.find(nodep);
if (VL_UNCOVERABLE(iter == s_nodes.end())) {
#ifdef VL_LEAK_CHECKS
nodep->v3fatalSrc("AstNode is in tree, but not allocated");
#endif
} else {
#ifdef VL_LEAK_CHECKS
UASSERT_OBJ(iter->second & FLAG_ALLOCATED, nodep,
"AstNode is in tree, but not allocated");
#endif
UASSERT_OBJ(!(iter->second & FLAG_IN_TREE), nodep,
"AstNode is already in tree at another location");
}
int or_flags = FLAG_IN_TREE | (linkable ? FLAG_LINKABLE : 0);
if (iter == s_nodes.end()) {
s_nodes.emplace(nodep, or_flags);
} else {
iter->second |= or_flags;
}
}
static bool isAllocated(const AstNode* nodep) {
// Some generic node has a pointer to this node. Is it allocated?
// Use this when might not be in tree; otherwise use okIfLinkedTo().
#ifdef VL_LEAK_CHECKS
const auto iter = s_nodes.find(nodep);
if (iter == s_nodes.end()) return false;
if (!(iter->second & FLAG_ALLOCATED)) return false;
#endif
return true;
}
static bool okIfLinkedTo(const AstNode* nodep) {
// Some node in tree has a pointer to this node. Is it kosher?
const auto iter = s_nodes.find(nodep);
if (iter == s_nodes.end()) return false;
#ifdef VL_LEAK_CHECKS
if (!(iter->second & FLAG_ALLOCATED)) return false;
#endif
if (!(iter->second & FLAG_IN_TREE)) return false;
if (!(iter->second & FLAG_LINKABLE)) return false;
return true;
}
static bool okIfAbove(const AstNode* nodep) {
// Must be linked to and below current node
if (!okIfLinkedTo(nodep)) return false;
const auto iter = s_nodes.find(nodep);
if (iter == s_nodes.end()) return false;
if ((iter->second & FLAG_UNDER_NOW)) return false;
return true;
}
static bool okIfBelow(const AstNode* nodep) {
// Must be linked to and below current node
if (!okIfLinkedTo(nodep)) return false;
const auto iter = s_nodes.find(nodep);
if (iter == s_nodes.end()) return false;
if (!(iter->second & FLAG_UNDER_NOW)) return false;
return true;
}
static void prepForTree() {
#ifndef VL_LEAK_CHECKS
s_nodes.clear();
#else
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
it->second &= ~FLAG_IN_TREE;
it->second &= ~FLAG_LINKABLE;
}
#endif
}
static void doneWithTree() {
for (int backs = 0; backs < 2;
backs++) { // Those with backp() are probably under one leaking without
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
// LCOV_EXCL_START
if (VL_UNCOVERABLE((it->second & FLAG_ALLOCATED) && !(it->second & FLAG_IN_TREE)
&& !(it->second & FLAG_LEAKED)
&& (it->first->backp() ? backs == 1 : backs == 0))) {
bool isAllocated(const AstNode* nodep) const { return m_allocated.count(nodep) != 0; }
void checkForLeaks() {
if (!v3Global.opt.debugCheck()) return;
const uint8_t brokenCntCurrent = s_brokenCntGlobal.get();
// Those with backp() are probably under a parent that was leaked and has no backp()
for (const bool withBack : {false, true}) {
for (const AstNode* const nodep : m_allocated) {
// LCOV_EXCL_START
// Most likely not leaked, so check that first
if (VL_UNCOVERABLE(nodep->brokenState() != brokenCntCurrent)) {
const bool hasBack = nodep->backp() != nullptr;
if (hasBack != withBack) continue;
// Use only AstNode::dump instead of the virtual one, as there
// may be varp() and other cross links that are bad.
if (v3Global.opt.debugCheck()) {
// When get this message, find what forgot to delete the
// node by running GDB, where for node "<e###>" use:
// watch AstNode::s_editCntGbl==####
// run
// bt
std::cerr << "%Error: LeakedNode"
<< (it->first->backp() ? "Back: " : ": ");
AstNode* rawp
= const_cast<AstNode*>(static_cast<const AstNode*>(it->first));
rawp->AstNode::dump(std::cerr);
std::cerr << endl;
V3Error::incErrors();
}
it->second |= FLAG_LEAKED;
// When get this message, find what forgot to delete the
// node by running GDB, where for node "<e###>" use:
// watch AstNode::s_editCntGbl==####
// run
// bt
std::cerr << "%Error: LeakedNode" << (withBack ? "with back pointer: " : ": ");
nodep->AstNode::dump(std::cerr);
std::cerr << endl;
V3Error::incErrors();
}
// LCOV_EXCL_STOP
}
}
}
} s_allocTable;
// CONSTRUCTORS
BrokenTable() = default;
virtual ~BrokenTable() override = default;
};
BrokenTable::NodeMap BrokenTable::s_nodes;
bool AstNode::brokeExists() const {
// Called by node->broken() routines to do table lookup
return BrokenTable::okIfLinkedTo(this);
}
bool AstNode::brokeExistsAbove() const {
// Called by node->broken() routines to do table lookup
return BrokenTable::okIfBelow(this);
}
bool AstNode::brokeExistsBelow() const {
// Called by node->broken() routines to do table lookup
return BrokenTable::okIfAbove(this);
}
void V3Broken::addNewed(const AstNode* nodep) { s_allocTable.addNewed(nodep); }
void V3Broken::deleted(const AstNode* nodep) { s_allocTable.deleted(nodep); }
//######################################################################
// Table of AstNode pointers that can be linked to via member pointers
static class LinkableTable final {
private:
// MEMBERS
std::unordered_set<const AstNode*> m_linkable; // Set of all nodes allocated but not freed
public:
// METHODS
void clear() { m_linkable.clear(); }
inline void addLinkable(const AstNode* nodep) { m_linkable.emplace(nodep); }
inline bool isLinkable(const AstNode* nodep) const { return m_linkable.count(nodep) != 0; }
} s_linkableTable;
bool V3Broken::isLinkable(const AstNode* nodep) { return s_linkableTable.isLinkable(nodep); }
//######################################################################
// Mark every node in the tree
class BrokenMarkVisitor final : public AstNVisitor {
// Mark every node in the tree
private:
// NODE STATE
// Nothing! // This may be called deep inside other routines
// // so userp and friends may not be used
// METHODS
void processAndIterate(AstNode* nodep) {
BrokenTable::addInTree(nodep, nodep->maybePointedTo());
iterateChildrenConst(nodep);
}
const uint8_t m_brokenCntCurrent = s_brokenCntGlobal.get();
// VISITORS
virtual void visit(AstNode* nodep) override {
// Process not just iterate
processAndIterate(nodep);
#ifdef VL_LEAK_CHECKS
UASSERT_OBJ(s_allocTable.isAllocated(nodep), nodep,
"AstNode is in tree, but not allocated");
#endif
UASSERT_OBJ(nodep->brokenState() != m_brokenCntCurrent, nodep,
"AstNode is already in tree at another location");
if (nodep->maybePointedTo()) s_linkableTable.addLinkable(nodep);
nodep->brokenState(m_brokenCntCurrent);
iterateChildrenConst(nodep);
}
public:
@@ -235,26 +164,40 @@ public:
};
//######################################################################
// Broken state, as a visitor of each AstNode
// Check every node in tree
class BrokenCheckVisitor final : public AstNVisitor {
bool m_inScope = false; // Under AstScope
// Constants for marking we are under/not under a node
const uint8_t m_brokenCntCurrentNotUnder = s_brokenCntGlobal.get(); // Top bit is clear
const uint8_t m_brokenCntCurrentUnder = m_brokenCntCurrentNotUnder | 0x80; // Top bit is set
// Current CFunc, if any
const AstCFunc* m_cfuncp = nullptr;
// All local variables declared in current function
std::unordered_set<const AstVar*> m_localVars;
// Variable references in current function that do not reference an in-scope local
std::unordered_map<const AstVar*, const AstNodeVarRef*> m_suspectRefs;
// Local variables declared in the scope of the current statement
std::vector<std::unordered_set<const AstVar*>> m_localsStack;
private:
static void checkWidthMin(const AstNode* nodep) {
UASSERT_OBJ(nodep->width() == nodep->widthMin()
|| v3Global.widthMinUsage() != VWidthMinUsage::MATCHES_WIDTH,
nodep, "Width != WidthMin");
}
void processAndIterate(AstNode* nodep) {
BrokenTable::setUnder(nodep, true);
const char* whyp = nodep->broken();
void processEnter(AstNode* nodep) {
nodep->brokenState(m_brokenCntCurrentUnder);
const char* const whyp = nodep->broken();
UASSERT_OBJ(!whyp, nodep,
"Broken link in node (or something without maybePointedTo): " << whyp);
if (nodep->dtypep()) {
UASSERT_OBJ(nodep->dtypep()->brokeExists(), nodep,
"Broken link in node->dtypep() to " << cvtToHex(nodep->dtypep()));
UASSERT_OBJ(VN_IS(nodep->dtypep(), NodeDType), nodep,
UASSERT_OBJ(nodep->dtypep(), nodep,
"Non-dtype link in node->dtypep() to " << cvtToHex(nodep->dtypep()));
}
if (v3Global.assertDTypesResolved()) {
@@ -267,17 +210,40 @@ private:
}
UASSERT_OBJ(!nodep->getChildDTypep(), nodep,
"childDTypep() non-null on node after should have removed");
if (const AstNodeDType* dnodep = VN_CAST(nodep, NodeDType)) checkWidthMin(dnodep);
if (const AstNodeDType* const dnodep = VN_CAST(nodep, NodeDType))
checkWidthMin(dnodep);
}
checkWidthMin(nodep);
}
void processExit(AstNode* nodep) { nodep->brokenState(m_brokenCntCurrentNotUnder); }
void processAndIterate(AstNode* nodep) {
processEnter(nodep);
iterateChildrenConst(nodep);
BrokenTable::setUnder(nodep, false);
processExit(nodep);
}
void processAndIterateList(AstNode* nodep) {
while (nodep) {
processAndIterate(nodep);
nodep = nodep->nextp();
};
}
void pushLocalScope() {
if (m_cfuncp) m_localsStack.emplace_back();
}
void popLocalScope() {
if (m_cfuncp) m_localsStack.pop_back();
}
bool isInScopeLocal(const AstVar* varp) const {
for (const auto& set : m_localsStack) {
if (set.count(varp)) return true;
}
return false;
}
virtual void visit(AstNodeAssign* nodep) override {
processAndIterate(nodep);
UASSERT_OBJ(!(v3Global.assertDTypesResolved() && nodep->brokeLhsMustBeLvalue()
&& VN_IS(nodep->lhsp(), NodeVarRef)
&& !VN_CAST(nodep->lhsp(), NodeVarRef)->access().isWriteOrRW()),
&& !VN_AS(nodep->lhsp(), NodeVarRef)->access().isWriteOrRW()),
nodep, "Assignment LHS is not an lvalue");
}
virtual void visit(AstScope* nodep) override {
@@ -295,6 +261,65 @@ private:
UASSERT_OBJ(
!(v3Global.assertScoped() && m_inScope && nodep->varp() && !nodep->varScopep()), nodep,
"VarRef missing VarScope pointer");
if (m_cfuncp) {
// Check if variable is an in-scope local, otherwise mark as suspect
if (const AstVar* const varp = nodep->varp()) {
if (!isInScopeLocal(varp)) {
// This only stores the first ref for each Var, which is what we want
m_suspectRefs.emplace(varp, nodep);
}
}
}
}
virtual void visit(AstCFunc* nodep) override {
UASSERT_OBJ(!m_cfuncp, nodep, "Nested AstCFunc");
m_cfuncp = nodep;
m_localVars.clear();
m_suspectRefs.clear();
m_localsStack.clear();
pushLocalScope();
processAndIterate(nodep);
// Check suspect references are all to non-locals
for (const auto& pair : m_suspectRefs) {
UASSERT_OBJ(m_localVars.count(pair.first) == 0, pair.second,
"Local variable not in scope where referenced: " << pair.first);
}
m_cfuncp = nullptr;
}
virtual void visit(AstNodeIf* nodep) override {
// Each branch is a separate local variable scope
pushLocalScope();
processEnter(nodep);
processAndIterate(nodep->condp());
if (AstNode* const ifsp = nodep->ifsp()) {
pushLocalScope();
processAndIterateList(ifsp);
popLocalScope();
}
if (AstNode* const elsesp = nodep->elsesp()) {
pushLocalScope();
processAndIterateList(elsesp);
popLocalScope();
}
processExit(nodep);
popLocalScope();
}
virtual void visit(AstNodeStmt* nodep) override {
// For local variable checking act as if any statement introduces a new scope.
// This is aggressive but conservatively correct.
pushLocalScope();
processAndIterate(nodep);
popLocalScope();
}
virtual void visit(AstVar* nodep) override {
processAndIterate(nodep);
if (m_cfuncp) {
m_localVars.insert(nodep);
m_localsStack.back().insert(nodep);
}
}
virtual void visit(AstNode* nodep) override {
// Process not just iterate
@@ -308,7 +333,7 @@ public:
};
//######################################################################
// Broken class functions
// Broken check entry point
void V3Broken::brokenAll(AstNetlist* nodep) {
// UINFO(9, __FUNCTION__ << ": " << endl);
@@ -318,24 +343,23 @@ void V3Broken::brokenAll(AstNetlist* nodep) {
UINFO(1, "Broken called under broken, skipping recursion.\n"); // LCOV_EXCL_LINE
} else {
inBroken = true;
BrokenTable::prepForTree();
BrokenMarkVisitor mvisitor(nodep);
BrokenCheckVisitor cvisitor(nodep);
BrokenTable::doneWithTree();
const BrokenMarkVisitor mvisitor{nodep};
const BrokenCheckVisitor cvisitor{nodep};
s_allocTable.checkForLeaks();
s_linkableTable.clear();
s_brokenCntGlobal.inc();
inBroken = false;
}
}
void V3Broken::addNewed(AstNode* nodep) { BrokenTable::addNewed(nodep); }
void V3Broken::deleted(AstNode* nodep) { BrokenTable::deleted(nodep); }
bool V3Broken::isAllocated(AstNode* nodep) { return BrokenTable::isAllocated(nodep); }
//######################################################################
// Self test
void V3Broken::selfTest() {
// Warmup addNewed and deleted for coverage, as otherwise only with VL_LEAK_CHECKS
FileLine* fl = new FileLine(FileLine::commandLineFilename());
auto* newp = new AstBegin(fl, "[EditWrapper]", nullptr);
#ifndef VL_LEAK_CHECKS
// Exercise addNewed and deleted for coverage, as otherwise only used with VL_LEAK_CHECKS
FileLine* const fl = new FileLine(FileLine::commandLineFilename());
const AstNode* const newp = new AstBegin(fl, "[EditWrapper]", nullptr);
addNewed(newp);
deleted(newp);
#endif
VL_DO_DANGLING(delete newp, newp);
}
+7 -6
View File
@@ -20,17 +20,18 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
// Forward declare so we can include this in V3Ast.h
class AstNode;
class AstNetlist;
class V3Broken final {
public:
static void brokenAll(AstNetlist* nodep);
static void addNewed(AstNode* nodep);
static void deleted(AstNode* nodep);
static bool isAllocated(AstNode* nodep);
static bool isLinkable(const AstNode* nodep);
static void addNewed(const AstNode* nodep);
static void deleted(const AstNode* nodep);
static void selfTest();
};
+47 -43
View File
@@ -39,7 +39,7 @@ public:
enum en : uint8_t { MODULE, CLASS, COVERAGE };
private:
enum en m_e;
const enum en m_e;
public:
// cppcheck-suppress noExplicitConstructor
@@ -60,24 +60,22 @@ private:
AstCFunc* makeNewFunc() {
const int funcNum = m_newFunctions.size();
const string funcName = m_basename + "_" + cvtToStr(funcNum);
AstCFunc* const funcp = new AstCFunc(m_modp->fileline(), funcName, nullptr, "void");
funcp->isStatic(!m_type.isClass()); // Class constructors are non static
AstCFunc* const funcp = new AstCFunc{m_modp->fileline(), funcName, nullptr, "void"};
funcp->isStatic(false);
funcp->isLoose(!m_type.isClass());
funcp->declPrivate(true);
funcp->slow(!m_type.isClass()); // Only classes construct on fast path
string preventUnusedStmt;
if (m_type.isClass()) {
funcp->argTypes(EmitCBaseVisitor::symClassVar());
preventUnusedStmt = "if (false && vlSymsp) {}";
preventUnusedStmt = "if (false && vlSymsp) {} // Prevent unused\n";
} else if (m_type.isCoverage()) {
funcp->argTypes(EmitCBaseVisitor::prefixNameProtect(m_modp) + "* self, "
+ EmitCBaseVisitor::symClassVar() + ", bool first");
preventUnusedStmt = "if (false && self && vlSymsp && first) {}";
} else { // Module
funcp->argTypes(EmitCBaseVisitor::prefixNameProtect(m_modp) + "* self");
preventUnusedStmt = "if (false && self) {}";
funcp->argTypes("bool first");
preventUnusedStmt = "if (false && first) {} // Prevent unused\n";
}
if (!preventUnusedStmt.empty()) {
funcp->addStmtsp(new AstCStmt{m_modp->fileline(), preventUnusedStmt});
}
preventUnusedStmt += " // Prevent unused\n";
funcp->addStmtsp(new AstCStmt(m_modp->fileline(), preventUnusedStmt));
m_modp->addStmtp(funcp);
m_numStmts = 0;
return funcp;
@@ -93,9 +91,9 @@ public:
}
V3CCtorsBuilder(AstNodeModule* nodep, const string& basename, VCtorType type)
: m_modp(nodep)
: m_modp{nodep}
, m_basename{basename}
, m_type(type) {
, m_type{type} {
// Note: The constructor is always called, even if empty, so we must always create at least
// one.
m_newFunctions.push_back(makeNewFunc());
@@ -110,13 +108,12 @@ public:
AstCFunc* const rootFuncp = makeNewFunc();
rootFuncp->name(m_basename);
for (AstCFunc* const funcp : m_newFunctions) {
AstCCall* const callp = new AstCCall(m_modp->fileline(), funcp);
AstCCall* const callp = new AstCCall{m_modp->fileline(), funcp};
if (m_type.isClass()) {
callp->argTypes("vlSymsp");
} else if (m_type.isCoverage()) {
callp->argTypes("self, vlSymsp, first");
} else { // Module
callp->argTypes("self");
} else {
if (m_type.isCoverage()) callp->argTypes("first");
callp->selfPointer("this");
}
rootFuncp->addStmtsp(callp);
}
@@ -130,35 +127,41 @@ private:
//######################################################################
void V3CCtors::evalAsserts() {
AstNodeModule* modp = v3Global.rootp()->modulesp(); // Top module wrapper
AstCFunc* funcp = new AstCFunc(modp->fileline(), "_eval_debug_assertions", nullptr, "void");
AstNodeModule* const modp = v3Global.rootp()->modulesp(); // Top module wrapper
AstCFunc* const funcp
= new AstCFunc{modp->fileline(), "_eval_debug_assertions", nullptr, "void"};
funcp->declPrivate(true);
funcp->isStatic(false);
funcp->isLoose(true);
funcp->slow(false);
funcp->ifdef("VL_DEBUG");
modp->addStmtp(funcp);
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
if (AstVar* varp = VN_CAST(np, Var)) {
if (AstVar* const varp = VN_CAST(np, Var)) {
if (varp->isPrimaryInish() && !varp->isSc()) {
if (AstBasicDType* basicp = VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
int storedWidth = basicp->widthAlignBytes() * 8;
int lastWordWidth = varp->width() % storedWidth;
if (const AstBasicDType* const basicp
= VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
const int storedWidth = basicp->widthAlignBytes() * 8;
const int lastWordWidth = varp->width() % storedWidth;
if (lastWordWidth != 0) {
// if (signal & CONST(upper_non_clean_mask)) { fail; }
AstNode* newp = new AstVarRef(varp->fileline(), varp, VAccess::READ);
AstVarRef* const vrefp
= new AstVarRef{varp->fileline(), varp, VAccess::READ};
vrefp->selfPointer("this");
AstNode* newp = vrefp;
if (varp->isWide()) {
newp = new AstWordSel(
newp = new AstWordSel{
varp->fileline(), newp,
new AstConst(varp->fileline(), varp->widthWords() - 1));
new AstConst(varp->fileline(), varp->widthWords() - 1)};
}
uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth);
newp = new AstAnd(varp->fileline(), newp,
const uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth);
newp = new AstAnd{varp->fileline(), newp,
new AstConst(varp->fileline(), AstConst::WidthedValue(),
storedWidth, value));
AstNodeIf* ifp = new AstIf(
storedWidth, value)};
AstNodeIf* const ifp = new AstIf{
varp->fileline(), newp,
new AstCStmt(varp->fileline(), "Verilated::overWidthError(\""
+ varp->prettyName() + "\");"));
new AstCStmt{varp->fileline(), "Verilated::overWidthError(\""
+ varp->prettyName() + "\");"}};
ifp->branchPred(VBranchPred::BP_UNLIKELY);
newp = ifp;
funcp->addStmtsp(newp);
@@ -173,23 +176,24 @@ void V3CCtors::cctorsAll() {
UINFO(2, __FUNCTION__ << ": " << endl);
evalAsserts();
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp;
modp = VN_CAST(modp->nextp(), NodeModule)) {
modp = VN_AS(modp->nextp(), NodeModule)) {
// Process each module in turn
{
V3CCtorsBuilder var_reset(modp, "_ctor_var_reset",
VN_IS(modp, Class) ? VCtorType::CLASS : VCtorType::MODULE);
V3CCtorsBuilder var_reset{modp, "_ctor_var_reset",
VN_IS(modp, Class) ? VCtorType::CLASS : VCtorType::MODULE};
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
if (AstVar* const varp = VN_CAST(np, Var)) {
if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset()) {
const auto vrefp = new AstVarRef(varp->fileline(), varp, VAccess::WRITE);
var_reset.add(new AstCReset(varp->fileline(), vrefp));
if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset()
&& !varp->isParam()) {
const auto vrefp = new AstVarRef{varp->fileline(), varp, VAccess::WRITE};
var_reset.add(new AstCReset{varp->fileline(), vrefp});
}
}
}
}
if (v3Global.opt.coverage()) {
V3CCtorsBuilder configure_coverage(modp, "_configure_coverage", VCtorType::COVERAGE);
V3CCtorsBuilder configure_coverage{modp, "_configure_coverage", VCtorType::COVERAGE};
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
if (AstCoverDecl* const coverp = VN_CAST(np, CoverDecl)) {
np = coverp->backp();
@@ -197,8 +201,8 @@ void V3CCtors::cctorsAll() {
}
}
}
if (AstClass* classp = VN_CAST(modp, Class)) {
AstCFunc* funcp = new AstCFunc(modp->fileline(), "~", nullptr, "");
if (const AstClass* const classp = VN_CAST(modp, Class)) {
AstCFunc* const funcp = new AstCFunc{modp->fileline(), "~", nullptr, ""};
funcp->isDestructor(true);
funcp->isStatic(false);
// If can be referred to by base pointer, need virtual delete
+36 -166
View File
@@ -13,14 +13,12 @@
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
// V3Class's Transformations:
// V3CUse's Transformations:
//
// Each module:
// Each cell:
// Create CUse for cell forward declaration
// Each class:
// Create string access functions
// Search for dtypes referencing class, and create CUse for forward declaraion
// Search for dtypes referencing class, and create CUse for forward declaration
//
//*************************************************************************
@@ -30,61 +28,37 @@
#include "V3Global.h"
#include "V3CUse.h"
#include "V3Ast.h"
#include "V3EmitCBase.h"
#include <unordered_map>
#include <set>
//######################################################################
class CUseState final {
private:
// MEMBERS
AstNodeModule* m_modInsertp; // Current module to insert AstCUse under
using UseString = std::pair<VUseType, std::string>;
std::map<const UseString, AstCUse*> m_didUse; // What we already used
// NODE STATE
// Entire netlist:
// AstClass::user1() -> bool. True if class needs to_string dumper
AstUser1InUse m_inuser1;
// AstClass::user2() -> bool. True if iterated
AstUser2InUse m_inuser2;
// METHODS
VL_DEBUG_FUNC; // Declare debug()
public:
AstCUse* newUse(AstNode* nodep, VUseType useType, const string& name) {
UseString key(useType, name);
if (m_didUse.find(key) == m_didUse.end()) {
AstCUse* newp = new AstCUse(nodep->fileline(), useType, name);
m_modInsertp->addStmtp(newp);
UINFO(8, "Insert " << newp << endl);
m_didUse[key] = newp;
}
return m_didUse[key];
}
// CONSTRUCTORS
explicit CUseState(AstNodeModule* nodep)
: m_modInsertp{nodep} {}
virtual ~CUseState() = default;
VL_UNCOPYABLE(CUseState);
};
// Visit within a module all nodes and data types they reference, finding
// any classes so we can make sure they are defined when Verilated code
// compiles
class CUseDTypeVisitor final : public AstNVisitor {
class CUseVisitor final : public AstNVisitor {
// NODE STATE
// AstNode::user1() -> bool. True if already visited
const AstUser1InUse m_inuser1;
// MEMBERS
CUseState& m_stater; // State for inserter
bool m_impOnly = false; // In details needed only for implementation
AstNodeModule* const m_modp; // Current module
std::set<std::pair<VUseType, std::string>> m_didUse; // What we already used
// METHODS
void addNewUse(AstNode* nodep, VUseType useType, const string& name) {
if (m_didUse.emplace(useType, name).second) {
AstCUse* const newp = new AstCUse{nodep->fileline(), useType, name};
m_modp->addStmtp(newp);
UINFO(8, "Insert " << newp << endl);
}
}
// VISITORS
virtual void visit(AstClassRefDType* nodep) override {
if (nodep->user2SetOnce()) return; // Process once
if (!m_impOnly) m_stater.newUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
// No class.h, it's inside the class package's h file
m_stater.newUse(nodep, VUseType::IMP_INCLUDE, nodep->classp()->classOrPackagep()->name());
if (nodep->user1SetOnce()) return; // Process once
if (!m_impOnly) addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
// Need to include extends() when we implement, but no need for pointers to know
VL_RESTORER(m_impOnly);
{
@@ -93,131 +67,27 @@ class CUseDTypeVisitor final : public AstNVisitor {
}
}
virtual void visit(AstNodeDType* nodep) override {
if (nodep->user2SetOnce()) return; // Process once
if (nodep->user1SetOnce()) return; // Process once
if (nodep->virtRefDTypep()) iterate(nodep->virtRefDTypep());
if (nodep->virtRefDType2p()) iterate(nodep->virtRefDType2p());
}
virtual void visit(AstNode* nodep) override {
if (nodep->user2SetOnce()) return; // Process once
if (nodep->dtypep() && !nodep->dtypep()->user2()) iterate(nodep->dtypep());
if (nodep->user1SetOnce()) return; // Process once
if (nodep->dtypep() && !nodep->dtypep()->user1()) iterate(nodep->dtypep());
iterateChildren(nodep);
}
virtual void visit(AstCell* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
// Currently no IMP_INCLUDE because we include __Syms which has them all
addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->modp()->name());
iterateChildren(nodep);
}
public:
// CONSTRUCTORS
explicit CUseDTypeVisitor(AstNodeModule* nodep, CUseState& stater)
: m_stater(stater) { // Need () or GCC 4.8 false warning
iterate(nodep);
}
virtual ~CUseDTypeVisitor() override = default;
VL_UNCOPYABLE(CUseDTypeVisitor);
};
class CUseVisitor final : public AstNVisitor {
// MEMBERS
CUseState m_state; // Inserter state
// METHODS
VL_DEBUG_FUNC; // Declare debug()
// Module use builders
void makeUseCells(AstNodeModule* nodep) {
for (AstNode* itemp = nodep->stmtsp(); itemp; itemp = itemp->nextp()) {
if (AstCell* cellp = VN_CAST(itemp, Cell)) {
// Currently no include because we include __Syms which has them all
m_state.newUse(nodep, VUseType::INT_FWD_CLASS, cellp->modp()->name());
}
}
}
void makeVlToString(AstClass* nodep) {
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "VL_TO_STRING", nullptr, "std::string");
funcp->argTypes("const VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(nodep)
+ ">& obj");
funcp->isMethod(false);
funcp->isConst(false);
funcp->isStatic(false);
funcp->protect(false);
AstNode* exprp = new AstCMath(nodep->fileline(), "obj ? obj->to_string() : \"null\"", 0);
exprp->dtypeSetString();
funcp->addStmtsp(new AstCReturn(nodep->fileline(), exprp));
nodep->addStmtp(funcp);
}
void makeToString(AstClass* nodep) {
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "to_string", nullptr, "std::string");
funcp->isConst(true);
funcp->isStatic(false);
funcp->protect(false);
AstNode* exprp = new AstCMath(nodep->fileline(),
R"(std::string("'{") + to_string_middle() + "}")", 0);
exprp->dtypeSetString();
funcp->addStmtsp(new AstCReturn(nodep->fileline(), exprp));
nodep->addStmtp(funcp);
}
void makeToStringMiddle(AstClass* nodep) {
AstCFunc* funcp
= new AstCFunc(nodep->fileline(), "to_string_middle", nullptr, "std::string");
funcp->isConst(true);
funcp->isStatic(false);
funcp->protect(false);
funcp->addStmtsp(new AstCStmt(nodep->fileline(), "std::string out;\n"));
std::string comma;
for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) {
if (auto* varp = VN_CAST(itemp, Var)) {
if (!varp->isParam()) {
string stmt = "out += \"";
stmt += comma;
comma = ", ";
stmt += itemp->origNameProtect();
stmt += ":\" + ";
if (itemp->isWide()) {
stmt += "VL_TO_STRING_W(";
stmt += cvtToStr(itemp->widthWords());
stmt += ", ";
} else {
stmt += "VL_TO_STRING(";
}
stmt += itemp->nameProtect();
stmt += ");\n";
nodep->user1(true); // So what we extend dumps this
funcp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
}
}
}
if (nodep->extendsp() && nodep->extendsp()->classp()->user1()) {
string stmt = "out += \"";
if (!comma.empty()) stmt += "\", \"+ ";
// comma = ", "; // Nothing further so not needed
stmt += nodep->extendsp()->dtypep()->nameProtect();
stmt += "::to_string_middle();\n";
nodep->user1(true); // So what we extend dumps this
funcp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
}
funcp->addStmtsp(new AstCStmt(nodep->fileline(), "return out;\n"));
nodep->addStmtp(funcp);
}
// VISITORS
virtual void visit(AstNodeModule* nodep) override {
if (v3Global.opt.trace()) {
AstCUse* usep
= m_state.newUse(nodep, VUseType::INT_FWD_CLASS, v3Global.opt.traceClassBase());
usep->protect(false);
}
makeUseCells(nodep);
{ CUseDTypeVisitor dtypeVisitor(nodep, m_state); }
if (AstClass* classp = VN_CAST(nodep, Class)) {
makeVlToString(classp);
makeToString(classp);
makeToStringMiddle(classp);
}
}
virtual void visit(AstNode*) override {} // All in AstNodeModule
public:
// CONSTRUCTORS
explicit CUseVisitor(AstNodeModule* nodep)
: m_state{nodep} {
iterate(nodep);
explicit CUseVisitor(AstNodeModule* modp)
: m_modp(modp) {
iterate(modp);
}
virtual ~CUseVisitor() override = default;
VL_UNCOPYABLE(CUseVisitor);
@@ -230,10 +100,10 @@ void V3CUse::cUseAll() {
UINFO(2, __FUNCTION__ << ": " << endl);
// Call visitor separately for each module, so visitor state is cleared
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp;
modp = VN_CAST(modp->nextp(), NodeModule)) {
modp = VN_AS(modp->nextp(), NodeModule)) {
// Insert under this module; someday we should e.g. make Ast
// for each output file and put under that
CUseVisitor visitor(modp);
CUseVisitor{modp};
}
V3Global::dumpCheckGlobalTree("cuse", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

Some files were not shown because too many files have changed in this diff Show More