Compare commits

...
418 Commits
Author SHA1 Message Date
Wilson Snyder 52d29d238c Version bump 2022-10-29 17:45:54 -04:00
Geza Lore 0675510eb9 DFG: Fix attempted evaluation of constants wider than 32 bits
Fixes #3718
2022-10-28 17:14:19 +01:00
Geza Lore 99791ac8b3 Reduce verbosity of DFG debug 2022-10-28 16:35:53 +01:00
Wilson Snyder ee26378394 Tests: Fix instability (#3717) 2022-10-28 10:32:52 -04:00
Geza Lore 54c4351c39 V3Const: Do not introduce redundant AstExtend
Fixes #3716
2022-10-28 14:54:00 +01:00
Geza Lore e504e9aced Optimize clocked processes to comb when referencing const variables
In V3Active, we try hard to turn `always @(a or b or c)` into an
`always_comb` if the only variables read in the block are also in the
sensitivity list. In addition, also allow this optimization when reading
variables that are not in the sensitivity list, but are known to be
constant/never changing after initialization. In particular lookup
tables introduced by V3Table are covered by this. This can have a
significant impact on designs that use the `always @(a or b or c)` style
for combinational logic.
2022-10-28 14:37:30 +01:00
Geza Lore ef2776034e Adjust instruction count estimates for AstCMethodHard
The cost of an AstCMethodHard right now is generally unknown. However,
VlTriggerVec::at is used a lot in conditions, so we make an effort
to estimate this correctly via 2 changes:
- In general when an AstVarRef appears as the target of an
  AstCMethodHard, we cost it as a simple address computation (an add)
- Check for VlTriggerVec::at explicitly when costing AstCMethodHard,
  which is essentially a load.

This can have a significant effect when there are a lot of unique
triggers in the design.
2022-10-28 14:37:30 +01:00
Wilson Snyder 5c658f8cd5 Fix width mismatch on inside operator (#3714). 2022-10-28 06:38:49 -04:00
Wilson Snyder 0ed7aaeabd Update include/gtkwave/fstapi.c from upstream. 2022-10-27 21:37:09 -04:00
Wilson Snyder 5f9b0929b4 Commentary: Changes update 2022-10-27 21:35:09 -04:00
Geza Lore 65290afe0a Fix VString::endsWith when suffix is longer than input string. 2022-10-27 18:38:44 +01:00
Kamil Rakoczy ed93a111c2 Fix deadlock in `timeprecision` when using systemC (#3707) 2022-10-26 04:50:28 -07:00
Wilson Snyder 3487701b04 Tests: Cover some previously uncovered warnings 2022-10-22 16:03:42 -04:00
Wilson Snyder d33a3d09f7 Tests: Fix bison-dependent .out 2022-10-22 14:35:36 -04:00
Wilson Snyder 0716a28816 Tests: Check for v3warn/v3error without a test 2022-10-22 13:46:49 -04:00
Wilson Snyder 8e1901da10 Tests: Cover some previously uncovered warnings 2022-10-22 13:45:48 -04:00
Wilson Snyder ecfa385f13 Test/cleanup endtable without table 2022-10-22 12:30:44 -04:00
Wilson Snyder 63df87e220 Commentary 2022-10-22 12:18:28 -04:00
Wilson Snyder 495d52d30f Test and fix extra end_keywords 2022-10-22 12:17:56 -04:00
Wilson Snyder 2df886b2b0 Tests: Check std:: package for mailbox/process/semaphore 2022-10-22 10:58:10 -04:00
Krzysztof Bieganski fcf0d03cd4 Dynamic triggers for non-static contexts (#3599)
In non-static contexts like class objects or stack frames, the use of
global trigger evaluation is not feasible. The concept of dynamic
triggers allows for trigger evaluation in such cases. These triggers are
simply local variables, and coroutines are themselves responsible for
evaluating them. They await the global dynamic trigger scheduler object,
which is responsible for resuming them during the trigger evaluation
step in the 'act' eval region. Once the trigger is set, they await the
dynamic trigger scheduler once again, and then get resumed during the
resumption step in the 'act' eval region.

Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-10-22 14:05:39 +00:00
Wilson Snyder 4154584c4b Commentary: Changes update 2022-10-21 20:04:07 -04:00
Wilson Snyder a57a3579c0 Fix false LATCH warning on 'unique if' (#3088). 2022-10-21 19:10:06 -04:00
Wilson Snyder 203993b018 Internals: Fix constructor style. 2022-10-21 19:03:40 -04:00
Wilson Snyder 347e9b4ec8 Fix cell assigning integer array parameters (#3299). 2022-10-21 18:26:39 -04:00
Wilson Snyder 9d02082801 Internals: Avoid VM_COVERAGE ifdef in library to get closer to libverilated.a 2022-10-21 17:09:06 -04:00
Krzysztof Bieganski 785c51dd0b Fix emitting timing debug info with --protect-ids (#3689) (#3701) 2022-10-21 16:56:44 -04:00
Jiuyang Liu 2e4f5c863f Fix VPI inline module naming mismatch (#3690) (#3694) 2022-10-21 13:04:42 -04:00
HungMingWu 196f3292d5 Improve V3Ast function usage ergonomics (#3650)
Signed-off-by: HungMingWu <[email protected]>
2022-10-21 14:12:12 +01:00
Arkadiusz Kozdra 627a144b83 Support access to constructs inside type parameters (#3702)
This changeset brings support for accesses like:

	class Cls#(type TYPE1);
	    TYPE1::some_method();
	endclass

It is done by delaying dot resolution on type parameters until they get
resolved by V3Param, and doing a more thorough reference skip.
2022-10-21 09:00:40 -04:00
Krzysztof Bieganski 5688d1a935 Internals: Add V3UniqueNames consistency assertion (#3692) 2022-10-21 07:05:38 -04:00
Geza Lore 8b0d71994d DFG: don't try to call DfgVertex::width() on arrays
In DFG DfgVertex::width() is only defined for vertices representing
packed values, which DfgVertex::hash() used to violate. The only
non-packed values at the moment are DfgVarArray, which is a
DfgVertexVar, which are handled specially anyway, so this is easy to
fix.

Fixes #3682
2022-10-21 10:57:13 +01:00
Wilson Snyder 79682e6072 Support empty generate_regions (#3695). [mpb27] 2022-10-20 22:04:50 -04:00
Wilson Snyder 7e1b92fa75 Add --get-supported to determine what features are in Verilator (#3688). 2022-10-20 21:42:30 -04:00
Wilson Snyder ca8ef7ce73 github: upload-artifacts/download-artifacts/cache@v3 (#3700) 2022-10-20 20:23:04 -04:00
Wilson Snyder 23d538fdaf github: upload-artifacts/download-artifacts/cache@v3 2022-10-20 20:04:45 -04:00
Wilson Snyder aaadc3def7 github: Use checkout@v3 (#3700) 2022-10-20 19:57:50 -04:00
Krzysztof Bieganski 444a4a760c Support timing with more Clang and libc++ versions (#3669) (#3698) 2022-10-20 19:54:22 -04:00
Wilson Snyder b42799f3b5 Commentary 2022-10-20 19:48:13 -04:00
Geza Lore 7b07cf912e Delete trigger dump when --protect-ids is used
In order to not leak signal names with --protect-ids, we simply make the
trigger dump function empty (this is a debug only construct).

Partial fix for #3689
2022-10-20 15:44:51 +01:00
Geza Lore 6a3ec17887 DFG: Do not inline SystemC variables
The emitted SystemC types (e.g. sc_bv) are not interchangeable with
Verilator internal C++ types (e.g.: VlWide), so the variables themselves
are not interchangeable (but can be assigned to/from each other). We can
preserve correctness simply be not inlining any SystemC variables (i.e.:
don't simplify any 'sc = nonSc' or 'nonSc = sc' assignments). SystemC
types only appear at top level ports so this should have no significant
impact.

Fixes #3688
2022-10-20 15:44:51 +01:00
Krzysztof Bieganski 8c3ca30c91 Fix print format warning on some platforms (#3699) 2022-10-20 10:15:29 -04:00
Arkadiusz Kozdra 0e4da3b0bf Support virtual interfaces (#3654) 2022-10-20 06:31:00 -04:00
Krzysztof Bieganski e6add5e0b8 Fix trace activity with --timing (#3576) (#3678) (#3696) 2022-10-20 06:28:55 -04:00
Krzysztof Bieganski 22243d1e49 Support class type params without defaults (#3693) 2022-10-19 21:59:26 -04:00
Krzysztof Bieganski bec0b7d4d0 Disallow delays with --lib-create (#3691) 2022-10-19 20:52:29 -04:00
Wilson Snyder f6f13c7fda Internals: Comment out debug that may flag ASAN problem (#3574) 2022-10-18 21:17:52 -04:00
Wilson Snyder e7068369fe Fix $display of fixed-width numbers (#3565). 2022-10-18 21:10:35 -04:00
Wilson Snyder b930d0731a Fix foreach and pre/post increment in functions (#3613). 2022-10-18 20:04:09 -04:00
Wilson Snyder 2723223884 Fix LSB error on --hierarchical submodules (#3539). 2022-10-18 17:29:51 -04:00
Kamil Rakoczy b6c116d4bf Internals: Add VL_MT_SAFE annotations to const functions (#3681) 2022-10-18 17:07:09 -04:00
Kamil Rakoczy 54e3f15dce Internals: Add attribute when using clang to VL_MT_SAFE and VL_MT_UNSAFE (#3685) 2022-10-18 05:15:33 -04:00
github action c057847760 Apply 'make format' 2022-10-17 23:52:01 +00:00
Topa Topino 46c5764383 Split UNUSED warning into genvar, param, and signal warnings (#3607) 2022-10-17 19:51:13 -04:00
Wilson Snyder 22ce36012e Add VERILATOR_TIMING define (#3684) 2022-10-17 18:18:56 -04:00
Geza Lore 5c65e0cfa1 Dfg: Fix incorrect folding of associative expressions with shared terms
Fixes #3679
2022-10-17 15:03:30 +01:00
Geza Lore 840e26b69a Fix incorrect return in DFG decomposition
Fixes #3676
2022-10-17 14:41:20 +01:00
Krzysztof Bieganski 5e79652922 Test tracing with --timing and --main (#3656)
Add a test for tracing with `--main` and `--timing`.

Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-10-17 12:36:37 +02:00
Wilson Snyder cb7b024e8f Commentary: Spelling, and add upgrade notes (#3462) 2022-10-16 11:10:41 -04:00
Wilson Snyder f3292a3651 Tests: Prove fixed (#2410) 2022-10-16 10:36:18 -04:00
Wilson Snyder 76ccd332a6 Internals: Remove DETECTARRAY, dead code. 2022-10-16 09:41:51 -04:00
Wilson Snyder 3cd2c8532d Internals: Cleanup spacing of Vi for loops. 2022-10-15 18:47:10 -04:00
Wilson Snyder c0739e908c Fix internal traceActivity to be zero reset not randomized. 2022-10-15 18:37:44 -04:00
Wilson Snyder 379a947379 Tests: Fix some internal code coverage holes 2022-10-15 13:59:07 -04:00
Wilson Snyder 916a3d9066 Fix --main --trace missing initial timestep (#3678). 2022-10-15 13:24:38 -04:00
Wilson Snyder b16b607b98 Commentary: Changes update 2022-10-15 11:04:03 -04:00
Wilson Snyder 732d5bea10 Commentary: Standard format for company contributions 2022-10-15 10:59:31 -04:00
Wilson Snyder d9a0d0ade2 Commentary (fix earlier commit) 2022-10-15 10:57:46 -04:00
Wilson Snyder e32ff0e1a6 Tests: Better mailbox and semaphore tests. 2022-10-15 10:37:24 -04:00
Wilson Snyder 5957156dee Tests: Fix bad result check. 2022-10-15 06:57:12 -04:00
Wilson Snyder 14f58ed6c7 Add error on real edge event control. 2022-10-15 06:21:34 -04:00
Arkadiusz Kozdra 038d57070b Support standalone 'this' in classes (#3675) (#2594) (#3248) 2022-10-14 08:55:55 -04:00
Krzysztof Bieganski 8a347248f5 Use AstDelay nodes for intra-assignment delays (#3672)
Also fix messy implementation of net delays.

Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-10-14 09:35:26 +02:00
Krzysztof Bieganski caed086516 Move Postponed logic after the eval loop (#3673)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-10-13 21:04:43 +02:00
Krzysztof Bieganski 68927d4fd3 Make class ref typing stricter (#3671)
Prevents the possibility of assigning an integer to a class reference,
both at the SystemVerilog and the emitted C++ levels.

Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-10-13 14:33:15 +02:00
Geza Lore b2070a9407 Commentary: Mention DFG in changes 2022-10-12 10:21:02 +01:00
github action 8dacbdec3a Apply 'make format' 2022-10-11 09:04:38 +00:00
Geza Lore 2a110c91cf Speed up DfgGraph decomposition algorithms 2022-10-11 09:55:08 +01:00
Krzysztof Bieganski ba052beccd Make reference to increment temporary an rvalue (#3659)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-10-10 13:58:05 +02:00
Wilson Snyder 6f9c585452 Spelling (#3664) 2022-10-09 14:18:14 -04:00
Wilson Snyder 18c26b90af Fix --trace with --main/--binary (#3664) 2022-10-09 14:16:44 -04:00
Geza Lore ff49f797e5 Speed up DfgGraph::addGraph
Append whole lists in one go, rather than going item by item.
2022-10-08 12:46:02 +01:00
Geza Lore c033a0d7c8 Optimize DfgGraph vertex storage
Vertices representing variables (DfgVertexVar) and constants (DfgConst)
are very common (40-50% of all vertices created in some large designs),
and we also need to, or can treat them specially in algorithms. Keep
these as separate lists in DfgGraph for direct access to them. This
improve verilation speed.
2022-10-08 12:46:02 +01:00
Geza Lore 461f3c1004 DFG: Remove topological sort
Cyclic components are now extracted separately, so there is no
functional reason to have to do a topological sort (previously we used it
to detect cyclic graphs). Removing it to gain some speed.
2022-10-08 12:46:02 +01:00
Geza Lore 90447d54d1 Make DfgConst hold V3Number directly
Remove intermediary AstConst. No functional change intended.
2022-10-08 12:46:02 +01:00
Geza Lore 439d30a953 Minor cleanup in V3Number 2022-10-08 12:46:02 +01:00
Iztok Jeras a972230b3a Commentary: example_binary does not uses SystemC code (#3662) 2022-10-08 09:50:15 +03:00
Geza Lore 29a080dd9b DFG: Special case representation of AstSel
AstSel is a ternary node, but the 'widthp' is always constant and is
hence redundant, and 'lsbp' is very often constant. As AstSel is fairly
common, we special case as a DfgSel for the constant 'lsbp', and as
'DfgMux` for the non-constant 'lsbp'.
2022-10-06 19:59:01 +01:00
Geza Lore 0570cb8d9f DFG: Correctly set dtype when converting DfgCountOnes to Ast 2022-10-06 19:59:01 +01:00
Geza Lore 6fa14bf029 Speed up DfgPeephole in various ways 2022-10-06 19:59:01 +01:00
Geza Lore 4f0158b5e0 Speed up Dfg common sub-expression elimination
Added a DfgVertex::user() mechanism for storing data in vertices.
Similar in spirit to AstNode user data, but the generation counter is
stored in the DfgGraph the vertex is held under. Use this to cache
DfgVertex::hash results, and also speed up DfgVertex hashing in general.

Use these and additional improvements to speed up CSE.
2022-10-06 19:59:01 +01:00
Krzysztof Bieganski 97add4d57a Fix null access on optimized-out fork statements (#3658)
`V3SchedTiming` currently assumes that if a fork still exists, it must
have statements within it (otherwise it would have been deleted by
`V3Timing`). However, in a case like this:
```
module t;
    reg a;
    initial fork a = 1; join
endmodule
```
the assignment in the fork is optimized out by `V3Dead` after
`V3Timing`. This leads to `V3SchedTiming` accessing fork's `stmtsp`
pointer, which at this point is null. This patch addresses that issue.

Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-10-06 15:38:59 +02:00
Geza Lore 5b742571d3 DFG: run removeVars after CSE
This enables removing some more redundant variables.
2022-10-06 09:31:56 +01:00
Geza Lore a83043d735 DfgPeephole: Rework folding of associative operations
Allow constant folding through adjacent nodes of all associative
operations, for example '((a & 2) & 3)' or '(3 & (2 & a))' can now be
folded into '(a & 2)' and '(2 & a)' respectively. Also improve speed of
making associative expression trees right leaning by using rotation of
the existing vertices whenever instead of allocation of new nodes.
2022-10-06 09:10:26 +01:00
Geza Lore 22fcd616aa DfgPeephole: Further restrict PUSH_REDUCTION_THROUGH_CONCAT
Only apply when there is guaranteed to be a subsequent constant folding
and elimination of some of the expression, otherwise this sometimes
interferes with the simplification of concatenations and harms overall
performance.
2022-10-06 09:10:26 +01:00
Krzysztof Bieganski 1a8188e1b4 Fix linker errors in user-facing timing functions (#3657)
Before this change, a design verilated with `--timing` that does not
actually use timing features would be emitted with `eventsPending` and
`nextTimeSlot` declared in the top class. However, their definitions
would be missing, leading to linker errors during design compilation.
This patch makes Verilator always emit the definitions, which prevents
linker errors. Trying to use `nextTimeSlot` without delays in the design
will result in an error at runtime.
2022-10-05 18:16:05 -04:00
Geza Lore f87fe4c3b4 DfgPeephole: add constant folding for all integer types
Also added a testing only -fno-const-before-dfg option, as otherwise
V3Const eats up a lot of the simple inputs. A lot of the things V3Const
swallows in the simple cases can make it to DFG in complex cases, or DFG
itself can create them during optimization. In any case to save
complexity of testing DFG constant folding, we use this option to turn
off V3Const prior to the DFG passes in the relevant test.
2022-10-05 12:05:40 +01:00
Geza Lore f23f3ca907 Try to ensure DFG peephole patterns don't grow the graph
Some optimizations are only a net win if they help us remove a graph
node (or at least ensure they don't grow the graph), or yields otherwise
special logic, so try to apply them only in these cases.
2022-10-04 18:54:46 +01:00
Geza Lore 965d99f1bc DFG: Make implementation more similar to AST
Use the same style, and reuse the bulk of astgen to generate DfgVertex
related code. In particular allow for easier definition of custom
DfgVertex sub-types that do not directly correspond to an AstNode
sub-type. Also introduces specific names for the fixed arity vertices.
No functional change intended.
2022-10-04 15:49:30 +01:00
Krzysztof Bieganski 56ac054fb2 Internals: Refactor verilated_timing.* (#3653).
* Put suspended coroutine source location in a separate struct,
* Have `dump()` always print, wrap calls in `VL_DEBUG_IF`,
* Improve const correctness.
2022-10-03 11:40:30 -04:00
Wilson Snyder ced82cbac4 Internals: Add some internal coverage exclusions etc. No functional change. 2022-10-03 10:57:37 -04:00
Geza Lore 2fc1746ef5 Tracing: Clear offload buffer pointers when no longer needed
These are also used as a marker (when non-nullptr) when creating a
buffer. Reset them when they are not valid to avoid invalid write if a
buffer is created after a close (due to a subsequent re-open).

Fixes #3651.
2022-10-03 10:50:47 +01:00
Wilson Snyder 10fc1f757c Internals: cppcheck cleanups. No functional change intended. 2022-10-02 23:04:55 -04:00
Wilson Snyder 90009b9ec7 Commentary: Fix sphinx doc warnings 2022-10-02 16:47:32 -04:00
Wilson Snyder 4367e03e46 Internals: Make VL_UNREACHABLE similar to std::unreachable() 2022-10-02 16:35:45 -04:00
Wilson Snyder c9634695a7 Fix std::exchange for C++11 compilers 2022-10-02 16:25:11 -04:00
Geza Lore 2a12b052f2 DFG: handle simple always blocks 2022-10-01 16:46:58 +01:00
Geza Lore 84b9502af4 DFG: Add more peephole patterns 2022-10-01 16:46:58 +01:00
Geza Lore 694bdbc130 DFG: Improve .dot dumps slightly 2022-10-01 16:46:58 +01:00
Wilson Snyder 880cac2fdd Merge branch 'master' into develop-v5 2022-10-01 11:24:55 -04:00
github action a204b24fcf Apply 'make format' 2022-10-01 15:06:12 +00:00
Marcel Chang 526e6b9fc7 Add --dump-tree-dot to enable dumping Ast Tree .dot files (#3636) 2022-10-01 11:05:33 -04:00
github action f1ba6cb517 Apply 'make format' 2022-10-01 14:53:40 +00:00
Kanad Kanhere 159cf0429c Support linting for top module interfaces (#3635) 2022-10-01 10:48:37 -04:00
Ryszard Rozak 46b8dca360 Add handling of tristate select/extend (#3604) 2022-10-01 10:34:30 -04:00
Wilson Snyder 4db998d357 CI: coverage on 22.04 2022-10-01 10:09:14 -04:00
Wilson Snyder 0b843ada03 devel release 2022-10-01 08:34:43 -04:00
Wilson Snyder 746c7ea8f7 Version bump 2022-10-01 08:28:27 -04:00
Wilson Snyder 5ed882faf2 Fix unused compiler warning when not VL_THREADED. 2022-09-30 23:41:35 -04:00
Wilson Snyder fa4b10b4d9 Commentary: Changes update 2022-09-30 23:03:26 -04:00
Geza Lore cc51966ad1 DFG: Remove unconneced variables early 2022-09-30 11:53:03 +01:00
Geza Lore c9d6344f2f DFG: Extract cyclic components separately
A lot of optimizations in DFG assume a DAG, but the more things are
representable, the more likely it is that a small cyclic sub-graph is
present in an otherwise very large graph that is mostly acyclic. In
order to avoid loosing optimization opportunities, we explicitly extract
the cyclic sub-graphs (which are the strongly connected components +
anything feeing them, up to variable boundaries) and treat them
separately. This enables optimization of the remaining input.
2022-09-30 09:51:10 +01:00
Geza Lore acebafcbc2 DFG: Partial support for unpacked arrays
Representation and Ast / Dfg conversions available, for element-wise
access only. Not much optimization yet (only CSE).
2022-09-29 19:00:45 +01:00
Geza Lore 4a1a2def95 DFG: make variable inlining part of the peephole optimizer
This saves some traversals and prepares us to better handle cyclic DFGs.
2022-09-29 18:40:10 +01:00
Geza Lore 09e352ef66 DFG: support hashing of graphs circular through variables
No functional change
2022-09-29 18:40:10 +01:00
Geza Lore 17976d7401 DFG: fix REPLACE_EQ_OF_CONST_AND_CONST peephole pattern 2022-09-29 18:40:10 +01:00
Wilson Snyder f25a9a4c80 examples: Use SC_ZERO_TIME in tracing (#3646) 2022-09-28 19:11:00 -04:00
Wilson Snyder cd2a5771b8 Add --timing to --binary (#3625). 2022-09-28 19:02:23 -04:00
Krzysztof Bieganski 9c2ead90d5 Add custom memory management for verilated classes (#3595)
This change introduces a custom reference-counting pointer class that
allows creating such pointers from 'this'. This lets us keep the
receiver object around even if all references to it outside of a class
method no longer exist. Useful for coroutine methods, which may outlive
all external references to the object.

The deletion of objects is deferred until the next time slot. This is to
make clearing the triggered flag on named events in classes safe
(otherwise freed memory could be accessed).
2022-09-28 18:54:18 -04:00
Geza Lore a999c73ce0 Commentary 2022-09-28 14:43:40 +01:00
Wilson Snyder b92173bf3d Add --binary option as alias of --main --exe --build (#3625). 2022-09-28 09:04:33 -04:00
github action 91823c41c5 Apply 'make format' 2022-09-28 02:22:05 +00:00
Wilson Snyder c6bce636ee Merge branch 'master' into develop-v5 2022-09-27 22:19:04 -04:00
Wilson Snyder 75a70bee6d Update to clang-format-14 on Ubuntu22.04 2022-09-27 21:47:45 -04:00
Wilson Snyder 1b1907af49 Tests: Rename some tests 2022-09-27 18:42:03 -04:00
Jake Merdich 1b18eee5dc Tests: Fix CCache test not properly skipped (#3643)
If the skip condition happens, the rest still runs and it fails. Don't do that.
2022-09-27 09:11:49 -04:00
Ryszard Rozak 4931e48016 Support resolving assignments with equal strengths (#3637) 2022-09-26 21:21:37 -04:00
Geza Lore 1b17acdb01 DFG: Support AstSel and AstConcat on LHS of assignments
Added DfgVertexVariadic to represent DFG vetices with a varying number
of source operands. Converted DfgVar to be a variadic vertex, with each
driver corresponding to a fixed range of bits in the packed variable.
This allows us to handle AstSel on the LHS of assignments. Also added
support for AstConcat on the LHS by selecting into the RHS as
appropriate.

This improves OpenTitan ST speed by ~13%
2022-09-26 19:54:52 +01:00
Geza Lore 9c1cc5465d DFG: Support packed structure and union types 2022-09-26 18:31:50 +01:00
Geza Lore d8b5359fcb Merge branch 'master' into develop-v5 2022-09-26 14:45:08 +01:00
Geza Lore f96454adcc gitignore .gdb_history 2022-09-26 14:43:09 +01:00
Geza Lore 9da012568c Ensure DFG stats are consistent 2022-09-26 14:38:26 +01:00
Geza Lore 9a20a258f5 Omit AstNode::m_editCount in release build
This is only a debugging aid at this point, so compile out of the
release build. This reduces peak memory consumption by 4-5%. We still
keep the global counters to detect the tree have changed, to avoid
unnecessary dumps.
2022-09-25 08:57:33 +01:00
Geza Lore 10796457d2 V3Life: don't depend on AstNode::editCountGbl()
No functional change intended.
2022-09-24 20:45:30 +01:00
Geza Lore 78e659a142 Reduce size of FileLine
Multiple tricks to reduce the size of class FileLine from 72 to 40
bytes:

- Reduce file name index from 32 to 16 bits. This still allows 64K
  unique input files, which is hopefully enough.
- Intern message/warning enable bitset and use a 16-bit index, again
  allowing 64K unique sets which is hopefully enough.
- Put the m_waive flag into the sign bit of one of the line numbers.
- Use explicit reference counting to avoid overhead of shared_ptr.

Added assertions to ensure interned data fits within it's index space.

This saves ~5-10% peak memory consumption at no measurable run-time cost
on various designs.
2022-09-24 20:16:21 +01:00
Geza Lore 2f50642ecb Exclude VL_DEFINE_DEBUG_FUNCTIONS from code coverage 2022-09-23 17:20:24 +01:00
Geza Lore 47bce4157d Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.

This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.

The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.

For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.

The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.

Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 16:46:22 +01:00
Geza Lore 3a8a314566 Merge branch 'master' into develop-v5 2022-09-23 11:21:12 +01:00
Geza Lore 050060b139 Make enum constructors and operators constexpr 2022-09-23 11:10:28 +01:00
Geza Lore ddb678cc5b Merge branch 'master' into develop-v5 2022-09-22 17:33:36 +01:00
Geza Lore 63c694f65f Streamline dump control options
- Rename `--dump-treei` option to `--dumpi-tree`, which itself is now a
  special case of `--dumpi-<tag>` where tag can be a magic word, or a
  filename
- Control dumping via static `dump*()` functions, analogous to `debug()`
- Make dumping independent of the value of `debug()` (so dumping always
  works even without the debug flag)
- Add separate `--dumpi-graph` for dumping V3Graphs, which is again a
  special case of `--dumpi-<tag>`
- Alias `--dump-<tag>` to `--dumpi-<tag> 3` as before
2022-09-22 17:24:41 +01:00
github action 12093e6939 Apply 'make format' 2022-09-21 19:22:15 +00:00
Geza Lore 9949a6cd17 Generate AstGen::checkTreeiter to enforce Ast op*p use
Use astgen to generate a more thorough version of AstNode::checkTree,
which checks that operands are or consistent structure and type, as
described in the @astgen op directives. Also change checkTree to always
run when --debug-check is given.

Fix discovered fallout.
2022-09-21 18:12:11 +01:00
Geza Lore 4600932d8c Remove unused files 2022-09-21 14:16:20 +01:00
Geza Lore 79ded3bb61 Merge branch 'master' into develop-v5 2022-09-21 14:15:21 +01:00
Geza Lore 95145038b4 Generate AstNode accessors via astgen
Introduce the @astgen directives parsed by astgen, currently used for
the generation child node (operand) accessors. Please see the updated
internal documentation for details.
2022-09-21 14:05:27 +01:00
Geza Lore ce03293128 Generate AstNode accessors via astgen
Introduce the @astgen directives parsed by astgen, currently used for
the generation child node (operand) accessors. Please see the updated
internal documentation for details.
2022-09-21 13:56:03 +01:00
Geza Lore 72e7271a14 Merge branch 'master' into develop-v5 2022-09-21 12:19:00 +01:00
Geza Lore 0a8cfb8d2c Put dump file in TEST_OBJ_DIR 2022-09-21 11:35:03 +01:00
Kamil Rakoczy 0b07679ff2 v3errorEnd: look for instance only when warning is not ignored (#3632)
This approach reduced total time of V3Undriven stage from 34,2s to 2,5s
in design containing almost 400 000 unused variables.

Signed-off-by: Kamil Rakoczy <[email protected]>
2022-09-21 10:54:23 +01:00
Wilson Snyder 550a5111b7 Commentary 2022-09-20 20:28:43 -04:00
Wilson Snyder d162619bd3 Merge branch 'master' into develop-v5 2022-09-20 20:06:21 -04:00
Wilson Snyder 5df14627fd Fix 32-bit build of previous commit 2022-09-20 18:23:44 -04:00
Mariusz Glebocki fc3ce29845 Improve Verilation memory by reducing V3Number size (#3521) 2022-09-20 16:46:47 -04:00
Yu-Sheng Lin bba800f2d6 Fix calling trace() after open() segfault (#3610) (#3627) 2022-09-20 16:45:09 -04:00
Ryszard Rozak fe2a1e1749 Remove assignments with strengths weaker than strongest non-tristate RHS (#3629) 2022-09-19 04:54:20 -04:00
Wilson Snyder fc4ffd454e Rename --bin to --build-dep-bin. 2022-09-18 10:32:43 -04:00
Wilson Snyder 8dae4ad93a Tests: Rename to avoid dash. 2022-09-18 10:19:15 -04:00
Wilson Snyder 8da261f302 Tests: Rename to avoid dash. 2022-09-18 10:19:15 -04:00
Geza Lore 7bc7b5372e Merge branch 'master' into develop-v5 2022-09-17 16:12:28 +01:00
Geza Lore 7d88e63bab astgen: generate type specific addNext, remove astNextNull
Generate type specific static overloads of Ast<Node>::addNext, which
return the correct sub-type of the 'this' they were invoked on.

Also remove AstNode::addNextNull, which is now only used in the parser,
implement in verilog.y directly as a template function.
2022-09-17 15:05:22 +01:00
Wilson Snyder a214fd1f78 Internals: Fix constructor syntax in new develop-v5 code 2022-09-17 08:56:41 -04:00
Wilson Snyder 79be097e34 Sort -V env variable output 2022-09-17 08:17:55 -04:00
Wilson Snyder 11b0d36ba2 Merge cleanups from 'develop-v5'. No functional change 2022-09-17 08:17:22 -04:00
Wilson Snyder 1234ee5fd2 Merge branch 'master' into develop-v5 2022-09-17 08:02:25 -04:00
Wilson Snyder 80b73859a2 Commentary: Some fixes from 'develop-v5' 2022-09-17 08:00:40 -04:00
Wilson Snyder a548a77f26 Merge branch 'develop-v5' into master: Changes file 2022-09-17 07:58:56 -04:00
Wilson Snyder d6bfb37e6a Docker: Use 22.04 2022-09-17 07:47:06 -04:00
Geza Lore af305bf280 Merge branch 'master' into develop-v5 2022-09-16 16:24:36 +01:00
Geza Lore 38a8d7fb2e Remove redundant 'inline' keywords from definitions
Also add checks to t/t_dist_cppstyle
2022-09-16 15:52:25 +01:00
Geza Lore 0c70a0dcbf Remove redundant 'virtual' keywords from overridden methods
'virtual' is redundant when 'override' is present, so keep only
'override'.

Add t/t_dist_cppstyle.pl to check for this.
2022-09-16 15:19:38 +01:00
Geza Lore d16619fe86 astgen: Explicitly generate AstNode members
Generate boilerplate members of AstNode sub-types directly via astgen.
This is in preparation for generating additional members.
2022-09-16 11:18:20 +01:00
Wilson Snyder ab6e1c2399 Commentary on --main 2022-09-15 20:26:08 -04:00
Wilson Snyder 2dc85a5acd Internals: enum constructor cleanups. No functional change intended. 2022-09-15 19:58:10 -04:00
Kamil Rakoczy dbe1348b4c Tests: Fix earlier commit, add build jobs to stats (#3623) (#3626) 2022-09-15 11:29:50 -04:00
Geza Lore 22846df03e Merge branch 'master' into develop-v5 2022-09-15 14:01:19 +01:00
Wilson Snyder d74536a4dc Internals: Cleanup some constructors. No functional change intended. 2022-09-15 08:54:04 -04:00
Kamil Rakoczy da20da264b Add --build-jobs, and rework arguments for -j (#3623) 2022-09-15 08:28:58 -04:00
Geza Lore 22b9dfb9c9 Split and re-order AstNode definitions (#3622)
- Move DType representations into V3AstNodeDType.h
- Move AstNodeMath and subclasses into V3AstNodeMath.h
- Move any other AstNode subtypes into V3AstNodeOther.h
- Fix up out-of-order definitions via inline methods and implementations
  in V3Inlines.h and V3AstNodes.cpp
- Enforce declaration order of AstNode subtypes via astgen, 
  which will now fail when definitions are mis-ordered.
2022-09-15 13:10:39 +01:00
Geza Lore 27031ed688 Merge branch 'master' into develop-v5 2022-09-15 10:28:35 +01:00
Wilson Snyder e43c089ab8 Commentary 2022-09-14 23:26:34 -04:00
Wilson Snyder d85b909054 Internals: Use std:: for mem and str functions. 2022-09-14 21:10:19 -04:00
Wilson Snyder 75fd71d7e5 Add --main to generate main() C++ (previously was experimental only) (#3265). 2022-09-14 20:18:40 -04:00
Wilson Snyder 9efd64ab98 Commentary 2022-09-14 20:13:28 -04:00
Wilson Snyder 7aa01625d8 Commentary: Changes update 2022-09-14 08:15:42 -04:00
Ryszard Rozak a3c58d7b70 Support IEEE constant signal strengths (#3601). 2022-09-14 07:39:27 -04:00
Kamil Rakoczy ae466b1703 Internals: Improve Verilation peak memory usage in V3Subst (#3512). 2022-09-14 07:37:51 -04:00
Geza Lore 2564484429 astgen: Rewrite in a more OOP way, in preparation for extensions
Rely less on strings and represent AstNode classes as a 'class Node',
with all associated properties kept together, rather than distributed
over multiple dictionaries or constructed at retrieval time.

No functional change intended.
2022-09-13 21:54:12 +01:00
Kamil Rakoczy 93a044f587 Internals: Rework addFilesp towards parallel emit (#3620). No functional change intended. 2022-09-13 12:15:34 -04:00
Wilson Snyder 81fe35ee2e Fix typedef'ed class conversion to boolean (#3616). 2022-09-12 18:03:56 -04:00
Geza Lore 08b6bdddf9 Update default --mod-prefix when --prefix is repeated
Fixes #3603
2022-09-12 17:25:09 +01:00
Kamil Rakoczy 4d49db48a3 Internals: Remove usage of user1 from EmitCTrace (#3617). No Functional change intended. 2022-09-12 12:00:41 -04:00
Kamil Rakoczy 9b2266f68c Internals: Remove usage of global state in V3EmitCFunc (#3615). No functional change intended. 2022-09-12 11:59:14 -04:00
Wilson Snyder 752f425025 Tests: Process/Semaphore/Mailbox testing (all fail until supported) 2022-09-11 13:05:24 -04:00
Gustav Svensk 47262cd4ec Fix arguments in non-static method call (#3547) (#3582) 2022-09-11 12:33:31 -04:00
Wilson Snyder 47e64535d6 Commentary 2022-09-11 12:25:44 -04:00
Wilson Snyder 45d622e4bc Github actions: Avoid cpan cert expirations. 2022-09-08 11:06:44 -04:00
Wilson Snyder 249feaae7c Tests: Remove lint-py, need ci package. 2022-09-07 22:59:32 -04:00
Wilson Snyder 9a27004ae5 Github actions: Avoid cpan cert expirations. 2022-09-07 22:49:09 -04:00
Wilson Snyder 42283150d7 Actions: Use 22.04 for contrib action 2022-09-07 22:13:02 -04:00
Wilson Snyder 5a1bcf9794 Tests: Add lint-py checker 2022-09-07 22:04:57 -04:00
Wilson Snyder 361cef4633 Fix pylint warnings. 2022-09-07 21:48:52 -04:00
Geza Lore 90ab746a42 Make it possible to parallelize ico and act scheduling sections
Small fixup patch so the 'ico' and 'act' scheduling sections could be
ordered as multi-threaded. However, we still only order these single
threaded at the moment (but switching them to multi-threaded now works).
2022-09-06 16:01:13 +01:00
github action e94cdcf29c Apply 'make format' 2022-09-05 22:43:09 +00:00
Mladen Slijepcevic 1af046986d Fix thread saftey in SystemC VL_ASSIGN_SBW/WSB (#3494) (#3513). 2022-09-05 18:42:12 -04:00
Wilson Snyder 1c9263a25b Commentary 2022-09-05 15:20:08 -04:00
Geza Lore fd6275a62b Merge branch 'master' into develop-v5 2022-09-05 17:03:43 +01:00
Krzysztof Bieganski 6b6790fc50 Preserve return type of AstNode::addNext via templating (#3597)
No functional change intended.

Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-09-05 16:56:57 +01:00
Krzysztof Bieganski fb931087ab Add stats tracking for V3Undriven. (#3600)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-09-05 16:20:38 +01:00
Krzysztof Bieganski a2e1b32a1c Fix inlining of forks (#3594)
Before this change, some forked processes were being inlined in
`V3Timing` because they contained no `CAwait`s. This only works under
the assumption that no `CAwait`s will be added there later, which is not
true, as a function called by a forked process could be turned into a
coroutine later. The call would be wrapped in a new `CAwait`, but the
process itself would have already been inlined at this point.

This commit moves the inlining to `transformForks` in `V3SchedTiming`,
which is called at a point when all `CAwait`s are already in place.

Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-09-05 15:19:19 +01:00
Krzysztof Bieganski 54f89bce42 Move SenExprBuilder to a header. (#3598)
No functional change intended.

Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-09-05 15:17:51 +01:00
Krzysztof Bieganski 8b19d02e3b Fix co_await VlNow{} being added too many times (#3596)
(or not at all)

Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-09-05 11:46:34 +01:00
Krzysztof Bieganski da7ad35577 Fix fork debug output (#3593)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-09-05 11:27:24 +01:00
Geza Lore 937e893b6d Build verilator_bin with -O3 (#3592)
This is consistently a few percent faster.
2022-09-03 22:10:07 +01:00
Geza Lore d42a2d6494 Fix V3Gate crash on circular logic
The recent patch to defer substitutions on V3Gate crashes on circular
logic that has cycle length >= 3 with all inlineable signals (cycle
length 2 is detected correctly and is not inlined). Fix by stopping
recursion at the loop-back edge.

Fixes #3543
2022-09-02 19:58:58 +01:00
Geza Lore 8e8f4b1e5c Remove AstVarScope::valuep() and related code
This is detritus from when V3TraceDecl used to run after V3Gate, today
V3TraceDecl runs before V3Gate and this value has no function at all.

No functional change intended.
2022-09-02 16:44:13 +01:00
Geza Lore 298f71f2b1 Merge branch 'master' into develop-v5 2022-09-02 12:19:35 +01:00
Geza Lore 2ba39b25f1 Replace dynamic_casts with static_casts
dynamic_cast is not free. Replace obvious instances (where the result is
unconditionally dereferenced) with static_cast in contexts with
performance implications.
2022-09-02 12:08:34 +01:00
Geza Lore 5c828b7e60 V3Partition: use V3Lists to keep track of SiblingMCs
Replace std::set<SiblingMC> with V3Lists to keep track of SiblingMCs
associated with MTasks, use a std::set<LogicMTask*> for ensuring
uniqueness. This yields a bit more speed in PartContraction.
2022-09-01 19:40:44 +01:00
Geza Lore 4640bea31a V3Partition: More improvements for PartFixDataHazards
- Remove redundant loop through the MTask graph
- Gather variables directly from the OrderGraph, which is simpler and
  faster.
2022-09-01 16:30:04 +01:00
Geza Lore 875361d7ce V3Partition: Reduce working set size of PartContraction (#3587)
This yields an additional 25% speedup of MT scheduling.
2022-09-01 16:29:40 +01:00
Wilson Snyder 849bb5590a Merge branch 'master' into develop-v5 2022-08-31 19:51:07 -04:00
Wilson Snyder 8d0c06e570 devel release 2022-08-31 19:49:24 -04:00
Wilson Snyder 5b2fbf4f37 Version bump 2022-08-31 19:46:45 -04:00
Wilson Snyder 592dab2bdb Commentary: Changes update 2022-08-31 19:27:43 -04:00
Wilson Snyder 51daa64e9a Fix --hierarchical with order-based pin connections (#3585). 2022-08-31 18:12:21 -04:00
Geza Lore c0f9b0d8f6 V3Partition: Refactor initialization of MTask dependencies
No functional change
2022-08-31 16:54:04 +01:00
Geza Lore 505bba14eb Improve PartFixDataHazards for clarity and speed.
- Use modern C++
- Implement OrderLogicVertex->LogicMTask map with
  OrderLogicVertex::userp(), insteas of std::unordered_map
- Simplify data structures
- Simplify code and assert properties

No functional change.
2022-08-31 16:52:05 +01:00
Geza Lore ebbe24966c Remove unnecessary virtual methods 2022-08-31 16:52:05 +01:00
Geza Lore 881c3f6e40 Minor optimization of PartContraction
Remove rarely used debug code from initialization loop.
2022-08-31 16:52:05 +01:00
Geza Lore 546aeab9f2 V3Order: Minor refactoring for clarity
Refactor ProcessMoveBuildGraph utilizing the fact that OrderGraph is a
bipartite graph, also remove unnecessary unordered_map and distribute
variable domain map. No functional change.
2022-08-31 16:52:05 +01:00
Geza Lore 8de21e9bb7 Document and ensure OrderGraph is bipartite
Minor refactoring and documentation. No functional change.
2022-08-31 16:52:05 +01:00
Geza Lore 2ecda74471 Merge branch 'master' into develop-v5 2022-08-31 10:45:18 +01:00
Aleksander Kiryk 2136afde6b Support negated properties (#3572) 2022-08-30 06:33:42 -04:00
Wilson Snyder ea55db7286 Internals: Cleanup some string constructors. No functional change. 2022-08-30 01:02:39 -04:00
Wilson Snyder 819e8741cc Merge branch 'master' into develop-v5 2022-08-30 00:20:21 -04:00
Wilson Snyder 6a5f77b278 Internals: Cleanup some string/model constructors. No functional change. 2022-08-29 23:50:32 -04:00
Wilson Snyder 8658a0d7dc Internals: Constructor format update. No functional change. 2022-08-29 23:05:52 -04:00
Wilson Snyder c335aad25f Fix --hierarchical with order-based pin connections (#3583). 2022-08-29 22:49:19 -04:00
Wilson Snyder 9d9d647c1f Fix indentation of --protect import function SV code. 2022-08-29 22:28:02 -04:00
Wilson Snyder d47a37fb76 Internals: Cleanup constructors etc. No functional change. 2022-08-29 22:17:27 -04:00
Aleksander Kiryk 24ec84851a Support $sampled (#3569) 2022-08-29 08:39:41 -04:00
Arkadiusz Kozdra 0a3a15a66e Support class parameters (#2231) (#3541) 2022-08-28 10:24:55 -04:00
Wilson Snyder 2358ced061 Rename tracing rolloverSize and add test (#3570). 2022-08-28 08:25:02 -04:00
Krzysztof Bieganski 2af5304884 Fix tracing of slow coroutines (#3576 part) (#3579) 2022-08-26 05:11:44 -05:00
Varun Koyyalagunta 5869fdf7f6 Fix $dump systemtask with --output-split-cfuncs (#3495) (#3497) 2022-08-25 18:29:11 -05:00
Krzysztof Bieganski 1a1d2ecfd9 Enable tracing in generated main (#3578)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-08-25 14:55:37 +01:00
Geza Lore 5c356a4680 Merge branch 'master' into develop-v5 2022-08-22 14:32:06 +01:00
Krzysztof Bieganski 39af5d020e Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.

Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).

The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.

There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
  with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
  them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
  resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
  blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
  for suspending a stack of coroutines (normally, C++ coroutines are
  stackless).

There is a new visitor in `V3Timing.cpp` which:
  * scales delays according to the timescale,
  * simplifies intra-assignment timing controls and net delays into
    regular timing controls and assignments,
  * simplifies wait statements into loops with event controls,
  * marks processes and tasks with timing controls in them as
    suspendable,
  * creates delay, trigger scheduler, and fork sync variables,
  * transforms timing controls and fork joins into C++ awaits

There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.

There is also a function that transforms forked processes into separate
functions.

See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.

Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-08-22 13:26:32 +01:00
Geza Lore 9ac64d0b92 Improve performance of MTask coarsening
Various optimizations to speed up MTasks coarsening (which is the long
pole in the multi-threaded scheduling of very large designs).

The biggest impact ones:
- Use efficient hand written Pairing Heaps for implementing priority
  queues and the scoreboard, instead of the old SortByValueMap. This
  helps us avoid having to sort a lot of merge candidates that we will
  never actually consider and helps a lot in performance.
- Remove unnecessary associative containers and store data structures
  (the heap nodes in particular) directly in the object they relate to.
  This eliminates a huge amount of lookups and helps a lot in
  performance.
- Distribute storage for SiblingMC instances into the LogicMTask
  instances, and combine with the sibling maps. This again eliminates
  hash table lookups and makes storage structures smaller.
- Remove some now bidirectional edge maps, keep only the forward map.

There are also some other smaller optimizations:
- Replaced more unnecessary dynamic_casts with static_casts
- Templated some functions/classes to reduce the number of static
  branches in loops.
- Improves sorting of edges for sibling candidate creation
- Various micro-optimizations here and there

This speeds up MTask coarsening by 3.8x on a large design, which
translates to a 2.5x speedup of the ordering pass in multi-threaded
mode. (Combined with the earlier optimizations, ordering is now 3x
faster.)

Due to the elimination of a lot of the auxiliary data structures, and
ensuring a minimal size for the necessary ones, memory consumption of
the MTask coarsening is also reduced (measured up to 4.4x reduction
though the accuracy of this is low).

The algorithm is identical except for minor alterations of the order
some candidates are added or removed, this can cause perturbation in the
output due to tied scores being broken based on IDs.
2022-08-20 21:18:50 +01:00
Wilson Snyder 7cc89b8b42 Merge branch 'master' into develop-v5 2022-08-20 14:19:45 -04:00
Wilson Snyder c6607724cb Fix clang warning. 2022-08-20 14:19:00 -04:00
Wilson Snyder ebb37b0156 Merge branch 'master' into develop-v5 2022-08-20 14:02:09 -04:00
Wilson Snyder 90dc04cf93 Add --future0 and --future1 options. 2022-08-20 14:01:13 -04:00
Krzysztof Bieganski 10cf492946 Add support for expressions in event controls (#3550)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-08-19 20:18:38 +02:00
Geza Lore 4d81eb021d Revert "Improve performance of MTask coarsening"
This reverts commit 83475008d9.
2022-08-19 18:03:45 +01:00
Geza Lore 83475008d9 Improve performance of MTask coarsening
Various optimizations to speed up MTasks coarsening (which is the long
pole in the multi-threaded scheduling of very large designs).

The biggest impact ones:
- Use efficient hand written Pairing Heaps for implementing priority
  queues and the scoreboard, instead of the old SortByValueMap. This
  helps us avoid having to sort a lot of merge candidates that we will
  never actually consider and helps a lot in performance.
- Remove unnecessary associative containers and store data structures
  (the heap nodes in particular) directly in the object they relate to.
  This eliminates a huge amount of lookups and helps a lot in
  performance.
- Distribute storage for SiblingMC instances into the LogicMTask
  instances, and combine with the sibling maps. This again eliminates
  hash table lookups and makes storage structures smaller.
- Remove some now bidirectional edge maps, keep only the forward map.

There are also some other smaller optimizations:
- Replaced more unnecessary dynamic_casts with static_casts
- Templated some functions/classes to reduce the number of static
  branches in loops.
- Improves sorting of edges for sibling candidate creation
- Various micro-optimizations here and there

This speeds up MTask coarsening by 3.8x on a large design, which
translates to a 2.5x speedup of the ordering pass in multi-threaded
mode. (Combined with the earlier optimizations, ordering is now 3x
faster.)

Due to the elimination of a lot of the auxiliary data structures, and
ensuring a minimal size for the necessary ones, memory consumption of
the MTask coarsening is also reduced (measured up to 4.4x reduction
though the accuracy of this is low).

The algorithm is identical except for minor alterations of the order
some candidates are added or removed, this can cause perturbation in the
output due to tied scores being broken based on IDs.
2022-08-19 16:59:20 +01:00
Geza Lore 03ac7ad730 Make PartPropagateCp specific to the MTask graph
While keeping the client code abstract in PartPropagateCp is nice for
testing, there is performance to be had removing the abstraction. As
this code dominates in scheduling large designs, we eliminate the
abstraction and re-work the testing to use the actual LogicMTask and
MTaskEdge graph types. No functional change intended.
2022-08-19 14:06:11 +01:00
Geza Lore cd50949a7e Reuse MTaskEdge instances in MT scheduling
Instead of deleting then re-allocating MTaskEdge instances when merging
two MTasks, just redirect the edged of the donor MTask to the recipient
MTask. This is both faster as it avoids an allocation and a deletion,
together with one update of the sibling maps, and also makes the
algorithm more stable due to MergeCandidate IDs being stable and
allocated up front for all MTaskEdges, before any SiblingMCs are
allocated.

Perturbations in output are expected as the IDs used to break ties
between merge candidates with equal costs are not updated when
redirecting an edge (on purpose). The relinking of only one end of the
graph edges also perturbs the order in which they are enumerated, which
does change candidate opportunities when the number of edges is larger
than PART_SIBLING_EDGE_LIMIT. Confirmed output is identical when
IDs are updated and edges are updated to appear in their original order.
2022-08-19 14:06:11 +01:00
Geza Lore f0040c7b9a Remove reliance on pointer comparison in MT scheduling
The critical path propagation used to rely on a pointer comparison to
break equal scoring critical path updates. Use the corresponding mtask
ids instead, which is deterministic across invocations.
2022-08-19 14:06:11 +01:00
Geza Lore f8a0389e73 Do not use stepCost when gathering sibling merge candidates
siblingPairFromRelatives gathers neighbours of a vertex, and sorts them.
It then takes the N best nodes, and creates sibling merge candidates
from them. We now use the unadjusted cost instead of the step cost of
the vertices when sorting. This is both faster as we need not do the
log-space rounding to compute stepCost, and will also make similar but
yet cheaper nodes appear closer to the front as we don't lose precision
in rounding, hence they are more likely to be entered as merge
candidates. Note that when creating the merge candidate, we still use
the stepCost, so it's purpose of reducing the propagation of critical
path updates is maintained in full. In summary, this should make both
Verilator and the generated model very slightly faster, at least in
theory, and I have observed minor improvement in places.
2022-08-19 14:06:11 +01:00
Geza Lore b436794773 Add specialized GraphStreamUnordered
GraphStreamUnordered used to be GraphStream<std::less<const
V3GraphVertex*>>, but a lot of performance improvements can be had by a
specialized implementation, so added a highly optimized one. This helps
a lot with --debug-partition.
2022-08-19 14:06:11 +01:00
Geza Lore 1404319b28 Merge branch 'master' into develop-v5 2022-08-19 13:39:44 +01:00
Geza Lore 90d22cbec6 Fix AstNode::exists return type 2022-08-19 13:22:06 +01:00
Krzysztof Bieganski 33e2acfe61 Fix AstNode::forall return type (#3559)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-08-19 12:33:17 +01:00
Ryszard Rozak db5fdfb0ee Fix === with some tristate constants (#3551). 2022-08-18 07:03:05 -04:00
Krzysztof Bieganski 951cd73fe0 Handle MemberSel in V3EmitV.cpp (#3555) 2022-08-18 06:33:45 -04:00
Arkadiusz Kozdra 0eeb40b975 Fix converting subclasses to string (#3552) 2022-08-17 18:08:43 -04:00
Wilson Snyder 93272c13fd Tests: Confirm fixed (#181) 2022-08-15 22:17:36 -04:00
Wilson Snyder 43abaeb055 Tests: Confirm fixed (#485) 2022-08-15 22:17:17 -04:00
Wilson Snyder 18b9e661c9 Tests: Confirm fixed (#446) 2022-08-15 22:17:09 -04:00
Wilson Snyder f435d96241 Fix case statement comparing string literal (#3544). 2022-08-15 21:56:09 -04:00
github action d32e3f042f Apply 'make format' 2022-08-12 10:56:12 +00:00
Mostafa Gamal df5f95a5bd Fix nested default assignment for struct pattern (#3511) (#3524) 2022-08-12 06:55:07 -04:00
Drew Ranck b0c475205b Fix void-cast queue pop_front or pop_back (#3542) (#3364)
Fix compile error for queue method usage, if it is the
first statement in a block of code, and the return
value is not used. Example:

>  if (foo)
>    void'(bar.pop_front());
2022-08-12 06:51:25 -04:00
Wilson Snyder 1e2219347e Internals: Cleanup ifdef, move up not under compilver version ifdef 2022-08-11 17:41:43 -04:00
Wilson Snyder cbe1b8e266 Fix segfault exporting non-existant package (#3535). 2022-08-08 17:53:50 -04:00
Mariusz Glebocki 2b12fe5773 Internals: Construct V3Number with correct type instead of changing it manually. (#3529) 2022-08-08 08:17:02 -04:00
Geza Lore a4fd6d38fb Add operator != to VlWide
This is required by VlUnpacked::neq
2022-08-07 13:13:28 +01:00
Yutetsu TAKATSUKASA d20f22beb1 Fix tristate logic when reading inout port in a module #3399 (#3523)
* Tests: Add a test to reproduce #3399

* Fix #3399. When reading an inout port in a module, it should refer the
original inout port, not the generated MODTEMP.
2022-08-07 21:12:57 +09:00
Wilson Snyder f4fe10844b Tests: Fix t_flag_help.pl (#3532). 2022-08-07 04:57:59 -04:00
Mariusz Glebocki 122e89ffde Fix V3Number::isMsbXZ(). (#3530) 2022-08-05 19:12:52 +01:00
Geza Lore c266739e9f Merge branch 'master' into develop-v5 2022-08-05 12:17:57 +01:00
Geza Lore 96a4b3e5a5 Update clang-format config and apply
- Regroup and sort #include directives (like we used to, but automatic)
- Set AlwaysBreakTemplateDeclarations to true
2022-08-05 12:00:24 +01:00
Geza Lore 7403226a97 Merge branch 'master' into develop-v5 2022-08-04 10:03:38 +01:00
Geza Lore fac8e76923 Rework SortByValueMap for better performance
Keep a single std::set of key/value pairs, and a single unordered_map
from key to iterators into the set. Also improve some of the accessing
mechanisms using modern C++. This speeds up multi-threaded ordering by
about 10%.
2022-08-03 21:17:02 +01:00
Geza Lore b864f5f5ba V3Partition: use static_cast with LogicMTaskVertex
dynamic_cast is not free, and the mtask graph contains only
LogicMTaskVertex vertices, use static_cast instead for some speedup.
2022-08-03 17:05:01 +01:00
Geza Lore f9f66d787e Fix integer overflow in V3Unroll (#3451) 2022-08-03 09:41:30 +01:00
Geza Lore bd211c87aa astgen: split 'visit' method declarations from definitions
Add definitions to V3Ast.cpp, and use static_cast.
This fixes a lot of clang-tidy noise.
2022-08-02 17:53:19 +01:00
Geza Lore 6c33e6e889 Tell clang-tidy .h files are C++ (not C) headers 2022-08-02 17:53:19 +01:00
Geza Lore 6fc25dae9e Fix clang-tidy warnings (#3522) 2022-08-02 15:58:48 +01:00
Kamil Rakoczy cfb6fd8b34 Reduce max RSS usage (#3483)
By constant folding nodes earlier in V3Expand, we can save some max RSS on large designs.
2022-08-02 13:36:14 +01:00
Geza Lore 39d1a62f9e Fix change detection on unpacked arrays
Expand array assignment when creating the trigger, as V3Expand might
mangle it otherwise.
2022-08-02 13:01:41 +01:00
Geza Lore ba66fa7200 Merge branch 'master' into develop-v5 2022-08-02 11:16:35 +01:00
Geza Lore cb60663d49 V3Gate: Defer substitutions until required as well
Similarly to the earlier patch that defers constant folding on optimized
logic, now we also defer the variable substitutions as well. This again
eliminates a lot of traversals, and yields another ~10x speedup of V3Gate
on a design where V3Gate used to dominate while producing identical
results.
2022-08-01 12:54:41 +01:00
Geza Lore 0d2bf23d82 V3Gate: Defer constant folding until required
Rather than constant folding each logic block after every substitution,
only constant fold updated blocks when re-analysed, or at the end. This
removes a lot of invocations of V3Const on large blocks that can be
optimized well, and should yield the same result.

This speeds up V3Gate by ~4x on a design where V3Gate dominates.
2022-07-31 20:42:04 +01:00
Geza Lore 682a60e325 Cleanup V3Gate, no functional change 2022-07-31 20:07:54 +01:00
Geza Lore 2ab6272cc7 Use AstNode::foreach in V3Gate
This yields a little speedup.
2022-07-31 20:05:25 +01:00
Geza Lore 152a6cd886 Improve AstNode::foreach (also exists and forall)
Speed improvements:
- Use a direct, recursion-free implementation
- Improve pre-fetching

Functionality:
- Support remove/replace of currently iterated node
2022-07-31 19:07:32 +01:00
Wilson Snyder 3c54d5df70 Merge branch 'master' into develop-v5 2022-07-30 14:42:51 -04:00
Wilson Snyder f91793e931 Revert - SC overrides cause non-override clang error. 2022-07-30 13:53:54 -04:00
Wilson Snyder 12925cd8b0 Internals: clang-tidy cleanups. No functional change intended. 2022-07-30 12:49:30 -04:00
Wilson Snyder daac7cb90d Merge branch 'master' into develop-v5 2022-07-30 12:09:05 -04:00
Wilson Snyder a2d26b45bb Internals: Fix some clang-tidy issues. No functional change intended. 2022-07-30 11:54:28 -04:00
Wilson Snyder dce8f3d25d Internals: Spacing from develop-v5. No functional change. 2022-07-30 11:54:28 -04:00
Geza Lore 38e5b6c1ad Replace __gcov_flush with __gcov_dump
__gcov_flush was a private function and was removed from later GCC
versions (at least from 11.2.0, possibly earlier). Replace with the
documented public __gcov_dump.
2022-07-30 16:02:03 +01:00
Wilson Snyder 4859f5e1fa Merge branch 'master' into develop-v5 2022-07-30 10:26:16 -04:00
Wilson Snyder b9d7819faa Internals: Fix some cppcheck issues. Some dump functions fixed. 2022-07-30 10:01:39 -04:00
Geza Lore ad2fbfe62d Merge branch 'master' into develop-v5 2022-07-29 12:04:24 +01:00
Yutetsu TAKATSUKASA 1f9323d086 Set correct dtype in replaceShiftSame() (#3520)
* Tests: Add a test to reproduce bug3399

* Fix3399. Set the correct dtype in replaceShiftSame().

* Tests: update stats.

* Update Changes
2022-07-29 07:05:04 +09:00
Geza Lore 574dbfded1 V3MergeCond: Fix incorrect merge of assignments to the condition 2022-07-28 15:50:02 +01:00
Wilson Snyder 2a87387eb3 Documentation fixes (#3514) 2022-07-28 08:41:01 -04:00
Geza Lore a5ddd10e31 Tests: compare VCD files both ways
vcddiff is a bit broken, and sometimes 'vcddiff a b' fails while the
files are indeed equivalent. There is a chance however that 'vcddif b a'
will succeed in this case, so compare trace files both ways when
checking test results and claim success if vcddiff succeeds in at least
one direction.
2022-07-27 10:48:02 +01:00
github action e871cd8a44 Apply 'make format' 2022-07-25 21:47:29 +00:00
Mostafa Gamal 7b431b37c7 Fix struct pattern assignment (#2328) (#3517). 2022-07-25 17:46:22 -04:00
Gustav Svensk eeef5ab4de Fix sformat string incorrectly cleared (#3515) (#3519). 2022-07-25 17:36:34 +02:00
Geza Lore ac4ec87942 Respect clang's default -fbracket-depth by default
Set default value of --comp-limit-parens to 240, to respect default
 maximum nesting of parentheses in clang (which is controlled by
 -fbracket-depth and defaults to 256). For code generation consistency,
 also use the same default with gcc.
2022-07-25 12:59:26 +01:00
Geza Lore 290c2e0388 Mark FileLine::v3errorEndFatal as noreturn 2022-07-25 12:51:02 +01:00
Geza Lore 89924bda51 Always type '$clog2' as signed 32 2022-07-25 12:48:13 +01:00
Yutetsu TAKATSUKASA 60eab3eb8c Fix wrong result of bit op tree optimization #3509 (#3516)
* Tests: Add a test to reproduce #3509

* Tests: Compile without tautological-compare check because bit op tree optimization is disabled in the test.

* Internals: Dedup code. No functional change is intended.

* Fix #3509.

"2'b10 == (2'b11 & {1'b0, val[0]})"  and "2'b10 != (2'b11 & {1'b0, val[0]})" were
wrongly optimized to "!val[0]" and "val[0]" respectively.
Now properly optimize them to 1'b0 and 1'b1.

* Commentary

* Commentary: Update Changes
2022-07-24 19:54:37 +09:00
Geza Lore 386401da60 Merge branch 'master' into develop-v5 2022-07-22 15:09:20 +01:00
Geza Lore 31abe537a0 Fix DPI export trigger sensitivity in 'nba'
Fixes #3508
2022-07-21 17:43:03 +01:00
Geza Lore e0b61ceabd Remove legacy #ifdef SYSTEMC_64BIT_PATCHES
These days this is always false, see #3505
2022-07-21 15:01:17 +01:00
Geza Lore f9ecbdc70b Merge branch 'master' into develop-v5 2022-07-21 09:56:14 +01:00
Geza Lore 30e3edb81d Remove deprecated and unused timescale override defines
These have been 'deprecated' for 2 years and are otherwise unused except
for using a temporary placeholder value, which I have inlined with the
default value.

Also remove the now VL_TIME_STR_CONVERT utility function (and
corresponding unit tests), which have no references in any project on
GitHub.
2022-07-20 14:06:09 +01:00
Arkadiusz Kozdra 542e324869 Wildcard index type support for associative arrays (#3501).
Associative arrays that specify a wildcard index type may be indexed by
integral expressions of any size, with leading zeros removed
automatically.  A natural representation for such expressions is a
string, especially that the standard explicitly specifies automatic
casts from string indices to bit vectors of equivalent size.
The automatic cast part is done implicitly by the existing type system.

A simpler way to just make this work would be to convert wildcard index
type to a string type directly in the parser code, but several new AST
classes are needed to make sure illegal method calls are detected.
The verilated data structure implementation is reused, because there is
no need for differentiating the behavior on C++ side.
2022-07-20 15:01:36 +02:00
Geza Lore 1c5e5704f5 Fix iteration fixup in AstNode::addHereThisAsNext
Previous version broke verialor_ext_tests due to iteration order
mismatch after 3fc8249429
2022-07-20 13:08:51 +01:00
Geza Lore 1d400dd98c Configure tracing at run-time, instead of compile time (#3504)
All remaining use of conditional compilation in the tracing
implementation of the run-time library are replaced with the use of
VerilatedModel::traceConfig, and is now done at run-time.
2022-07-20 11:27:10 +01:00
Geza Lore a4ed3c2086 Make parallel tracing switchable at run-time 2022-07-19 17:13:13 +01:00
Geza Lore efb5caad22 Improve robustness of trace configuration
Always fail if adding a model to a trace file that has already executed
a dump. We used to do this before as well, though in a less robust way.
We will be relying on this property more in the future, so improve the
check.
2022-07-19 14:16:08 +01:00
Geza Lore 3a002b6cf2 Remove VerilatedVcd::m_evcd and related dead code.
The legacy code that was using this was removed earlier, and m_evcd was
constant false, so removed.
2022-07-19 13:58:18 +01:00
Geza Lore f8b7981be4 Make use of FST writer thread switchable at run-time.
Always build the FST libray with -DFST_WRITER_PARALLEL, iff VL_THREADED.
This supports run-time enablement of the FST writer thread, and has no
measurable performance impact on single threaded tracing but simplifies
the library build.

Note: the actual choice of using the fst writer thread is still compile
time, but can now be made run-time easily.
2022-07-19 13:48:03 +01:00
Geza Lore b55ee79d86 Fix typo 2022-07-19 12:36:21 +01:00
Geza Lore af70db88db Remove unused method 2022-07-19 11:32:16 +01:00
Geza Lore 7ef033f876 Ensure generated Makefile for hierarchical build is stable.
Avoid iterating unordered_map. Iterate sorted blocks instead.
2022-07-19 11:32:01 +01:00
Geza Lore db59c07f27 Implement trace offloading with fewer ifdefs
Step towards a proper run-time library. Reduce the amount of ifdefs in
the implementation of offloaded tracing. There are still a very small
number of ifdefs left, which will need more careful changes in order to
keep user API compatibility.
2022-07-19 11:31:35 +01:00
Geza Lore 9085e34d70 Pass VerilatedModel at trace registration time 2022-07-19 11:00:09 +01:00
Arkadiusz Kozdra 0dfa7d3af5 Internals: const-qualify findDType function. No functional change. (#3502) 2022-07-18 18:58:55 +02:00
Geza Lore c28bf9ce24 Fix change detection over unpacked arrays. 2022-07-18 12:25:22 +01:00
Geza Lore 5a1f1796d7 Fix t/t_public_{clk,src}.pl after merge of master 2022-07-15 16:48:22 +01:00
Geza Lore ffc95fcf0e Merge branch 'master' into develop-v5 2022-07-15 16:48:14 +01:00
Todd Strader b0e796ca83 Public combo propagation issues (#2905) 2022-07-15 11:44:32 -04:00
Geza Lore 3773e2ef95 Simplify primary input checks 2022-07-15 16:18:41 +01:00
Geza Lore 00c1f67c57 Make trigger dumping functions always Slow code 2022-07-14 16:28:09 +01:00
Geza Lore 3f19ba1554 Improve handling of extra trigges in V3Sched.
Add utility class for allocation, and add human readable text to debug
code.
2022-07-14 16:06:15 +01:00
Geza Lore f37cc2353d Fix standard library incldues 2022-07-14 15:49:00 +01:00
Geza Lore 6a7bda6910 Correctly schedule combinational logic driven from DPI exports.
Fixes #3429.
2022-07-14 15:35:49 +01:00
Geza Lore ff1b9930fc Handle multiple external domains in V3Order
Make the external domains provider of ordering populate an output
vector, which then allows us to add multiple external sensitivities to
combinational logic.
2022-07-14 11:09:40 +01:00
Geza Lore 582da6df9a Merge branch 'master' into develop-v5 2022-07-14 10:08:52 +01:00
Geza Lore 3bd830eacf Minor clean up of initialization 2022-07-13 18:24:48 +01:00
Geza Lore f4efcbde5c Remove simple use of static data from V3OutFormatter::indentSpaces 2022-07-13 16:15:21 +01:00
Geza Lore 658819bb71 Trivial static const -> constexpr 2022-07-13 16:01:03 +01:00
Geza Lore 3fc8249429 Use AstNode::addHereThisAsNext in a few places 2022-07-13 13:57:00 +01:00
Geza Lore e0a38ce2c2 Remove unnecessary AstNode::clearIter() 2022-07-13 13:57:00 +01:00
Geza Lore 178e1789b5 Make AstNode::addHereThisAsNext always O(1)
Using unlinkFrBackWithNext is O(n) in the size of the list if unlinking
from the middle, so addHereThisAsNext also had this complexity. This
patch implements addHereThisAsNext directly, which is always O(1).
2022-07-13 12:13:40 +01:00
William D. Jones 108c900387 Fix unique_ptr memory header for MinGW64 (#3493). 2022-07-13 06:38:03 -04:00
Wilson Snyder 63507e8e29 Internals: Favor UASSERT_OBJ when have object. 2022-07-12 18:02:57 -04:00
Geza Lore 87f1e06c41 Small algorithmic improvement of PartContraction::siblingPairFromRelatives
Use std::partial_sort for the non-exhaustive case. This is O(n) instead
of O(n*log(n)) in the size of the candidate list being sorted. (It
actually is O(n*log(k)), but k is constant 6 in the non-exhaustive
case).
2022-07-12 19:10:01 +01:00
Geza Lore 7e8bafd217 Remove static data use from PartContraction::siblingPairFromRelatives
Use std::sort with lambda rather than qsort with static function and
static data. Verilation performance neutral.
2022-07-12 19:09:40 +01:00
Geza Lore 457ad07ade Remove unnecessary static state from V3EmitCFunc 2022-07-12 17:51:17 +01:00
Geza Lore c9ac9a75a6 Merge branch 'master' into develop-v5 2022-07-12 17:29:45 +01:00
Geza Lore 79c901c220 Tighten signatures/implementaion of VerilatedModel abstract methods. 2022-07-12 16:06:08 +01:00
Geza Lore b61d819fcb Move contextp() under VerilatedModel 2022-07-12 16:06:08 +01:00
Geza Lore f4038e3674 Move thread pool and execution profiler into the context. (#3477)
Fixes #3454
2022-07-12 11:41:15 +01:00
Arkadiusz Kozdra 8377514127 Add support for $test$plusargs(expr) (#3489) 2022-07-11 06:21:35 -04:00
Wilson Snyder d8ea989eda Tests/examples: Remove some legacy Verilator:: calls. 2022-07-09 09:50:50 -04:00
Wilson Snyder 5f3316d3dc * Fix empty string arguments to display (#3484). 2022-07-09 08:30:57 -04:00
Wilson Snyder a4fddb3fbe Fix table misoptimizing away display (#3488). 2022-07-09 07:55:46 -04:00
Wilson Snyder 3d71716a8a Internals: Constructor style cleanup. No functional change. 2022-07-09 07:40:07 -04:00
Yutetsu TAKATSUKASA 9f37cef1bb Fix #3470 of incorrect bit op tree optimization (#3476)
* Tests: Add a test to reproduce #3470

* Update LSB during return path of traversal. No functional change is intended.

* Introduce LeafInfo::m_msb

* Update LeafInfo::m_msb when visitin AstCCast

* Internals: Add comment, reorder. No functional change is intended.

* Delete explicit from copy constructor to fix build error.

* Update Changes

* Internals: Remove unused parameter. No functional change is intended.

* Tests: Add explanation to t_const_opt.
2022-07-06 08:33:37 +09:00
Geza Lore 0de1bbc85b Add and use VL_CONSTEXPR_CXX17 2022-07-05 14:21:28 +01:00
Geza Lore 3aa8624658 Set 'threads' in tests via parameter to compile
This is in preparation to #3454.
2022-07-05 12:33:41 +01:00
Geza Lore 42b711b862 Don't use 'assert' in profiler initialization 2022-07-05 12:18:54 +01:00
Wilson Snyder b25b798dbe Merge branch 'master' into develop-v5 2022-07-04 13:20:03 -04:00
Mariusz Glebocki 2873dbe154 Optimize file writing by using a memory buffer. (#3461) 2022-07-04 10:23:31 -04:00
Felix Yan 31a83cb0d8 Fix use of C++17 on Arch Linux (#3479) 2022-06-29 11:24:30 -04:00
Geza Lore 383e384739 Remove always true cfg_with_threaded from test driver 2022-06-27 15:23:32 +01:00
Geza Lore 1bb6433649 Improve worker thread shutdown.
Always ensure worker thread task queue is drained before shutting down.
2022-06-27 15:03:36 +01:00
Yutetsu TAKATSUKASA ced39d0982 Internals: preparation for fixing #3470 (#3475)
* Internals: Let LeafInfo class. No functional change is intended.

* Internals: Rename LeafInfo::width -> LeafInfo::varWidth(). No functional change is intende.
2022-06-27 22:41:33 +09:00
Wilson Snyder fc4d6a62af Remove VL_PROFILER ifdef. Partial (#3454). 2022-06-22 20:06:23 -04:00
Wilson Snyder fa99cbbc73 Commentary: Fix mis-sorted option names. No functional change. 2022-06-21 19:28:26 -04:00
Wilson Snyder 49455721a3 Commentary 2022-06-21 19:28:23 -04:00
Unai Martinez-Corral 11032b1936 Fix bisonpre for MSYS2 (#3471) 2022-06-20 11:59:27 -04:00
Wilson Snyder e7ca4a69e3 Merge branch 'master' into develop-v5 2022-06-19 15:22:09 -04:00
Wilson Snyder f2fba51fe2 devel release 2022-06-19 15:13:29 -04:00
github action aca9fd3bed Apply 'make format' 2022-06-04 16:30:41 +00:00
Wilson Snyder 09f3f40462 Fix clang-discovered missing comma. 2022-06-04 12:27:44 -04:00
Wilson Snyder e2525d8440 Tests: Fix racy tests for develop-v5. 2022-06-04 12:22:11 -04:00
Wilson Snyder 6769106881 Merge branch 'master' into develop-v5 2022-06-04 12:17:29 -04:00
Wilson Snyder 0f324c8309 Merge branch 'master' into develop-v5 2022-06-04 11:59:49 -04:00
Geza Lore 3af5e7e8da Remove scope pointer from OrderEitherVertex.
For ordering, only the scope of logic vertices should be relevant, so
remove the scope pointer from OrderEitherVertex and move it into
OrderLogicVertex. This does not change single-threaded scheduling at
all. Theoretically, multi-threaded scheduling should not be affected
either though due to some implementation quirk depending on vertex order
in a graph the MT schedule is perturbed by this change, but the
performance effect of this is negligible on all benchmarks I have access
to.

No functional change intended.

Fixes #3442
2022-05-25 20:32:32 +01:00
Geza Lore 160f3ee4a7 Remove dead code, no functional change 2022-05-25 19:11:20 +01:00
Krzysztof Bieganski 3a310f19f0 Adjust loop conditions in VlTriggerVec functions
This change is not a functional one; it is only meant to appease the
compiler with respect to warnings such as GCC's `-Wtype-limits`.

Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-05-25 18:45:59 +01:00
Krzysztof Bieganski d7a75dc026 Merge branch 'master' into develop-v5 2022-05-25 11:06:38 +02:00
Wilson Snyder f0a2c98376 Commentary 2022-05-20 08:34:32 -04:00
Geza Lore 282887d9c6 Fix code coverage holes
Fixes #3422
2022-05-16 21:22:21 +01:00
Geza Lore 0e62cd11da Don't issue DEPRECATED for now no-op clock_enable attribute
Fixes #3421
2022-05-16 18:57:51 +01:00
Geza Lore 599d23697d IEEE compliant scheduler (#3384)
This is a major re-design of the way code is scheduled in Verilator,
with the goal of properly supporting the Active and NBA regions of the
SystemVerilog scheduling model, as defined in IEEE 1800-2017 chapter 4.

With this change, all internally generated clocks should simulate
correctly, and there should be no more need for the `clock_enable` and
`clocker` attributes for correctness in the absence of Verilator
generated library models (`--lib-create`).

Details of the new scheduling model and algorithm are provided in
docs/internals.rst.

Implements #3278
2022-05-15 16:03:32 +01:00
Geza Lore 39b7c47e7b Merge branch 'master' into develop-v5 2022-05-15 13:39:14 +01:00
Geza Lore f6f03bc261 Merge branch 'master' into develop-v5 2022-05-15 12:05:12 +01:00
Geza Lore 766fb56651 Merge branch 'master' into develop-v5 2022-05-14 10:30:45 +01:00
Geza Lore bec4610e12 Merge branch 'master' into develop-v5 2022-05-08 15:50:50 +01:00
Wilson Snyder 184ebe72a2 Merge branch 'master' into develop-v5 2022-05-02 22:27:58 -04:00
Geza Lore 2ad0bcbba9 Merge branch 'master' into develop-v5 2022-05-01 16:49:42 +01:00
Geza Lore 88bb7cdca6 Merge branch 'master' into develop-v5 2022-04-29 17:14:11 +01:00
Geza Lore 46de9460a6 Merge branch 'master' into develop-v5 2022-04-23 15:38:30 +01:00
Wilson Snyder 92f64de115 Add FIXMEV5 comments 2022-04-13 22:18:59 -04:00
Wilson Snyder f4baa86b71 Devel version 5 2022-04-13 07:15:11 -04:00
1058 changed files with 59993 additions and 26427 deletions
+21 -11
View File
@@ -17,7 +17,7 @@ AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
@@ -56,18 +56,29 @@ DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
# Include grouping/sorting
SortIncludes: true
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
- Regex: '"(config_build|verilated_config|verilatedos)\.h"'
Priority: -1 # Sepecials before main header
- Regex: '(<|")verilated.*'
Priority: 1 # Runtime headers
- Regex: '"V3.*__gen.*\.h"'
Priority: 3 # Generated internal headers separately
- Regex: '"V3.*"'
Priority: 2 # Internal header
- Regex: '".*"'
Priority: 4 # Other non-system headers
- Regex: '<[[:alnum:]_.]+>'
Priority: 5 # Simple system headers next
- Regex: '<.*>'
Priority: 6 # Other system headers next
IncludeIsMainRegex: '$'
IndentCaseLabels: false
IndentPPDirectives: None
IndentWidth: 4
@@ -91,7 +102,6 @@ PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
+5 -5
View File
@@ -61,12 +61,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
path: repo
- name: Cache $CCACHE_DIR
uses: actions/cache@v2
uses: actions/cache@v3
env:
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
with:
@@ -86,7 +86,7 @@ jobs:
run: tar --posix -c -z -f ${{ env.VERILATOR_ARCHIVE }} repo
- name: Upload tar archive
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
path: ${{ github.workspace }}/${{ env.VERILATOR_ARCHIVE }}
name: ${{ env.VERILATOR_ARCHIVE }}
@@ -132,7 +132,7 @@ jobs:
steps:
- name: Download tar archive
uses: actions/download-artifact@v2
uses: actions/download-artifact@v3
with:
name: ${{ env.VERILATOR_ARCHIVE }}
path: ${{ github.workspace }}
@@ -142,7 +142,7 @@ jobs:
run: tar -x -z -f ${{ env.VERILATOR_ARCHIVE }}
- name: Cache $CCACHE_DIR
uses: actions/cache@v2
uses: actions/cache@v3
env:
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
with:
+2 -2
View File
@@ -11,7 +11,7 @@ on:
jobs:
Test:
name: "'docs/CONTRIBUTORS' was signed"
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- run: test_regress/t/t_dist_contributors.pl
+7 -7
View File
@@ -24,14 +24,14 @@ jobs:
Build:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
env:
CI_BUILD_STAGE_NAME: build
CI_RUNS_ON: ubuntu-20.04
CI_RUNS_ON: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
path: repo
@@ -46,7 +46,7 @@ jobs:
run: tar --posix -c -z -f ${{ env.VERILATOR_ARCHIVE }} repo
- name: Upload tar archive
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
path: ${{ github.workspace }}/${{ env.VERILATOR_ARCHIVE }}
name: ${{ env.VERILATOR_ARCHIVE }}
@@ -73,15 +73,15 @@ jobs:
- 9
include:
- { test: dist, num: '' }
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
name: test-${{ matrix.test }}${{ matrix.num }}
env:
CI_BUILD_STAGE_NAME: test
CI_RUNS_ON: ubuntu-20.04
CI_RUNS_ON: ubuntu-22.04
steps:
- name: Download tar archive
uses: actions/download-artifact@v2
uses: actions/download-artifact@v3
with:
name: ${{ env.VERILATOR_ARCHIVE }}
path: ${{ github.workspace }}
+6 -6
View File
@@ -10,16 +10,16 @@ on:
jobs:
format:
runs-on: ubuntu-20.04
name: Ubuntu 20.04 | format
runs-on: ubuntu-22.04
name: Ubuntu 22.04 | format
env:
CI_OS_NAME: linux
CI_RUNS_ON: ubuntu-20.04
CI_RUNS_ON: ubuntu-22.04
CI_COMMIT: ${{ github.sha }}
CI_M32: 0
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Install packages for build
@@ -27,14 +27,14 @@ jobs:
CI_BUILD_STAGE_NAME: build
run: |
bash ci/ci-install.bash &&
sudo apt-get install clang-format-11 yapf3 &&
sudo apt-get install clang-format-14 yapf3 &&
git config --global user.email "[email protected]" &&
git config --global user.name "github action"
- name: Format code
run: |
autoconf &&
./configure &&
make -j 2 format CLANGFORMAT=clang-format-11 &&
make -j 2 format CLANGFORMAT=clang-format-14 &&
git status
- name: Push
run: |
+1
View File
@@ -1,5 +1,6 @@
\#*
.#*
.gdb_history
.nfs*
*~
*.tidy
+110 -5
View File
@@ -8,6 +8,111 @@ The changes in each Verilator version are described below. The
contributors that suggested a given feature are shown in []. Thanks!
Verilator 5.002 2022-10-29
==========================
**Major:**
* This is a major new release.
* Require C++20 for the new --timing features. Upgrading to a C++20 or
newer compiler is strongly recommended.
* Support the Active and NBA scheduling regions as defined by the
SystemVerilog standard (IEEE 1800-2017 chapter 4). This means all generated
clocks are now simulated correctly (#3278, #3384). [Geza Lore, Shunyao CAD]
* Support timing controls (delays, event controls in any location, wait
statements) and forks. [Krzysztof Bieganski, Antmicro Ltd]
This may require adding --timing or --no-timing. See docs for details.
* Introduce a new combinational logic optimizer (DFG), that can yield
significant performance improvements on some designs. [Geza Lore, Shunyao CAD]
* Add --binary option as alias of --main --exe --build --timing (#3625).
For designs where C++ was only used to make a simple no-I/O testbench, we
recommend abandoning that C++, and instead letting Verilator build it
with --binary (or --main).
**Minor:**
* Split UNUSED warning into genvar, param, and signal warnings (#3607). [Topa Topino]
* Support standalone 'this' in classes (#2594) (#3248) (#3675). [Arkadiusz Kozdra, Antmicro Ltd]
* Support tristate select/extend (#3604). [Ryszard Rozak, Antmicro Ltd>
* Support linting for top module interfaces (#3635). [Kanad Kanhere]
* Support virtual interfaces (#3654). [Arkadiusz Kozdra, Antmicro Ltd]
* Support class type params without defaults (#3693). [Krzysztof Bieganski, Antmicro Ltd]
* Support empty generate_regions (#3695). [mpb27]
* Support access to constructs inside type parameters (#3702). [Arkadiusz Kozdra, Antmicro Ltd]
* Add --dump-tree-dot to enable dumping Ast Tree .dot files (#3636). [Marcel Chang]
* Add --get-supported to determine what features are in Verilator.
* Add error on real edge event control.
* Fix false LATCH warning on 'unique if' (#3088). [Rachit Nigam]
* Fix cell assigning integer array parameters (#3299). [Michael Platzer]
* Fix LSB error on --hierarchical submodules (#3539). [danbone]
* Fix $display of fixed-width numbers (#3565). [Iztok Jeras]
* Fix foreach and pre/post increment in functions (#3613). [Nandu Raj]
* Fix linker errors in user-facing timing functions (#3657). [Krzysztof Bieganski, Antmicro Ltd]
* Fix null access on optimized-out fork statements (#3658). [Krzysztof Bieganski, Antmicro Ltd]
* Fix VPI inline module naming mismatch (#3690) (#3694). [Jiuyang Liu]
* Fix deadlock in timeprecision when using systemC (#3707). [Kamil Rakoczy, Antmicro Ltd]
* Fix width mismatch on inside operator (#3714). [Alex Torregrosa]
Verilator 4.228 2022-10-01
==========================
**Announcement:**
* The next release is anticipated to premiere Verilator Version 5. Please
consider beta-testing the github 'develop-v5' branch, which will soon
merge into the github 'master' branch (#3383).
**Minor:**
* Support some IEEE signal strengths (#3601) (#3629). [Ryszard Rozak, Antmicro Ltd]
* Add --main to generate main() C++ (previously was experimental only).
* Add --build-jobs, and rework arguments for -j (#3623). [Kamil Rakoczy]
* Rename --bin to --build-dep-bin.
* Rename debug flags --dumpi-tree, --dumpi-graph, etc. [Geza Lore]
* Fix thread safety in SystemC VL_ASSIGN_SBW/WSB (#3494) (#3513). [Mladen Slijepcevic]
* Fix crash in gate optimization of circular logic (#3543). [Bill Flynn]
* Fix arguments in non-static method call (#3547) (#3582). [Gustav Svensk]
* Fix default --mod-prefix when --prefix is repeated (#3603). [Geza Lore]
* Fix calling trace() after open() segfault (#3610) (#3627). [Yu-Sheng Lin]
* Fix typedef'ed class conversion to Boolean (#3616). [Aleksander Kiryk]
* Fix Verilation speed when disabled warnings (#3632). [Kamil Rakoczy, Antmicro Ltd]
Verilator 4.226 2022-08-31
==========================
**Minor:**
* Add --future0 and --future1 options.
* Support class parameters (#2231) (#3541). [Arkadiusz Kozdra, Antmicro Ltd]
* Support wildcard index associative arrays (#3501). [Arkadiusz Kozdra, Antmicro Ltd]
* Support negated properties (#3572). [Aleksander Kiryk]
* Support $test$plusargs(expr) (#3489).
* Rename trace rolloverSize() (#3570).
* Improve Verilation speed with --threads on large designs. [Geza Lore]
* Improve Verilation memory by reducing V3Number (#3521). [Mariusz Glebocki, Antmicro Ltd]
* Fix struct pattern assignment (#2328) (#3517). [Mostafa Gamal]
* Fix public combo propagation issues (#2905). [Todd Strader]
* Fix incorrect tristate logic (#3399) [shareefj, Vighnesh Iyer]
* Fix incorrect bit op tree optimization (#3470). [algrobman]
* Fix bisonpre for MSYS2 (#3471).
* Fix max memory usage (#3483). [Kamil Rakoczy, Antmicro Ltd]
* Fix empty string arguments to display (#3484). [Grulfen]
* Fix table optimizing away display (#3488). [Stefan Post]
* Fix unique_ptr memory header for MinGW64 (#3493).
* Fix $dump system task with --output-split-cfuncs (#3495) (#3497). [Varun Koyyalagunta]
* Fix wrong bit op tree optimization (#3509). [Nathan Graybeal]
* Fix nested default assignment for struct pattern (#3511) (#3524). [Mostafa Gamal]
* Fix sformat string incorrectly cleared (#3515) (#3519). [Gustav Svensk]
* Fix segfault exporting non-existent package (#3535).
* Fix void-cast queue pop_front or pop_back (#3542) (#3364). [Drew Ranck]
* Fix case statement comparing string literal (#3544). [Gustav Svensk]
* Fix === with some tristate constants (#3551). [Ryszard Rozak, Antmicro Ltd]
* Fix converting classes to string (#3552). [Arkadiusz Kozdra, Antmicro Ltd]
* Fix --hierarchical with order-based pin connections (#3583) (#3585). [Kelin9298]
Verilator 4.224 2022-06-19
==========================
@@ -26,7 +131,7 @@ Verilator 4.224 2022-06-19
* Improve conditional merging optimization (#3125). [Geza Lore, Shunyao CAD]
* Define VM_TRACE_VCD when tracing in VCD format. [Geza Lore, Shunyao CAD]
* Add assert when VerilatedContext is mis-deleted (#3121). [Rupert Swarbrick]
* Internal prep work towards timing control. [Krzysztof Bieganski]
* Internal prep work towards timing control. [Krzysztof Bieganski, Antmicro Ltd]
* Fix hang with large case statement optimization (#3405). [Mike Urbach]
* Fix UNOPTFLAT warning from initial static var (#3406). [Kamil Rakoczy]
* Fix compile error when enable VL_LEAK_CHECKS (#3411). [HungMingWu]
@@ -193,7 +298,7 @@ Verilator 4.212 2021-09-01
* Fix re-evaluation of logic dependent on state set in DPI exports (#3091). [Geza Lore]
* Support unpacked array localparams in tasks/functions (#3078). [Geza Lore]
* Support timeunit/timeprecision in $unit.
* Support assignment patterns as children of pins (#3041). [Krzysztof Bieganski]
* Support assignment patterns as children of pins (#3041). [Krzysztof Bieganski, Antmicro Ltd]
* Add --instr-count-dpi to tune assumed DPI import cost for multithreaded
model scheduling. Default value changed to 200 (#3068). [Yinan Xu]
* Output files are split based on the set of headers required
@@ -258,7 +363,7 @@ Verilator 4.204 2021-06-12
* Prep work towards better ccache hashing/performance. [Geza Lore]
* Fix assertion failure in bitOpTree optimization (#2891) (#2899). [Raynard Qiao]
* Fix DPI functions not seen as vpiModule (#2893). [Todd Strader]
* Fix bounds check in VL_SEL_IWII (#2910). [Krzysztof Bieganski]
* Fix bounds check in VL_SEL_IWII (#2910). [Krzysztof Bieganski, Antmicro Ltd]
* Fix slowdown in elaboration (#2911). [Nathan Graybeal]
* Fix initialization of assoc in assoc array (#2914). [myftptoyman]
* Fix make support for gmake 3.x (#2920) (#2921). [Philipp Wagner]
@@ -373,7 +478,7 @@ Verilator 4.108 2021-01-10
**Major:**
* Many VPI changes for IEEE compatibility, which may alter behavior from previous releases.
* Support randomize() class method and rand (#2607). [Krzysztof Bieganski]
* Support randomize() class method and rand (#2607). [Krzysztof Bieganski, Antmicro Ltd]
**Minor:**
@@ -433,7 +538,7 @@ Verilator 4.104 2020-11-14
* Support queue and associative array 'with' statements (#2616).
* Support queue slicing (#2326).
* Support associative array pattern assignments and defaults.
* Support static methods and typedefs in classes (#2615). [Krzysztof Bieganski]
* Support static methods and typedefs in classes (#2615). [Krzysztof Bieganski, Antmicro Ltd]
* Add error on typedef referencing self (#2539). [Cody Piersall]
* With --debug, turn off address space layout randomization.
* Fix iteration over mutating list bug in VPI (#2588). [Kaleb Barrett]
+14 -9
View File
@@ -107,6 +107,7 @@ SUBDIRS = docs src test_regress \
examples/cmake_tracing_c \
examples/cmake_tracing_sc \
examples/cmake_protect_lib \
examples/make_hello_binary \
examples/make_hello_c \
examples/make_hello_sc \
examples/make_tracing_c \
@@ -243,6 +244,7 @@ installdata:
; for p in $(VL_INST_INC_SRCDIR_FILES) ; do \
$(INSTALL_DATA) $$p $(DESTDIR)$(pkgdatadir)/$$p; \
done
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/make_hello_binary
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/make_hello_c
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/make_hello_sc
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/make_tracing_c
@@ -278,6 +280,7 @@ uninstall:
-rmdir $(DESTDIR)$(pkgdatadir)/include/gtkwave
-rmdir $(DESTDIR)$(pkgdatadir)/include/vltstd
-rmdir $(DESTDIR)$(pkgdatadir)/include
-rmdir $(DESTDIR)$(pkgdatadir)/examples/make_hello_binary
-rmdir $(DESTDIR)$(pkgdatadir)/examples/make_hello_c
-rmdir $(DESTDIR)$(pkgdatadir)/examples/make_hello_sc
-rmdir $(DESTDIR)$(pkgdatadir)/examples/make_tracing_c
@@ -322,16 +325,18 @@ cppcheck: $(CPPCHECK_DEP)
$(CPPCHECK) $(CPPCHECK_FLAGS) -DVL_DEBUG=1 -DVL_CPPCHECK=1 -DVL_THREADED=1 $(CPPCHECK_INC) $<
CLANGTIDY = clang-tidy
CLANGTIDY_FLAGS = -config='' -checks='-fuchsia-*,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-init-variables'
CLANGTIDY_DEP = $(subst .h,.h.tidy,$(CPPCHECK_H)) \
$(subst .cpp,.cpp.tidy,$(CPPCHECK_CPP))
CLANGTIDY_FLAGS = -config='' \
-header-filter='.*' \
-checks='-fuchsia-*,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-init-variables,-cppcoreguidelines-avoid-goto,-modernize-avoid-c-arrays,-readability-magic-numbers,-readability-simplify-boolean-expr,-cppcoreguidelines-macro-usage' \
CLANGTIDY_DEP = $(subst .cpp,.cpp.tidy,$(CPPCHECK_CPP))
CLANGTIDY_DEFS = -DVL_DEBUG=1 -DVL_THREADED=1 -DVL_CPPCHECK=1
clang-tidy: $(CLANGTIDY_DEP)
%.cpp.tidy: %.cpp
$(CLANGTIDY) $(CLANGTIDY_FLAGS) $< -- $(CLANGTIDY_DEFS) $(CPPCHECK_INC) | 2>&1 tee $@
%.h.tidy: %.h
$(CLANGTIDY) $(CLANGTIDY_FLAGS) $< -- $(CLANGTIDY_DEFS) $(CPPCHECK_INC) | 2>&1 tee $@
$(CLANGTIDY) $(CLANGTIDY_FLAGS) $< -- $(CLANGTIDY_DEFS) $(CPPCHECK_INC) -x c++-header | 2>&1 tee $@
analyzer-src:
-rm -rf src/obj_dbg
@@ -343,13 +348,13 @@ analyzer-include:
format: clang-format yapf format-pl-exec
CLANGFORMAT = clang-format-11
CLANGFORMAT = clang-format-14
CLANGFORMAT_FLAGS = -i
CLANGFORMAT_FILES = $(CPPCHECK_CPP) $(CPPCHECK_H) $(CPPCHECK_YL) test_regress/t/*.c* test_regress/t/*.h
clang-format:
@$(CLANGFORMAT) --version | egrep 11.0 > /dev/null \
|| echo "*** You are not using clang-format 11.0, indents may differ from master's ***"
@$(CLANGFORMAT) --version | egrep 14.0 > /dev/null \
|| echo "*** You are not using clang-format-14, indents may differ from master's ***"
$(CLANGFORMAT) $(CLANGFORMAT_FLAGS) $(CLANGFORMAT_FILES)
PY_PROGRAMS = \
@@ -388,10 +393,10 @@ yapf:
FLAKE8 = flake8
FLAKE8_FLAGS = \
--extend-exclude=fastcov.py \
--ignore=E123,E129,E251,E501,W503,W504,E701
--ignore=E123,E129,E251,E402,E501,W503,W504,E701
PYLINT = pylint
PYLINT_FLAGS = --disable=R0801
PYLINT_FLAGS = --score=n --disable=R0801
lint-py:
-$(FLAKE8) $(FLAKE8_FLAGS) $(PY_PROGRAMS)
+17 -16
View File
@@ -28,7 +28,7 @@ Welcome to Verilator
- |Logo|
* - |verilator multithreaded performance|
- **Fast**
* Outperforms many commercial simulators
* Outperforms many closed-source commercial simulators
* Single- and multi-threaded output models
* - **Widely Used**
* Wide industry and academic deployment
@@ -55,20 +55,19 @@ performing lint checks, and optionally inserting assertion checks and
coverage-analysis points. It outputs single- or multi-threaded .cpp and .h
files, the "Verilated" code.
The user writes a little C++/SystemC wrapper file, which instantiates the
"Verilated" model of the user's top level module. These C++/SystemC files
are then compiled by a C++ compiler (gcc/clang/MSVC++). Executing the
resulting executable performs the design simulation. Verilator also
supports linking Verilated generated libraries, optionally encrypted, into
other simulators.
These Verilated C++/SystemC files are then compiled by a C++ compiler
(gcc/clang/MSVC++), optionally along with a user's own C++/SystemC wrapper
file to instantiate the Verilated model. Executing the resulting executable
performs the design simulation. Verilator also supports linking Verilated
generated libraries, optionally encrypted, into other simulators.
Verilator may not be the best choice if you are expecting a full featured
replacement for Incisive, ModelSim/Questa, VCS or another commercial
Verilog simulator, or if you are looking for a behavioral Verilog simulator
e.g. for a quick class project (we recommend `Icarus Verilog`_ for this.)
However, if you are looking for a path to migrate SystemVerilog to C++ or
SystemC, or your team is comfortable writing just a touch of C++ code,
Verilator is the tool for you.
replacement for a closed-source Verilog simulator, need SDF annotation,
mixed-signal simulation, or are doing a quick class project (we recommend
`Icarus Verilog`_ for classwork.) However, if you are looking for a path
to migrate SystemVerilog to C++/SystemC, or want high speed simulation of
synthesizable designs containing limited verification constructs, Verilator
is the tool for you.
Performance
@@ -85,9 +84,11 @@ multithreading (yielding 200-1000x total over interpreted simulators).
Verilator has typically similar or better performance versus the
closed-source Verilog simulators (Carbon Design Systems Carbonator,
Modelsim, Cadence Incisive/NC-Verilog, Synopsys VCS, VTOC, and Pragmatic
CVer/CVC). But, Verilator is open-sourced, so you can spend on computes
rather than licenses. Thus Verilator gives you the best cycles/dollar.
Modelsim/Questa, Cadence Incisive/NC-Verilog, Synopsys VCS, VTOC, and
Pragmatic CVer/CVC). But, Verilator is open-sourced, so you can spend on
computes rather than licenses. Thus Verilator gives you the best
cycles/dollar.
Installation & Documentation
============================
+36 -20
View File
@@ -244,6 +244,7 @@ Verilator - Translate and simulate SystemVerilog code using C++/SystemC
verilator --help
verilator --version
verilator --binary -j 0 [options] [source_files.v]... [opt_c_files.cpp/c/cc/a/o/so]
verilator --cc [options] [source_files.v]... [opt_c_files.cpp/c/cc/a/o/so]
verilator --sc [options] [source_files.v]... [opt_c_files.cpp/c/cc/a/o/so]
verilator --lint-only -Wall [source_files.v]...
@@ -282,35 +283,43 @@ detailed descriptions of these arguments.
--autoflush Flush streams after all $displays
--bbox-sys Blackbox unknown $system calls
--bbox-unsup Blackbox unsupported language features
--bin <filename> Override Verilator binary
--binary Build model binary
--build Build model executable/library after Verilation
-CFLAGS <flags> C++ compiler arguments for makefile
--build-dep-bin <filename> Override build dependency Verilator binary
--build-jobs <jobs> Parallelism for --build
--cc Create C++ output
--cdc Clock domain crossing analysis
-CFLAGS <flags> C++ compiler arguments for makefile
--clk <signal-name> Mark specified signal as clock
--make <build-tool> Generate scripts for specified build tool
--no-clk <signal-name> Prevent marking specified signal as clock
--compiler <compiler-name> Tune for specified C++ compiler
--converge-limit <loops> Tune convergence settle time
--coverage Enable all coverage
--coverage-line Enable line coverage
--coverage-max-width <width> Maximum array depth for coverage
--coverage-toggle Enable toggle coverage
--coverage-user Enable SVL user coverage
--coverage-underscore Enable coverage of _signals
--coverage-user Enable SVL user coverage
-D<var>[=<value>] Set preprocessor define
--debug Enable debugging
--debug-check Enable debugging assertions
--no-debug-leak Disable leaking memory in --debug mode
--debugi <level> Enable debugging at a specified level
--debugi-<srcfile> <level> Enable debugging a source file at a level
--no-decoration Disable comments and symbol decorations
--default-language <lang> Default language to parse
+define+<var>=<value> Set preprocessor define
--dpi-hdr-only Only produce the DPI header file
--dump-defines Show preprocessor defines with -E
--dump-tree Enable dumping .tree files
--dump-treei <level> Enable dumping .tree files at a level
--dump-treei-<srcfile> <level> Enable dumping .tree file at a source file at a level
--dump-dfg Enable dumping DfgGraphs to .dot files
--dump-graph Enable dumping V3Graphs to .dot files
--dump-tree Enable dumping Ast .tree files
--dump-tree-addrids Use short identifiers instead of addresses
--dump-<srcfile> Enable dumping everything in source file
--dumpi-dfg <level> Enable dumping DfgGraphs to .dot files at level
--dumpi-graph <level> Enable dumping V3Graphs to .dot files at level
--dumpi-tree <level> Enable dumping Ast .tree files at level
--dumpi-<srcfile> <level> Enable dumping everything in source file at level
-E Preprocess, but do not compile
--error-limit <value> Abort after this number of errors
--exe Link to create executable
@@ -321,61 +330,61 @@ detailed descriptions of these arguments.
--flatten Force inlining of all modules, tasks and functions
-fno-<optimization> Disable internal optimization stage
-G<name>=<value> Overwrite top-level parameter
--gate-stmts <value> Tune gate optimizer depth
--gdb Run Verilator under GDB interactively
--gdbbt Run Verilator under GDB for backtrace
--generate-key Create random key for --protect-key
--getenv <var> Get environment variable with defaults
--get-supported <feature> Get if feature is supported
--help Display this help
--hierarchical Enable hierarchical Verilation
-I<dir> Directory to search for includes
-j <jobs> Parallelism for --build
--gate-stmts <value> Tune gate optimizer depth
--if-depth <value> Tune IFDEPTH warning
+incdir+<dir> Directory to search for includes
--inline-mult <value> Tune module inlining
--instr-count-dpi <value> Assumed dynamic instruction count of DPI imports
-LDFLAGS <flags> Linker pre-object arguments for makefile
-j <jobs> Parallelism for --build (alias to --build-jobs)
--l2-name <value> Verilog scope name of the top module
--language <lang> Default language standard to parse
-LDFLAGS <flags> Linker pre-object arguments for makefile
--lib-create <name> Create a DPI library
+libext+<ext>+[ext]... Extensions for finding modules
--lint-only Lint, but do not make output
--make <build-tool> Generate scripts for specified build tool
-MAKEFLAGS <flags> Arguments to pass to make during --build
--main Generate C++ main() file
--max-num-width <value> Maximum number width (default: 64K)
--MMD Create .d dependency files
--MP Create phony dependency targets
--Mdir <directory> Name of output object directory
--MMD Create .d dependency files
--mod-prefix <topname> Name to prepend to lower classes
--no-clk <signal-name> Prevent marking specified signal as clock
--no-decoration Disable comments and symbol decorations
--no-pins64 Don't use uint64_t's for 33-64 bit sigs
--no-skip-identical Disable skipping identical output
--MP Create phony dependency targets
+notimingchecks Ignored
-O0 Disable optimizations
-O3 High performance optimizations
-O<optimization-letter> Selectable optimizations
-o <executable> Name of final executable
--no-order-clock-delay Disable ordering clock enable assignments
--no-verilate Skip verilation and just compile previously Verilated code.
--no-verilate Skip Verilation, only compile previously Verilated code
--output-split <statements> Split .cpp files into pieces
--output-split-cfuncs <statements> Split model functions
--output-split-ctrace <statements> Split tracing functions
-P Disable line numbers and blanks with -E
--pins-bv <bits> Specify types for top level ports
--pins-sc-uint Specify types for top level ports
--pins-sc-biguint Specify types for top level ports
--pins-sc-uint Specify types for top level ports
--pins-uint8 Specify types for top level ports
--no-pins64 Don't use uint64_t's for 33-64 bit sigs
--pipe-filter <command> Filter all input through a script
--pp-comments Show preprocessor comments with -E
--prefix <topname> Name of top level class
--private Debugging; see docs
--prof-c Compile C++ code with profiling
--prof-cfuncs Name functions for profiling
--prof-exec Enable generating execution profile for gantt chart
--prof-pgo Enable generating profiling data for PGO
--protect-key <key> Key for symbol protection
--protect-ids Hash identifier names for obscurity
--protect-key <key> Key for symbol protection
--protect-lib <name> Create a DPI protected library
--private Debugging; see docs
--public Debugging; see docs
--public-flat-rw Mark all variables, etc as public_flat_rw
-pvalue+<name>=<value> Overwrite toplevel parameter
@@ -386,6 +395,7 @@ detailed descriptions of these arguments.
--rr Run Verilator and record with rr
--savable Enable model save-restore
--sc Create SystemC output
--no-skip-identical Disable skipping identical output
--stats Create statistics file
--stats-vars Provide statistics on variables
-sv Enable SystemVerilog parsing
@@ -393,6 +403,8 @@ detailed descriptions of these arguments.
--threads <threads> Enable multithreading
--threads-dpi <mode> Enable multithreaded DPI
--threads-max-mtasks <mtasks> Tune maximum mtask partitioning
--timing Enable timing support
--no-timing Disable timing support
--timescale <timescale> Sets default timescale
--timescale-override <timescale> Overrides all timescales
--top <topname> Alias of --top-module
@@ -413,6 +425,7 @@ detailed descriptions of these arguments.
--unused-regexp <regexp> Tune UNUSED lint signals
-V Verbose version and config
-v <filename> Verilog library
--no-verilate Skip verilation and just compile previously Verilated code.
+verilog1995ext+<ext> Synonym for +1364-1995ext+<ext>
+verilog2001ext+<ext> Synonym for +1364-2001ext+<ext>
--version Displays program version and exits
@@ -427,6 +440,9 @@ detailed descriptions of these arguments.
-Wno-lint Disable all lint warnings
-Wno-style Disable all style warnings
-Wpedantic Warn on compliance-test issues
-Wwarn-<message> Enable specified warning message
-Wwarn-lint Enable lint warning message
-Wwarn-style Enable style warning message
--x-assign <mode> Assign non-initial Xs to this value
--x-initial <mode> Assign initial Xs to this value
--x-initial-edge Enable initial X->0 and X->1 edge triggers
+1 -1
View File
@@ -38,7 +38,7 @@ def diff_dir(a, b):
anyfile = False
for base in sorted(files.keys()):
if (not 'a' in files[base]) or (not 'b' in files[base]):
if ('a' not in files[base]) or ('b' not in files[base]):
continue
a = files[base]['a']
b = files[base]['b']
+2 -1
View File
@@ -149,7 +149,8 @@ def report():
for thread in Threads:
# Make potentially multiple characters per column
for start in Threads[thread]:
if not Threads[thread][start]: continue
if not Threads[thread][start]:
continue
cpu = Threads[thread][start]['cpu']
elapsed = Threads[thread][start]['end'] - start
if cpu not in Global['cpus']:
+4
View File
@@ -19,6 +19,10 @@ set -x
cd $(dirname "$0")/..
# Avoid occasional cpan failures "Issued certificate has expired."
export PERL_LWP_SSL_VERIFY_HOSTNAME=0
echo "check_certificate = off" >> ~/.wgetrc
fatal() {
echo "ERROR: $(basename "$0"): $1" >&2; exit 1;
}
+1 -1
View File
@@ -6,7 +6,7 @@
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
FROM ubuntu:20.04
FROM ubuntu:22.04
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive \
+1 -1
View File
@@ -8,7 +8,7 @@ Verilator build. It uses the following parameters:
- Source revision (default: master)
- Compiler (GCC 9.3.0, clang 10.0.0, default: 9.3.0)
- Compiler (GCC 10.3.0, clang 10.0.0, default: 10.3.0)
The container is published as ``verilator/verilator-buildenv`` on `docker
hub
+1 -1
View File
@@ -6,7 +6,7 @@
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
FROM ubuntu:20.04
FROM ubuntu:22.04
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive \
+36 -6
View File
@@ -5,14 +5,14 @@
# General Public License Version 3 or the Perl Artistic License Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[4.224 2022-06-19],
[https://verilator.org],
[verilator],[https://verilator.org])
# When releasing, also update header of Changes file
# and commit using "devel release" or "Version bump" message
# Then 'make maintainer-dist'
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[5.002 2022-10-29],
[https://verilator.org],
[verilator],[https://verilator.org])
AC_CONFIG_HEADERS(src/config_build.h)
AC_CONFIG_FILES(Makefile src/Makefile src/Makefile_obj include/verilated.mk include/verilated_config.h verilator.pc verilator-config.cmake verilator-config-version.cmake)
@@ -355,7 +355,7 @@ AC_SUBST(CFG_CXXFLAGS_PROFILE)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++20)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++20)
case "$(which lsb_release 2>&1 > /dev/null && lsb_release -d)" in
*Ubuntu*22.04*)
*Arch*Linux* | *Ubuntu*22.04*)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++17)
;;
@@ -374,6 +374,36 @@ _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_WEXTRA,-Wlogical-op)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_WEXTRA,-Wthread-safety)
AC_SUBST(CFG_CXXFLAGS_WEXTRA)
# Flags for coroutine support for dynamic scheduling
_MY_CXX_CHECK_IFELSE(
-fcoroutines-ts,
[CFG_CXXFLAGS_COROUTINES="-fcoroutines-ts"],
[CFG_CXXFLAGS_COROUTINES="-fcoroutines"])
AC_SUBST(CFG_CXXFLAGS_COROUTINES)
# HAVE_COROUTINES
# Check if coroutines are supported at all
AC_MSG_CHECKING([whether coroutines are supported by $CXX])
ACO_SAVE_CXXFLAGS="$CXXFLAGS"
CXXFLAGS="$CXXFLAGS $CFG_CXXFLAGS_COROUTINES"
AC_LINK_IFELSE(
[AC_LANG_PROGRAM([
#ifdef __clang__
#define __cpp_impl_coroutine 1
#endif
#include <coroutine>
],[[]])],
[_my_result=yes
AC_DEFINE([HAVE_COROUTINES],[1],[Defined if coroutines are supported by $CXX])],
[AC_LINK_IFELSE(
[AC_LANG_PROGRAM([#include <experimental/coroutine>],[[]])],
[_my_result=yes
AC_DEFINE([HAVE_COROUTINES],[1],[Defined if coroutines are supported by $CXX])],
[_my_result=no])])
AC_MSG_RESULT($_my_result)
CXXFLAGS="$ACO_SAVE_CXXFLAGS"
AC_SUBST(HAVE_COROUTINES)
# Flags for compiling Verilator internals including parser always
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Qunused-arguments)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-faligned-new)
+15
View File
@@ -5,11 +5,13 @@ Please see the Verilator manual for 200+ additional contributors. Thanks to all.
Adrien Le Masle
Ahmed El-Mahmoudy
Aleksander Kiryk
Alex Chadwick
Àlex Torregrosa
Aliaksei Chapyzhenka
Ameya Vikram Singh
Andreas Kuster
Arkadiusz Kozdra
Chris Randall
Chuxuan Wang
Conor McCullough
@@ -32,6 +34,7 @@ Gianfranco Costamagna
Glen Gibb
Graham Rushton
Guokai Chen
Gustav Svensk
Harald Heckmann
Howard Su
Huang Rui
@@ -41,6 +44,7 @@ HyungKi Jeong
Iru Cai
Ivan Vnučec
Iztok Jeras
Jake Merdich
James Hanlon
James Hutchinson
James Pallister
@@ -49,6 +53,7 @@ Jamie Iles
Jan Van Winkel
Jean Berniolles
Jeremy Bennett
Jiuyang Liu
John Coiner
John Demme
Jonathan Drolet
@@ -67,7 +72,9 @@ Ludwig Rogiers
Lukasz Dalek
Maarten De Braekeleer
Maciej Sobkowski
Marcel Chang
Marco Widmer
Mariusz Glebocki
Markus Krause
Marlon James
Marshal Qiao
@@ -78,12 +85,15 @@ Michael Killough
Michaël Lefebvre
Mike Popoloski
Miodrag Milanović
Mladen Slijepcevic
Morten Borup Petersen
Mostafa Gamal
Nandu Raj
Nathan Kohagen
Nathan Myers
Patrick Stewart
Paul Wright
Pawel Sagan
Peter Horvath
Peter Monsson
Philipp Wagner
@@ -95,6 +105,7 @@ Rafal Kapuscik
Raynard Qiao
Richard Myers
Rupert Swarbrick
Ryszard Rozak
Samuel Riedel
Sean Cross
Sebastien Van Cauwenberghe
@@ -108,17 +119,21 @@ Tobias Rosenkranz
Tobias Wölfel
Todd Strader
Tomasz Gorochowik
Topa Topino
Tymoteusz Blazejczyk
Udi Finkelstein
Unai Martinez-Corral
Varun Koyyalagunta
Vassilis Papaefstathiou
Veripool API Bot
Victor Besyakov
William D. Jones
Wilson Snyder
Xi Zhang
Yoda Lee
Yossi Nivin
Yuri Victorovich
Yutetsu TAKATSUKASA
Yu-Sheng Lin
Yves Mathieu
Zhanglei Wang
+2 -4
View File
@@ -1,7 +1,5 @@
.. comment: generated by t_lint_didnotconverge_bad
.. code-block::
-V{t#,#}+ Vt_lint_didnotconverge_bad___024root___change_request
-V{t#,#}+ Vt_lint_didnotconverge_bad___024root___change_request_1
-V{t#,#} CHANGE: t/t_lint_didnotconverge_bad.v:14: a
%Error: t/t_lint_didnotconverge_bad.v:7: Verilated model didn't converge
-V{t#,#} 'stl' region trigger index 1 is active: @([hybrid] a)
%Error: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge.
+1 -1
View File
@@ -1,4 +1,4 @@
.. comment: generated by t_lint_didnotconverge_nodbg_bad
.. code-block::
%Error: t/t_lint_didnotconverge_bad.v:7: Verilated model didn't converge
%Error: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge.
+1 -1
View File
@@ -1,4 +1,4 @@
.. comment: generated by t_lint_stmtdly_bad
.. code-block::
%Warning-STMTDLY: example.v:1:8 Unsupported: Ignoring delay on this delayed statement.
%Warning-STMTDLY: example.v:1:7 Ignoring delay on this statement due to --no-timing
+1
View File
@@ -13,6 +13,7 @@ from datetime import datetime
import os
import re
import sys
sys.path.insert(0, os.path.abspath('./_ext'))
import sphinx_rtd_theme # pylint: disable=wrong-import-position,
+33 -58
View File
@@ -86,61 +86,20 @@ Connecting to C++
In C++ output mode (:vlopt:`--cc`), the Verilator generated model class is a
simple C++ class. The user must write a C++ wrapper and main loop for the
simulation, which instantiates the model class, and link with the Verilated
model. Here is a simple example:
model.
.. code-block:: C++
Refer to ``examples/make_tracing_c`` in the distribution for a detailed
commented example.
#include <verilated.h> // Defines common routines
#include <iostream> // Need std::cout
#include "Vtop.h" // From Verilating "top.v"
Top level IO signals are read and written as members of the model. You
call the model's :code:`eval()` method to evaluate the model. When the
simulation is complete call the model's :code:`final()` method to execute
any SystemVerilog final blocks, and complete any assertions. If using
:vlopt:`--timing`, there are two additional functions for checking if
there are any events pending in the simulation due to delays, and for
retrieving the simulation time of the next delayed event. See
:ref:`Evaluation Loop`.
Vtop *top; // Instantiation of model
uint64_t main_time = 0; // Current simulation time
// This is a 64-bit integer to reduce wrap over issues and
// allow modulus. This is in units of the timeprecision
// used in Verilog (or from --timescale-override)
double sc_time_stamp() { // Called by $time in Verilog
return main_time; // converts to double, to match
// what SystemC does
}
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv); // Remember args
top = new Vtop; // Create model
// Do not instead make Vtop as a file-scope static
// variable, as the "C++ static initialization order fiasco"
// may cause a crash
top->reset_l = 0; // Set some inputs
while (!Verilated::gotFinish()) {
if (main_time > 10) {
top->reset_l = 1; // Deassert reset
}
if ((main_time % 10) == 1) {
top->clk = 1; // Toggle clock
}
if ((main_time % 10) == 6) {
top->clk = 0;
}
top->eval(); // Evaluate model
cout << top->out << endl; // Read a output
main_time++; // Time passes...
}
top->final(); // Done simulating
// // (Though this example doesn't get here)
delete top;
}
Note top level IO signals are read and written as members of the model. You
call the :code:`eval()` method to evaluate the model. When the simulation is
complete call the :code:`final()` method to execute any SystemVerilog final
blocks, and complete any assertions. See :ref:`Evaluation Loop`.
Connecting to SystemC
@@ -449,14 +408,15 @@ accesses the above signal "readme" would be:
int main(int argc, char** argv, char** env) {
Verilated::commandArgs(argc, argv);
Vour* top = new Vour;
Verilated::internalsDump(); // See scopes to help debug
while (!Verilated::gotFinish()) {
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
const std::unique_ptr<Vour> top{new Vour{contextp.get()}};
contextp->internalsDump(); // See scopes to help debug
while (!contextp->gotFinish()) {
top->eval();
VerilatedVpi::callValueCbs(); // For signal callbacks
read_and_check();
}
delete top;
return 0;
}
EOF
@@ -483,10 +443,25 @@ there is only a single design, you would call :code:`eval_step()` then
:code:`eval_end_step()`; in fact :code:`eval()` described above is just a
wrapper which calls these two functions.
3. If using delays and :vlopt:`--timing`, there are two additional methods
the user should call:
* :code:`designp->eventsPending()`, which returns :code:`true` if there are
any delayed events pending,
* :code:`designp->nextTimeSlot()`, which returns the simulation time of the
next delayed event. This method can only be called if
:code:`designp->nextTimeSlot()` returned :code:`true`.
Call :code:`eventsPending()` to check if you should continue with the
simulation, and then :code:`nextTimeSlot()` to move simulation time forward.
:vlopt:`--main` can be used with :vlopt:`--timing` to generate a basic example
of a timing-enabled eval loop.
When :code:`eval()` (or :code:`eval_step()`) is called Verilator looks for
changes in clock signals and evaluates related sequential always blocks,
such as computing always_ff @ (posedge...) outputs. Then Verilator
evaluates combinatorial logic.
such as computing always_ff @ (posedge...) outputs. With :vlopt:`--timing`, it
resumes any delayed processes awaiting the current simulation time. Then
Verilator evaluates combinational logic.
Note combinatorial logic is not computed before sequential always blocks
are computed (for speed reasons). Therefore it is best to set any non-clock
+96 -80
View File
@@ -23,7 +23,8 @@ Contributors
Many people have provided ideas and other assistance with Verilator.
Verilator is receiving major development support from the `CHIPS Alliance
<https://chipsalliance.org>`_ and `Shunyao CAD <https://shunyaocad.com>`_.
<https://chipsalliance.org>`_, `Antmicro Ltd <https://antmicro.com>`_ and
`Shunyao CAD <https://shunyaocad.com>`_.
Previous major corporate sponsors of Verilator, by providing significant
contributions of time or funds included include: Atmel Corporation, Cavium
@@ -32,90 +33,101 @@ Hicamp Systems, Intel Corporation, Mindspeed Technologies Inc., MicroTune
Inc., picoChip Designs Ltd., Sun Microsystems Inc., Nauticus Networks Inc.,
SiCortex Inc, and Shunyao CAD.
The people who have contributed major functionality are: Byron Bradley,
Jeremy Bennett, Lane Brooks, John Coiner, Duane Galbi, Geza Lore, Todd
Strader, Stefan Wallentowitz, Paul Wasson, Jie Xu, and Wilson Snyder.
Major testers included Jeff Dutton, Jonathon Donaldson, Ralf Karge, David
Hewson, Iztok Jeras, Wim Michiels, Alex Solomatnikov, Sebastien Van
Cauwenberghe, Gene Weber, and Clifford Wolf.
The people who have contributed major functionality are: Krzysztof
Bieganski, Byron Bradley, Jeremy Bennett, Lane Brooks, John Coiner, Duane
Galbi, Geza Lore, Todd Strader, Stefan Wallentowitz, Paul Wasson, Jie Xu,
and Wilson Snyder. Major testers included Jeff Dutton, Jonathon Donaldson,
Ralf Karge, David Hewson, Iztok Jeras, Wim Michiels, Alex Solomatnikov,
Sebastien Van Cauwenberghe, Gene Weber, and Clifford Wolf.
Some of the people who have provided ideas, and feedback for Verilator
include: David Addison, Nikana Anastasiadis, Vasu Arasanipalai, Jens Arm,
Tariq B. Ahmad, Sharad Bagri, Matthew Ballance, Andrew Bardsley, Matthew
Barr, Geoff Barrett, Kaleb Barrett, Julius Baxter, Jeremy Bennett, Michael
Berman, Jean Berniolles, Victor Besyakov, Moinak Bhattacharyya, Krzysztof
Bieganski, David Binderman, Piotr Binkowski, Johan Bjork, David Black,
Tymoteusz Blazejczyk, Daniel Bone, Morten Borup Petersen, Gregg Bouchard,
Christopher Boumenot, Nick Bowler, Byron Bradley, Bryan Brady, Charlie
Brej, J Briquet, Lane Brooks, John Brownlee, Jeff Bush, Lawrence Butcher,
Tony Bybell, Iru Cai, Ted Campbell, Chris Candler, Lauren Carlson, Donal
Casey, Alex Chadwick, Terry Chen, Yi-Chung Chen, Enzo Chi, Robert A. Clark,
Ryan Clarke, Allan Cochrane, John Coiner, Gianfranco Costamagna, Sean
Cross, George Cuan, Joe DErrico, Lukasz Dalek, Gunter Dannoritzer, Ashutosh
Das, Maarten De Braekeleer, Bernard Deadman, Alberto Del Rio, John Demme,
Mike Denio, John Deroo, Philip Derrick, John Dickol, Ruben Diez, Danny
Ding, Jacko Dirks, Ivan Djordjevic, Jonathon Donaldson, Sebastian Dressler,
Alex Duller, Jeff Dutton, Tomas Dzetkulic, Richard E George, Edgar
E. Iglesias, Usuario Eda, Charles Eddleston, Chandan Egbert, Jan Egil Ruud,
Joe Eiler, Ahmed El-Mahmoudy, Trevor Elbourne, Mats Engstrom, Charles Eric
LaForest, Robert Farrell, Eugen Fekete, Fabrizio Ferrandi, Udi Finkelstein,
Brian Flachs, Andrea Foletto, Bob Fredieu, Duane Galbi, Benjamin Gartner,
Christian Gelinek, Peter Gerst, Glen Gibb, Michael Gielda, Barbara Gigerl,
Shankar Giri, Dan Gisselquist, Petr Gladkikh, Sam Gladstone, Andrew
Goessling, Amir Gonnen, Chitlesh Goorah, Tomasz Gorochowik, Kai Gossner,
Sergi Granell, Al Grant, Alexander Grobman, Xuan Guo, Driss Hafdi, Neil
include:
David Addison, Tariq B. Ahmad, Nikana Anastasiadis, Vasu Arasanipalai, Jens
Arm, Sharad Bagri, Matthew Ballance, Andrew Bardsley, Matthew Barr, Geoff
Barrett, Kaleb Barrett, Julius Baxter, Jeremy Bennett, Michael Berman, Jean
Berniolles, Victor Besyakov, Moinak Bhattacharyya, Krzysztof Bieganski,
David Binderman, Piotr Binkowski, Johan Bjork, David Black, Tymoteusz
Blazejczyk, Daniel Bone, Gregg Bouchard, Christopher Boumenot, Nick Bowler,
Byron Bradley, Bryan Brady, Maarten De Braekeleer, Charlie Brej, J Briquet,
Lane Brooks, John Brownlee, Jeff Bush, Lawrence Butcher, Tony Bybell, Iru
Cai, Ted Campbell, Chris Candler, Lauren Carlson, Donal Casey, Alex
Chadwick, Marcel Chang, Aliaksei Chapyzhenka, Guokai Chen, Terry Chen,
Yi-Chung Chen, Enzo Chi, Robert A. Clark, Ryan Clarke, Allan Cochrane, John
Coiner, Keith Colbert, Gianfranco Costamagna, Sean Cross, George Cuan, Joe
DErrico, Lukasz Dalek, Gunter Dannoritzer, Ashutosh Das, Bernard Deadman,
John Demme, Mike Denio, John Deroo, Philip Derrick, John Dickol, Ruben
Diez, Danny Ding, Jacko Dirks, Ivan Djordjevic, Jonathon Donaldson, Larry
Doolittle, Sebastian Dressler, Jonathan Drolet, Alex Duller, Jeff Dutton,
Tomas Dzetkulic, Usuario Eda, Charles Eddleston, Chandan Egbert, Joe Eiler,
Ahmed El-Mahmoudy, Trevor Elbourne, Mats Engstrom, Robert Farrell, Eugen
Fekete, Fabrizio Ferrandi, Udi Finkelstein, Brian Flachs, Bill Flynn,
Andrea Foletto, Bob Fredieu, Duane Galbi, Mostafa Gamal, Benjamin Gartner,
Christian Gelinek, Richard E George, Peter Gerst, Glen Gibb, Michael
Gielda, Barbara Gigerl, Shankar Giri, Dan Gisselquist, Petr Gladkikh, Sam
Gladstone, Mariusz Glebocki, Andrew Goessling, Amir Gonnen, Chitlesh
Goorah, Tomasz Gorochowik, Kai Gossner, Sergi Granell, Al Grant, Nathan
Graybeal, Alexander Grobman, Graham Rushton, Xuan Guo, Driss Hafdi, Neil
Hamilton, James Hanlon, Oyvind Harboe, Jannis Harder, Junji Hashimoto,
Thomas Hawkins, Mitch Hayenga, Harald Heckmann, Robert Henry, Stephen
Henry, David Hewson, Jamey Hicks, Joel Holdsworth, Andrew Holme, Hiroki
Honda, Alex Hornung, Pierre-Henri Horrein, David Horton, Peter Horvath, Jae
Hossell, Kuoping Hsu, Alan Hunter, James Hutchinson, Anderson Ignacio da
Silva, Jamie Iles, Thomas J Whatson, Ben Jackson, Mark Jackson Pulver,
Shareef Jalloq, Marlon James, Krzysztof Jankowski, HyungKi Jeong, Iztok
Jeras, James Johnson, Christophe Joly, Franck Jullien, James Jung, Mike
Kagen, Arthur Kahlich, Kaalia Kahn, Guy-Armand Kamendje, Vasu Kandadi,
Kanad Kanhere, Patricio Kaplan, Pieter Kapsenberg, Rafal Kapuscik, Ralf
Karge, Dan Katz, Sol Katzman, Ian Kennedy, Michael Killough, Jonathan
Kimmitt, Olof Kindgren, Kevin Kiningham, Dan Kirkham, Sobhan Klnv, Gernot
Koch, Jack Koenig, Soon Koh, Nathan Kohagen, Steve Kolecki, Brett Koonce,
Will Korteland, Wojciech Koszek, Varun Koyyalagunta, Markus Krause, David
Kravitz, Roland Kruse, Andreas Kuster, Sergey Kvachonok, Ed Lander, Steve
Lang, Stephane Laurent, Walter Lavino, Christian Leber, Larry Lee, Igor
Lesik, John Li, Eivind Liland, Charlie Lind, Andrew Ling, Jiuyang Liu, Paul
Liu, Derek Lockhart, Jake Longo, Geza Lore, Arthur Low, Stefan Ludwig, Dan
Lussier, Fred Ma, Duraid Madina, Odd Magne Reitan, Affe Mao, Julien
Margetts, Mark Marshall, Alfonso Martinez, Unai Martinez-Corral, Yves
Mathieu, Patrick Maupin, Conor McCullough, Jason McMullan, Elliot Mednick,
Wim Michiels, Miodrag Milanovic, Peter Monsson, Sean Moore, Dennis
Muhlestein, John Murphy, Matt Myers, Nathan Myers, Richard Myers, Dimitris
Nalbantis, Peter Nelson, Bob Newgard, Paul Nitza, Yossi Nivin, Pete Nixon,
Lisa Noack, Mark Nodine, Kuba Ober, Andreas Olofsson, Baltazar Ortiz,
Aleksander Osman, Don Owen, James Pallister, Vassilis Papaefstathiou, Brad
Parker, Dan Petrisko, Maciej Piechotka, David Pierce, Cody Piersall,
Dominic Plunkett, David Poole, Mike Popoloski, Roman Popov, Rich Porter,
Niranjan Prabhu, Usha Priyadharshini, Prateek Puri, Marshal Qiao, Nandu
Raj, Danilo Ramos, Chris Randall, Anton Rapp, Josh Redford, Frederic
Requin, Dustin Richmond, Samuel Riedel, Eric Rippey, Oleg Rodionov, Ludwig
Rogiers, Paul Rolfe, Arjen Roodselaar, Tobias Rosenkranz, Huang Rui, Denis
Rystsov, John Sanguinetti, Galen Seitz, Joseph Shaker, Salman Sheikh, Yu
Sheng Lin, Hao Shi, Mike Shinkarovsky, Rafael Shirakawa, Jeffrey Short, Fan
Shupei, Rodney Sinclair, Steven Slatter, Brian Small, Garrett Smith, Tim
Snyder, Wilson Snyder, Maciej Sobkowski, Stan Sokorac, Alex Solomatnikov,
Wei Song, Art Stamness, David Stanford, John Stevenson, Pete Stevenson,
Patrick Stewart, Rob Stoddard, Todd Strader, John Stroebel, Sven Stucki,
Howard Su, Emerson Suguimoto, Gene Sullivan, Wai Sum Mong, Qingyao Sun,
Renga Sundararajan, Rupert Swarbrick, Yutetsu Takatsukasa, Thierry Tambe,
Drew Taussig, Peter Tengstrand, Wesley Terpstra, Rui Terra, Stefan Thiede,
Gary Thomas, Ian Thompson, Kevin Thompson, Mike Thyer, Hans Tichelaar,
Viktor Tomov, Steve Tong, Alex Torregrosa, Michael Tresidder, David Turner,
Neil Turton, Cong Van Nguyen, Hans Van Antwerpen, Jan Van Winkel, Sebastien
Van Cauwenberghe, Laurens van Dam, Leendert van Doorn, Srini Vemuri, Yuri
Hossell, Kuoping Hsu, Teng Huang, Steven Hugg, Alan Hunter, James
Hutchinson, Ehab Ibrahim, Edgar E. Iglesias, Jamie Iles, Vighnesh Iyer, Ben
Jackson, Shareef Jalloq, Marlon James, Krzysztof Jankowski, HyungKi Jeong,
Iztok Jeras, Alexandre Joannou, James Johnson, Christophe Joly, Franck
Jullien, James Jung, Mike Kagen, Arthur Kahlich, Kaalia Kahn, Guy-Armand
Kamendje, Vasu Kandadi, Kanad Kanhere, Patricio Kaplan, Pieter Kapsenberg,
Rafal Kapuscik, Ralf Karge, Per Karlsson, Dan Katz, Sol Katzman, Ian
Kennedy, Michael Killough, Sun Kim, Jonathan Kimmitt, Olof Kindgren, Kevin
Kiningham, Dan Kirkham, Aleksander Kiryk, Sobhan Klnv, Gernot Koch, Jack
Koenig, Soon Koh, Nathan Kohagen, Steve Kolecki, Brett Koonce, Will
Korteland, Wojciech Koszek, Varun Koyyalagunta, Arkadiusz Kozdra, Markus
Krause, David Kravitz, Roland Kruse, Andreas Kuster, Sergey Kvachonok,
Charles Eric LaForest, Ed Lander, Steve Lang, Stephane Laurent, Walter
Lavino, Christian Leber, Larry Lee, Yoda Lee, Michaël Lefebvre, Igor Lesik,
John Li, Eivind Liland, Yu Sheng Lin, Charlie Lind, Andrew Ling, Jiuyang
Liu, Paul Liu, Derek Lockhart, Jake Longo, Geza Lore, Arthur Low, Stefan
Ludwig, Dan Lussier, Fred Ma, Duraid Madina, Affe Mao, Julien Margetts,
Mark Marshall, Alfonso Martinez, Unai Martinez-Corral, Adrien Le Masle,
Yves Mathieu, Patrick Maupin, Conor McCullough, Jason McMullan, Elliot
Mednick, David Metz, Wim Michiels, Miodrag Milanovic, Kevin Millis, Wai Sum
Mong, Peter Monsson, Sean Moore, Dennis Muhlestein, John Murphy, Matt
Myers, Nathan Myers, Richard Myers, Dimitris Nalbantis, Peter Nelson, Bob
Newgard, Rachit Nigam, Paul Nitza, Yossi Nivin, Pete Nixon, Lisa Noack,
Mark Nodine, Kuba Ober, Andreas Olofsson, Baltazar Ortiz, Aleksander Osman,
Don Owen, James Pallister, Vassilis Papaefstathiou, Brad Parker, Morten
Borup Petersen, Dan Petrisko, Maciej Piechotka, David Pierce, Cody
Piersall, Michael Platzer, Dominic Plunkett, David Poole, Mike Popoloski,
Roman Popov, Rich Porter, Stefan Post, Niranjan Prabhu, Damien Pretet, Usha
Priyadharshini, Mark Jackson Pulver, Prateek Puri, Marshal Qiao, Nandu Raj,
Kamil Rakoczy, Danilo Ramos, Drew Ranck, Chris Randall, Anton Rapp, Josh
Redford, Odd Magne Reitan, Frederic Requin, Dustin Richmond, Samuel Riedel,
Alberto Del Rio, Eric Rippey, Oleg Rodionov, Ludwig Rogiers, Paul Rolfe,
Arjen Roodselaar, Tobias Rosenkranz, Ryszard Rozak, Huang Rui, Graham
Rushton, Jan Egil Ruud, Denis Rystsov, John Sanguinetti, Martin Schmidt,
Julie Schwartz, Galen Seitz, Joseph Shaker, Salman Sheikh, Hao Shi, Mike
Shinkarovsky, Rafael Shirakawa, Jeffrey Short, Fan Shupei, Anderson Ignacio
da Silva, Rodney Sinclair, Ameya Vikram Singh, Steven Slatter, Mladen
Slijepcevic, Brian Small, Garrett Smith, Tim Snyder, Wilson Snyder, Maciej
Sobkowski, Stan Sokorac, Alex Solomatnikov, Flavien Solt, Wei Song, Trefor
Southwell, Martin Stadler, Art Stamness, David Stanford, John Stevenson,
Pete Stevenson, Patrick Stewart, Rob Stoddard, Todd Strader, John Stroebel,
Sven Stucki, Howard Su, Emerson Suguimoto, Gene Sullivan, Qingyao Sun,
Renga Sundararajan, Gustav Svensk, Rupert Swarbrick, Yutetsu Takatsukasa,
Thierry Tambe, Drew Taussig, Jose Tejada, Peter Tengstrand, Wesley
Terpstra, Rui Terra, Stefan Thiede, Gary Thomas, Ian Thompson, Kevin
Thompson, Mike Thyer, Hans Tichelaar, Viktor Tomov, Steve Tong, Topa
Topino, Alex Torregrosa, Michael Tresidder, David Turner, Neil Turton, Mike
Urbach, Hans Van Antwerpen, Sebastien Van Cauwenberghe, Laurens van Dam,
Leendert van Doorn, Cong Van Nguyen, Jan Van Winkel, Srini Vemuri, Yuri
Victorovich, Bogdan Vukobratovic, Holger Waechtler, Philipp Wagner, Stefan
Wallentowitz, Shawn Wang, Paul Wasson, Greg Waters, Thomas Watts, Eugene
Weber, David Welch, Martin Whitaker, Marco Widmer, Leon Wildman, Daniel
Wilkerson, Gerald Williams, Trevor Williams, Jeff Winston, Joshua Wise,
Clifford Wolf, Tobias Wolfel, Johan Wouters, Paul Wright, Junyi Xi, Ding
Xiaoliang, Jie Xu, Mandy Xu, Yinan Xu, Luke Yang, Amir Yazdanbakhsh, and
Keyi Zhang.
Wallentowitz, Shawn Wang, Zhanglei Wang, Paul Wasson, Greg Waters, Thomas
Watts, Eugene Weber, David Welch, Thomas J Whatson, Martin Whitaker, Marco
Widmer, Leon Wildman, Daniel Wilkerson, Gerald Williams, Trevor Williams,
Jeff Winston, Joshua Wise, Clifford Wolf, Tobias Wolfel, Johan Wouters,
Paul Wright, Junyi Xi, Ding Xiaoliang, Jie Xu, Mandy Xu, Yinan Xu, Luke
Yang, Amir Yazdanbakhsh, Keyi Zhang, and Xi Zhang.
Thanks to them, and all those we've missed including above, or wished to
remain anonymous.
@@ -151,6 +163,10 @@ In 2018, Verilator 4.000 was released with multithreaded support.
In 2019, Verilator joined the `CHIPS Alliance
<https://chipsalliance.org>`_.
In 2022, Verilator 5.000 was released with IEEE scheduling semantics,
fork/join, delay handling, DFG performance optimizations, and other
improvements.
Currently, various language features and performance enhancements are added
as the need arises. Verilator is now about 3x faster than in 2002, and is
faster than most (if not every) other simulator.
as the need arises, with a focus towards getting to full Universal
Verification Methodology (UVM, IEEE 1800.2-2017) support.
+9 -3
View File
@@ -7,9 +7,15 @@ Deprecations
The following deprecated items are scheduled for future removal:
C++11 compiler support
Verilator currently requires C++11 or newer compilers. Verilator will
require C++14 or newer compilers for both compiling Verilator and
compiling Verilated models no sooner than January 2023.
Verilator currently requires a C++20 or newer compiler for timing, and a
C++11 or newer compiler for no-timing.
Verilator will require C++14 or newer compilers for both compiling
Verilator and compiling Verilated models with --no-timing no sooner than
January 2023.
Verilator will require C++20 or newer compilers for both compiling
Verilator and compiling all Verilated models no sooner than January 2025.
Verilated_heavy.h
The legacy "verilated_heavy.h" include was replaced with just including
+63
View File
@@ -0,0 +1,63 @@
.. Copyright 2003-2022 by Wilson Snyder.
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
.. _Example Create-Binary Execution:
Example Create-Binary Execution
===============================
We'll compile this SystemVerilog example into a Verilated simulation binary. For
an example that discusses the next level of detail see :ref:`Example C++
Execution`.
.. include:: example_common_install.rst
Now, let's create an example Verilog file:
.. code-block:: bash
mkdir test_our
cd test_our
cat >our.v <<'EOF'
module our;
initial begin $display("Hello World"); $finish; end
endmodule
EOF
Now we run Verilator on our little example.
.. code-block:: bash
verilator --binary -j 0 -Wall our.v
Breaking this command down:
#. :vlopt:`--binary` telling Verilator to do everything needed to create a
simulation executable.
#. :vlopt:`-j` `0` to Verilate using use as many CPU threads as the machine
has.
#. :vlopt:`-Wall` so Verilator has stronger lint warnings
enabled.
#. An finally, :command:`our.v` which is our SystemVerilog design file.
And now we run it:
.. code-block:: bash
obj_dir/Vour
And we get as output:
.. code-block:: bash
Hello World
- our.v:2: Verilog $finish
Really, you're better off using a Makefile to run the steps for you so when
your source changes it will automatically run all of the appropriate steps.
To aid this Verilator can create a makefile dependency file. For examples
that do this see the :file:`examples` directory in the distribution.
+7 -4
View File
@@ -43,13 +43,10 @@ Now we run Verilator on our little example.
.. code-block:: bash
verilator -Wall --cc --exe --build sim_main.cpp our.v
verilator --cc --exe --build -j 0 -Wall sim_main.cpp our.v
Breaking this command down:
#. :vlopt:`-Wall` so Verilator has stronger lint warnings
enabled.
#. :vlopt:`--cc` to get C++ output (versus e.g. SystemC
or only linting).
@@ -61,6 +58,12 @@ Breaking this command down:
own compile rules, and run make yourself as we show in :ref:`Example
SystemC Execution`.)
#. :vlopt:`-j` `0' to Verilate using use as many CPU threads as the machine
has.
#. :vlopt:`-Wall` so Verilator has stronger lint warnings
enabled.
#. An finally, :command:`our.v` which is our SystemVerilog design file.
Once Verilator completes we can see the generated C++ code under the
+2
View File
@@ -10,6 +10,8 @@ See the ``examples/`` directory that is part of the distribution, and
is installed (in a OS-specific place, often in e.g.
``/usr/local/share/verilator/examples``). These examples include:
examples/make_hello_binary
Example GNU-make simple Verilog->binary conversion
examples/make_hello_c
Example GNU-make simple Verilog->C++ conversion
examples/make_hello_sc
+1 -1
View File
@@ -43,7 +43,7 @@ Now we run Verilator on our little example:
.. code-block:: bash
verilator -Wall --sc --exe sc_main.cpp our.v
verilator --sc --exe -Wall sc_main.cpp our.v
This example does not use --build, therefore we need to explicitly compile
it:
+1
View File
@@ -17,6 +17,7 @@ This section covers the following examples:
:maxdepth: 1
:hidden:
example_binary.rst
example_cc.rst
example_sc.rst
example_dist.rst
+3 -3
View File
@@ -8,7 +8,7 @@ Simulation Runtime Arguments
The following are the arguments that may be passed to a Verilated
executable, provided that executable calls
:code:`Verilated::commandArgs()`.
:code:`VerilatedContext*->commandArgs(argc, argv)`.
All simulation runtime arguments begin with "+verilator", so that the
user's executable may skip over all "+verilator" arguments when parsing its
@@ -72,7 +72,7 @@ Summary:
.. option:: +verilator+prof+threads+window+<value>
Deprecated. Alias for :vlopt:`+verilator+prof+exec+window+\<filename\>`
Deprecated. Alias for :vlopt:`+verilator+prof+exec+window+\<value\>`
.. option:: +verilator+prof+vlt+file+<filename>
@@ -96,7 +96,7 @@ Summary:
.. option:: +verilator+noassert
Disable assert checking per runtime argument. This is the same as
calling :code:`Verilated::assertOn(false)` in the model.
calling :code:`VerilatedContext*->assertOn(false)` in the model.
.. option:: +verilator+V
+286 -121
View File
@@ -115,12 +115,12 @@ Summary:
Using this argument will likely cause incorrect simulation.
.. option:: --bin <filename>
.. option:: --binary
Rarely needed. Override the default filename for Verilator itself.
When a dependency (.d) file is created, this filename will become a
source dependency, such that a change in this binary will have make
rebuild the output files.
Create a Verilated simulator binary. Alias for :vlopt:`--main`
:vlopt:`--exe` :vlopt:`--build` :vlopt:`--timing`.
See also :vlopt:`-j`.
.. option:: --build
@@ -129,15 +129,23 @@ Summary:
is also used). Verilator manages the build itself, and for this --build
requires GNU Make to be available on the platform.
.. option:: -CFLAGS <flags>
.. option:: --build-dep-bin <filename>
Add specified C compiler argument to the generated makefiles. For
multiple flags either pass them as a single argument with space
separators quoted in the shell (:command:`-CFLAGS "-a -b"`), or use
multiple -CFLAGS options (:command:`-CFLAGS -a -CFLAGS -b`).
Rarely needed. When a dependency (.d) file is created, this filename
will become a source dependency, such that a change in this binary will
have make rebuild the output files. Defaults to the full path to the
Verilator binary.
When make is run on the generated makefile these will be passed to the
C++ compiler (g++/clang++/msvc++).
This option was named `--bin` prior to version 4.228.
.. option:: --build-jobs [<value>]
Specify the level of parallelism for :vlopt:`--build`. If zero, uses the
number of threads in the current hardware. Otherwise, the <value> must
be a positive integer specifying the maximum number of parallel build
jobs.
See also :vlopt:`-j`.
.. option:: --cc
@@ -156,12 +164,19 @@ Summary:
have interest in adding more traditional CDC checks, please contact the
authors.
.. option:: -CFLAGS <flags>
Add specified C compiler argument to the generated makefiles. For
multiple flags either pass them as a single argument with space
separators quoted in the shell (:command:`-CFLAGS "-a -b"`), or use
multiple -CFLAGS options (:command:`-CFLAGS -a -CFLAGS -b`).
When make is run on the generated makefile these will be passed to the
C++ compiler (g++/clang++/msvc++).
.. option:: --clk <signal-name>
With :vlopt:`--clk`, the specified signal-name is taken as a root clock
into the model; Verilator will mark the signal as clocker and
propagate the clocker attribute automatically to other signals downstream in
that clock tree.
With :vlopt:`--clk`, the specified signal is marked as a clock signal.
The provided signal-name is specified using a RTL hierarchy path. For
example, v.foo.bar. If the signal is the input to top-module, then
@@ -173,11 +188,16 @@ Summary:
individual bits, Verilator will attempt to decompose the vector and
connect the single-bit clock signals.
The clocker attribute is useful in cases where Verilator does not
properly distinguish clock signals from other data signals. Using
clocker will cause the signal indicated to be considered a clock, and
remove it from the combinatorial logic reevaluation checking code. This
may greatly improve performance.
In versions prior to 5.000, the clocker attribute is useful in cases where
Verilator does not properly distinguish clock signals from other data
signals. Using clocker will cause the signal indicated to be considered a
clock, and remove it from the combinatorial logic reevaluation checking
code. This may greatly improve performance.
.. option:: --no-clk <signal-name>
Prevent the specified signal from being marked as clock. See
:vlopt:`--clk`.
.. option:: --compiler <compiler-name>
@@ -250,8 +270,8 @@ Summary:
generally is a less-optimized binary with symbols present (so GDB can be used on it).
* Enable debugging messages (equivalent to :vlopt:`--debugi 3 <--debugi>`).
* Enable internal assertions (equivalent to :vlopt:`--debug-check`).
* Enable intermediate form dump files (equivalent to :vlopt:`--dump-treei 3
<--dump-treei>`).
* Enable intermediate form dump files (equivalent to :vlopt:`--dumpi-tree 3
<--dumpi-tree>`).
* Leak to make node numbers unique (equivalent to :vlopt:`--debug-leak
<--no-debug-leak>`.
* Call abort() instead of exit() if there are any errors (so GDB can see
@@ -288,6 +308,13 @@ Summary:
<--debugi>`). Higher levels produce more detailed messages. See
:vlopt:`--debug` for other implications of enabling debug.
.. option:: --no-decoration
When creating output Verilated code, minimize comments, white space,
symbol names and other decorative items, at the cost of greatly reduced
readability. This may assist C++ compile times. This will not typically
change the ultimate model's performance, but may in some cases.
.. option:: --default-language <value>
Select the language to be used by default when first processing each
@@ -335,25 +362,31 @@ Summary:
touch foo.v ; verilator -E --dump-defines foo.v
.. option:: --dump-dfg
Rarely needed. Enable dumping DfgGraph .dot debug files with dumping
level 3.
.. option:: --dump-graph
Rarely needed. Enable dumping V3Graph .dot debug files with dumping
level 3. Before Verilator 4.228, :vlopt:`--dump-tree` used
to include this option.
.. option:: --dump-tree
Rarely needed. Enable writing .tree debug files with dumping level 3,
Rarely needed. Enable dumping Ast .tree debug files with dumping level 3,
which dumps the standard critical stages. For details on the format see
the Verilator Internals manual. :vlopt:`--dump-tree` is enabled
automatically with :vlopt:`--debug`, so :vlopt:`--debug --no-dump-tree
<--dump-tree>` may be useful if the dump files are large and not
desired.
.. option:: --dump-treei <level>
.. option:: --dump-tree-dot
.. option:: --dump-treei-<srcfile> <level>
Rarely needed - for developer use. Set internal tree dumping level
globally to a specific dumping level or set the specified Verilator
source file to the specified tree dumping level (e.g.
:vlopt:`--dump-treei-V3Order 9 <--dump-treei>`). Level 0 disables dumps
and is equivalent to :vlopt:`--no-dump-tree <--dump-tree>`. Level 9
enables dumping of every stage.
Rarely needed. Enable dumping Ast .tree.dot debug files in Graphviz
Dot format. This option implies :vlopt:`--dump-tree`, unless
:vlopt:`--dumpi-tree` was passed explicitly.
.. option:: --dump-tree-addrids
@@ -364,6 +397,33 @@ Summary:
by a newly allocated node after a node with the same address has been
dumped then freed.
.. option:: --dump-<srcfile>
Rarely needed - for developer use. Enable all dumping in the given
source file at level 3.
.. option:: --dumpi-dfg <level>
Rarely needed - for developer use. Set internal DfgGraph dumping level
globally to the specified value.
.. option:: --dumpi-graph <level>
Rarely needed - for developer use. Set internal V3Graph dumping level
globally to the specified value.
.. option:: --dumpi-tree <level>
Rarely needed - for developer use. Set internal Ast dumping level
globally to the specified value.
.. option:: --dumpi-<srcfile> <level>
Rarely needed - for developer use. Set the dumping level in the
specified Verilator source file to the specified value (e.g.
`--dumpi-V3Order 9`). Level 0 disables dumps and is equivalent to
`--no-dump-<srcfile>`. Level 9 enables dumping of everything.
.. option:: -E
Preprocess the source code, but do not compile, similar to C++
@@ -441,10 +501,37 @@ Summary:
.. option:: -fno-const
.. options: -fno-const-before-dfg
Do not apply any global expression folding prior to the DFG pass. This
option is solely for the purpose of DFG testing and should not be used
otherwise.
.. option:: -fno-const-bit-op-tree
.. option:: -fno-dedup
.. option:: -fno-dfg
Disable all use of the DFG based combinational logic optimizer.
Alias for :vlopt:`-fno-dfg-pre-inline` and :vlopt:`-fno-dfg-post-inline`.
.. option:: -fno-dfg-peephole
Disable the DFG peephole optimizer.
.. option:: -fno-dfg-peephole-<pattern>
Disable individual DFG peephole optimizer pattern.
.. option:: -fno-dfg-pre-inline
Do not apply the DFG optimizer before inlining.
.. option:: -fno-dfg-post-inline
Do not apply the DFG optimizer after inlining.
.. option:: -fno-expand
.. option:: -fno-gate
@@ -479,6 +566,30 @@ Summary:
are typically used only when recommended by a maintainer to help debug
or work around an issue.
.. option:: -future0 <option>
Rarely needed. Suppress an unknown Verilator option for an option that
takes no additional arguments. This is used to allow scripts written
with pragmas for a later version of Verilator to run under a older
version. e.g. :code:`-future0 option --option` would on older versions
that do not understand :code:`--option` or :code:`+option` suppress what
would otherwise be an invalid option error, and on newer versions that
implement :code:`--option`, :code:`-future0 option --option` would have
the :code:`-future0 option` ignored and the :code:`--option` would
function appropriately.
.. option:: -future1 <option>
Rarely needed. Suppress an unknown Verilator option for an option that
takes an additional argument. This is used to allow scripts written
with pragmas for a later version of Verilator to run under a older
version. e.g. :code:`-future1 option --option arg` would on older
versions that do not understand :code:`--option arg` or :code:`+option
arg` suppress what would otherwise be an invalid option error, and on
newer versions that implement :code:`--option arg`, :code:`-future1
option --option arg` would have the :code:`-future1 option` ignored and
the :code:`--option arg` would function appropriately.
.. option:: -G<name>=<value>
Overwrites the given parameter of the toplevel module. The value is
@@ -537,6 +648,15 @@ Summary:
a newline and exit immediately. This can be useful in makefiles. See
also :vlopt:`-V`, and the various :file:`*.mk` files.
.. option:: --get-supported <feature>
If the given feature is supported, print "1" and exit
immediately. Otherwise, print a newline and exit immediately. This can
be useful in makefiles. See also :vlopt:`-V`, and the various
:file:`*.mk` files.
Feature may be one of the following: COROUTINES, SYSTEMC.
.. option:: --help
Displays this message and program version and exits.
@@ -579,26 +699,10 @@ Summary:
.. option:: -j [<value>]
Specify the level of parallelism for :vlopt:`--build`. The <value> must
be a positive integer specifying the maximum number of parallel build
jobs, or can be omitted. When <value> is omitted, the build will not try
to limit the number of parallel build jobs but attempt to execute all
independent build steps in parallel.
.. option:: -LDFLAGS <flags>
Add specified C linker arguments to the generated makefiles. For multiple
flags either pass them as a single argument with space separators quoted
in the shell (``-LDFLAGS "-a -b"``), or use multiple -LDFLAGS arguments
(``-LDFLAGS -a -LDFLAGS -b``).
When make is run on the generated makefile these will be passed to the
C++ linker (ld) **after** the primary file being linked. This flag is
called :vlopt:`-LDFLAGS` as that's the traditional name in simulators;
it's would have been better called LDLIBS as that's the Makefile
variable it controls. (In Make, LDFLAGS is before the first object,
LDLIBS after. -L libraries need to be in the Make variable LDLIBS, not
LDFLAGS.)
Specify the level of parallelism for :vlopt:`--build` if
:vlopt:`--build-jobs` isn't provided. If zero, uses the number of threads
in the current hardware. Otherwise, the <value> must be a positive
integer specifying the maximum number of parallel build jobs.
.. option:: --l2-name <value>
@@ -616,12 +720,20 @@ Summary:
A synonym for :vlopt:`--default-language`, for compatibility with other
tools and earlier versions of Verilator.
.. option:: +libext+<ext>[+<ext>][...]
.. option:: -LDFLAGS <flags>
Specify the extensions that should be used for finding modules. If for
example module "my" is referenced, look in :file:`my.<ext>`. Note
"+libext+" is fairly standard across Verilog tools. Defaults to
".v+.sv".
Add specified C linker arguments to the generated makefiles. For multiple
flags either pass them as a single argument with space separators quoted
in the shell (``-LDFLAGS "-a -b"``), or use multiple -LDFLAGS arguments
(``-LDFLAGS -a -LDFLAGS -b``).
When make is run on the generated makefile these will be passed to the
C++ linker (ld) **after** the primary file being linked. This flag is
called :vlopt:`-LDFLAGS` as that's the traditional name in simulators;
it's would have been better called LDLIBS as that's the Makefile
variable it controls. (In Make, LDFLAGS is before the first object,
LDLIBS after. -L libraries need to be in the Make variable LDLIBS, not
LDFLAGS.)
.. option:: --lib-create <name>
@@ -638,8 +750,17 @@ Summary:
model has a time resolution that is always compatible with the time
precision of the upper instantiating module.
Designs compiled using this option cannot use :vlopt:`--timing` with delays.
See also :vlopt:`--protect-lib`.
.. option:: +libext+<ext>[+<ext>][...]
Specify the extensions that should be used for finding modules. If for
example module "my" is referenced, look in :file:`my.<ext>`. Note
"+libext+" is fairly standard across Verilog tools. Defaults to
".v+.sv".
.. option:: --lint-only
Check the files for lint violations only, do not create any other
@@ -673,22 +794,29 @@ Summary:
(e.g. ``-MAKEFLAGS -l -MAKEFLAGS -k``). Use of this option should not be
required for simple builds using the host toolchain.
.. option:: --main
Generates a top-level C++ main() file that supports parsing arguments,
but does not drive any inputs. This is sufficient to use for top-level
SystemVerilog designs that has no inputs.
This option can also be used once to generate a main .cpp file as a
starting point for editing. Copy it outside the obj directory, manually
edit, and then pass the filename on later Verilator command line
invocations.
Typically used with :vlopt:`--timing` to support delay-generated clocks,
and :vlopt:`--build`.
Implies :vlopt:`--cc` if no other output mode was provided.
See also :vlopt:`--binary`.
.. option:: --max-num-width <value>
Set the maximum number literal width (e.g. in 1024'd22 this it the
1024). Defaults to 64K.
.. option:: --MMD =item --no-MMD
Enable/disable creation of .d dependency files, used for make dependency
detection, similar to gcc -MMD option. By default this option is
enabled for :vlopt:`--cc` or :vlopt:`--sc` modes.
.. option:: --MP
When creating .d dependency files with :vlopt:`--MMD` option, make phony
targets. Similar to :command:`gcc -MP` option.
.. option:: --Mdir <directory>
Specifies the name of the Make object directory. All generated files
@@ -696,33 +824,23 @@ Summary:
The directory is created if it does not exist and the parent directories
exist; otherwise manually create the Mdir before calling Verilator.
.. option:: --MMD
.. option:: --no-MMD
Enable/disable creation of .d dependency files, used for make dependency
detection, similar to gcc -MMD option. By default this option is
enabled for :vlopt:`--cc` or :vlopt:`--sc` modes.
.. option:: --mod-prefix <topname>
Specifies the name to prepend to all lower level classes. Defaults to
the same as :vlopt:`--prefix`.
.. option:: --no-clk <signal-name>
.. option:: --MP
Prevent the specified signal from being marked as clock. See
:vlopt:`--clk`.
.. option:: --no-decoration
When creating output Verilated code, minimize comments, white space,
symbol names and other decorative items, at the cost of greatly reduced
readability. This may assist C++ compile times. This will not typically
change the ultimate model's performance, but may in some cases.
.. option:: --no-pins64
Backward compatible alias for :vlopt:`--pins-bv 33 <--pins-bv>`.
.. option:: --no-skip-identical =item --skip-identical
Rarely needed. Disables or enables skipping execution of Verilator if
all source files are identical, and all output files exist with newer
dates. By default this option is enabled for :vlopt:`--cc` or
:vlopt:`--sc` modes only.
When creating .d dependency files with :vlopt:`--MMD` option, make phony
targets. Similar to :command:`gcc -MP` option.
.. option:: +notimingchecks
@@ -755,6 +873,10 @@ Summary:
.. option:: --no-order-clock-delay
Deprecated and has no effect (ignored).
In versions prior to 5.000:
Rarely needed. Disables a bug fix for ordering of clock enables with
delayed assignments. This option should only be used when suggested by
the developers.
@@ -801,11 +923,6 @@ Summary:
With :vlopt:`-E`, disable generation of :code:`&96;line` markers and
blank lines, similar to :command:`gcc -P`.
.. option:: --pins64
Backward compatible alias for :vlopt:`--pins-bv 65 <--pins-bv>`. Note
that's a 65, not a 64.
.. option:: --pins-bv <width>
Specifies SystemC inputs/outputs of greater than or equal to <width>
@@ -838,6 +955,15 @@ Summary:
of uint32_t. Likewise pins of width 9-16 will use uint16_t instead of
uint32_t.
.. option:: --pins64
Backward compatible alias for :vlopt:`--pins-bv 65 <--pins-bv>`. Note
that's a 65, not a 64.
.. option:: --no-pins64
Backward compatible alias for :vlopt:`--pins-bv 33 <--pins-bv>`.
.. option:: --pipe-filter <command>
Rarely needed. Verilator will spawn the specified command as a
@@ -867,6 +993,11 @@ Summary:
prepended to the name of the :vlopt:`--top` option, or V prepended to
the first Verilog filename passed on the command line.
.. option:: --private
Opposite of :vlopt:`--public`. Is the default; this option exists for
backwards compatibility.
.. option:: --prof-c
When compiling the C++ code, enable the compiler's profiling flag
@@ -900,23 +1031,6 @@ Summary:
Deprecated. Same as --prof-exec and --prof-pgo together.
.. option:: --protect-key <key>
Specifies the private key for :vlopt:`--protect-ids`. For best security
this key should be 16 or more random bytes, a reasonable secure choice
is the output of :command:`verilator --generate-key` . Typically, a key
would be created by the user once for a given protected design library,
then every Verilator run for subsequent versions of that library would
be passed the same :vlopt:`--protect-key`. Thus, if the input Verilog is
similar between library versions (Verilator runs), the Verilated code
will likewise be mostly similar.
If :vlopt:`--protect-key` is not specified and a key is needed,
Verilator will generate a new key for every Verilator run. As the key is
not saved, this is best for security, but means every Verilator run will
give vastly different output even for identical input, perhaps harming
compile times (and certainly thrashing any "ccache").
.. option:: --protect-ids
Hash any private identifiers (variable, module, and assertion block
@@ -937,6 +1051,23 @@ Summary:
prototypes. Use of the VPI is not recommended as many design details
may be exposed, and an INSECURE warning will be issued.
.. option:: --protect-key <key>
Specifies the private key for :vlopt:`--protect-ids`. For best security
this key should be 16 or more random bytes, a reasonable secure choice
is the output of :command:`verilator --generate-key` . Typically, a key
would be created by the user once for a given protected design library,
then every Verilator run for subsequent versions of that library would
be passed the same :vlopt:`--protect-key`. Thus, if the input Verilog is
similar between library versions (Verilator runs), the Verilated code
will likewise be mostly similar.
If :vlopt:`--protect-key` is not specified and a key is needed,
Verilator will generate a new key for every Verilator run. As the key is
not saved, this is best for security, but means every Verilator run will
give vastly different output even for identical input, perhaps harming
compile times (and certainly thrashing any "ccache").
.. option:: --protect-lib <name>
Produces a DPI library similar to :vlopt:`--lib-create`, but hides
@@ -948,10 +1079,7 @@ Summary:
in the distribution for a demonstration of how to build and use the DPI
library.
.. option:: --private
Opposite of :vlopt:`--public`. Is the default; this option exists for
backwards compatibility.
Designs compiled using this option cannot use :vlopt:`--timing` with delays.
.. option:: --public
@@ -1045,6 +1173,15 @@ Summary:
Specifies SystemC output mode; see also :vlopt:`--cc` option.
.. option:: --skip-identical
.. option:: --no-skip-identical
Rarely needed. Disables or enables skipping execution of Verilator if
all source files are identical, and all output files exist with newer
dates. By default this option is enabled for :vlopt:`--cc` or
:vlopt:`--sc` modes only.
.. option:: --stats
Creates a dump file with statistics on the design in
@@ -1135,6 +1272,16 @@ Summary:
module. As "1fs" is the finest time precision it may be desirable to
always use a precision of "1fs".
.. option:: --timing
.. option:: --no-timing
Enables/disables support for timing constructs such as delays, event
controls (unless it's at the top of a process), wait statements, and joins.
When disabled, timing control constructs are ignored the same way as they
were in earlier versions of Verilator. Enabling this feature requires a C++
compiler with coroutine support (GCC 10, Clang 5, or newer).
.. option:: --top <topname>
.. option:: --top-module <topname>
@@ -1298,8 +1445,7 @@ Summary:
Enable all code style warnings, including code style warnings that are
normally disabled by default. Equivalent to :vlopt:`-Wwarn-lint`
:vlopt:`-Wwarn-style`. Excludes some specialty warnings,
i.e. IMPERFECTSCH.
:vlopt:`-Wwarn-style`. Excludes some specialty warnings.
.. option:: -Werror-<message>
@@ -1342,7 +1488,8 @@ Summary:
equivalent to ``-Wno-ALWCOMBORDER -Wno-BSSPACE -Wno-CASEINCOMPLETE
-Wno-CASEOVERLAP -Wno-CASEX -Wno-CASTCONST -Wno-CASEWITHX -Wno-CMPCONST -Wno-COLONPLUS
-Wno-ENDLABEL -Wno-IMPLICIT -Wno-LITENDIAN -Wno-PINCONNECTEMPTY
-Wno-PINMISSING -Wno-SYNCASYNCNET -Wno-UNDRIVEN -Wno-UNSIGNED -Wno-UNUSED
-Wno-PINMISSING -Wno-SYNCASYNCNET -Wno-UNDRIVEN -Wno-UNSIGNED
-Wno-UNUSEDGENVAR -Wno-UNUSEDPARAM -Wno-UNUSEDSIGNAL
-Wno-WIDTH`` plus the list shown for Wno-style.
It is strongly recommended you cleanup your code rather than using this
@@ -1354,7 +1501,8 @@ Summary:
Disable all code style related warning messages (note by default they are
already disabled). This is equivalent to ``-Wno-DECLFILENAME -Wno-DEFPARAM
-Wno-EOFNEWLINE -Wno-IMPORTSTAR -Wno-INCABSPATH -Wno-PINCONNECTEMPTY
-Wno-PINNOCONNECT -Wno-SYNCASYNCNET -Wno-UNDRIVEN -Wno-UNUSED
-Wno-PINNOCONNECT -Wno-SYNCASYNCNET -Wno-UNDRIVEN
-Wno-UNUSEDGENVAR -Wno-UNUSEDPARAM -Wno-UNUSEDSIGNAL
-Wno-VARHIDDEN``.
.. option:: -Wpedantic
@@ -1383,7 +1531,7 @@ Summary:
Enable all code style related warning messages. This is equivalent to
``-Wwarn ASSIGNDLY -Wwarn-DECLFILENAME -Wwarn-DEFPARAM -Wwarn-EOFNEWLINE
-Wwarn-INCABSPATH -Wwarn-PINNOCONNECT -Wwarn-SYNCASYNCNET -Wwarn-UNDRIVEN
-Wwarn-UNUSED -Wwarn-VARHIDDEN``.
-Wwarn-UNUSEDGENVAR -Wwarn-UNUSEDPARAM -Wwarn-UNUSEDSIGNAL -Wwarn-VARHIDDEN``.
.. option:: --x-assign 0
@@ -1552,6 +1700,10 @@ The grammar of configuration commands is as follows:
.. option:: clock_enable -module "<modulename>" -var "<signame>"
Deprecated and has no effect (ignored).
In versions prior to 5.000:
Indicate the signal is used to gate a clock, and the user takes
responsibility for insuring there are no races related to it.
@@ -1715,6 +1867,19 @@ The grammar of configuration commands is as follows:
Same as :option:`/*verilator&32;split_var*/` metacomment.
.. option:: timing_on [-file "<filename>" [-lines <line> [ - <line>]]]
.. option:: timing_off [-file "<filename>" [-lines <line> [ - <line>]]]
Enables/disables timing constructs for the specified file and lines.
When disabled, all timing control constructs in the specified source
code locations are ignored the same way as with the
:option:`--no-timing`, and code:`fork`/:code:`join*` blocks are
converted into :code:`begin`/:code:`end` blocks.
Same as :option:`/*verilator&32;timing_on*/`,
:option:`/*verilator&32;timing_off*/` metacomments.
.. option:: tracing_on [-file "<filename>" [-lines <line> [ - <line> ]]]
.. option:: tracing_off [-file "<filename>" [-lines <line> [ - <line> ]]]
+29 -6
View File
@@ -151,6 +151,13 @@ or "`ifdef`"'s may break other tools.
Take remaining text up to the next :option:`\`verilog` mode switch and
treat it as Verilator configuration commands. See :ref:`Configuration Files`.
.. option:: `VERILATOR_TIMING
The VERILATOR_TIMING define is set when :vlopt:`--timing` is used to
allow an "\`ifdef" of code dependent on this feature. Note this define
is not affected by the :option:`timing_off` configuration file option
nor timing metacomments.
.. option:: `verilog
Switch back to processing Verilog code after a
@@ -160,6 +167,10 @@ or "`ifdef`"'s may break other tools.
.. option:: /*verilator&32;clock_enable*/
Deprecated and has no effect (ignored).
In versions prior to 5.000:
Used after a signal declaration to indicate the signal is used to gate a
clock, and the user takes responsibility for insuring there are no races
related to it. (Typically by adding a latch, and running static timing
@@ -174,9 +185,7 @@ or "`ifdef`"'s may break other tools.
The clock_enable attribute will cause the clock gate to be ignored in
the scheduling algorithm, sometimes required for correct clock behavior,
and always improving performance. It's also a good idea to enable the
:option:`IMPERFECTSCH` warning, to ensure all clock enables are properly
recognized.
and always improving performance.
Same as :option:`clock_enable` configuration file option.
@@ -184,9 +193,7 @@ or "`ifdef`"'s may break other tools.
.. option:: /*verilator&32;no_clocker*/
Specifies that the signal is used as clock or not. This information is
used by Verilator to mark the signal and any derived signals as
clocker. See :vlopt:`--clk`.
Specifies that the signal is used as clock or not. See :vlopt:`--clk`.
Same as :option:`clocker` and :option:`no_clocker` in configuration
files.
@@ -503,6 +510,22 @@ or "`ifdef`"'s may break other tools.
Verilator) text that should be passed through to the XML output as a tag,
for use by downstream applications.
.. option:: /*verilator&32;timing_off*/
Ignore all timing constructs after this metacomment. All timing controls
behave as if they were not there (the same way as with
:option:`--no-timing`), and :code:`fork`/:code:`join*` blocks are
converted into :code:`begin`/:code:`end` blocks.
Same as :option:`timing_off` configuration file option.
.. option:: /*verilator&32;timing_on*/
Re-enable all timing constructs after this metacomment (only applicable
after :option:`timing_off`).
Same as :option:`timing_on` configuration file option.
.. option:: /*verilator&32;trace_init_task*/
Attached to a DPI import to indicate that function should be called when
+12 -6
View File
@@ -50,7 +50,7 @@ For --cc/--sc, it creates:
- Make include file with class names (from --make gmake)
* - *{prefix}*\ _hier.mk
- Make file for hierarchy blocks (from --make gmake)
* - *{prefix|*\ _hierMkArgs.f
* - *{prefix}*\ _hierMkArgs.f
- Arguments for hierarchical Verilation (from --make gmake)
* - *{prefix}*\ _hierCMakeArgs.f
- Arguments for hierarchical Verilation (from --make cmake)
@@ -62,13 +62,17 @@ For --cc/--sc, it creates:
- Top level (SystemVerilog $root) internal header file
* - *{prefix}*\ ___024root.cpp
- Top level (SystemVerilog $root) internal C++ file
* - *{prefix}*___024root*{__n}*\ .cpp
- Additional top level internal C++ files (from --output-split)
* - *{prefix}*\ ___024root\ *{__n}*\ .cpp
- Additional top level internal C++ files
* - *{prefix}*\ ___024root\ *{__DepSet_hash__n}*\ .cpp
- Additional top level internal C++ files (hashed to reduce build times)
* - *{prefix}*\ ___024root__Slow\ *{__n}*\ .cpp
- Infrequent cold routines
* - *{prefix}*\ ___024root__Trace{__n}*\ .cpp
* - *{prefix}*\ ___024root\ *{__DepSet_hash__n}*\ .cpp
- Infrequent cold routines (hashed to reduce build times)
* - *{prefix}*\ ___024root__Trace\ *{__n}*\ .cpp
- Wave file generation code (from --trace)
* - *{prefix}*\ ___024root__Trace__Slow{__n}*\ .cpp
* - *{prefix}*\ ___024root__Trace__Slow\ *{__n}*\ .cpp
- Wave file generation code (from --trace)
* - *{prefix}*\ __Dpi.h
- DPI import and export declarations (from --dpi)
@@ -87,7 +91,9 @@ For --cc/--sc, it creates:
* - *{prefix}{each_verilog_module}*\ .cpp
- Lower level internal C++ files
* - *{prefix}{each_verilog_module}{__n}*\ .cpp
- Additional lower C++ files (from --output-split)
- Additional lower C++ files
* - *{prefix}{each_verilog_module}{__DepSet_hash__n}*\ .cpp
- Additional lower C++ files (hashed to reduce build times)
For --hierarchy mode, it creates:
+1 -1
View File
@@ -124,7 +124,7 @@ Those developing Verilator itself may also want these (see internals.rst):
::
sudo apt-get install gdb graphviz cmake clang clang-format-11 gprof lcov
sudo apt-get install gdb graphviz cmake clang clang-format-14 gprof lcov
sudo apt-get install yapf3
sudo pip3 install sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe
cpan install Pod::Perldoc
+67 -18
View File
@@ -95,6 +95,72 @@ keywords on case statement, as well as "unique" on if statements. However,
"priority if" is currently ignored.
Time
====
With :vlopt:`--timing`, all timing controls are supported:
* delay statements,
* event control statements not only at the top of a process,
* intra-assignment timing controls,
* net delays,
* :code:`wait` statements,
as well as all flavors of :code:`fork`.
Compiling a verilated design that makes use of these features requires a
compiler with C++20 coroutine support, e.g. Clang 5, GCC 10, or newer.
:code:`#0` delays cause Verilator to issue the :option:`ZERODLY` warning, as
they work differently than described in the LRM. They do not schedule process
resumption in the Inactive region, though the process will get resumed in the
same time slot.
Rising/falling/turn-off delays are currently unsupported and cause the
:option:`RISEFALLDLY` warning.
Minimum/typical/maximum delays are currently unsupported. The typical delay is
always the one chosen. Such expressions cause the :option:`MINTYPMAX` warning.
Another consequence of using :vlopt:`--timing` is that the :vlopt:`--main`
option generates a main file with a proper timing eval loop, eliminating the
need for writing any driving C++ code. You can then simply compile the
simulation (perhaps using :vlopt:`--build`) and run it.
With :vlopt:`--no-timing`, all timing controls cause the :option:`NOTIMING`
error, with the exception of:
* delay statements – they are ignored (as they are in synthesis), though they
do issue a :option:`STMTDLY` warning,
* intra-assignment timing controls – they are ignored, though they do issue an
:option:`ASSIGNDLY` warning,
* net delays – they are ignored,
* event controls at the top of the procedure,
Forks cause this error as well, with the exception of:
* forks with no statements,
* :code:`fork..join` or :code:`fork..join_any` with one statement,
* forks with :vlopt:`--bbox-unsup`.
If neither :vlopt:`--timing` nor :vlopt:`--no-timing` is specified, all timing
controls cause the :option:`NEEDTIMINGOPT` error, with the exception of event
controls at the top of the process. Forks cause this error as well, with the
exception of:
* forks with no statements,
* :code:`fork..join` or :code:`fork..join_any` with one statement,
* forks with :vlopt:`--bbox-unsup`.
Timing controls and forks can also be ignored in specific files or parts of
files. The :option:`/*verilator&32;timing_off*/` and
:option:`/*verilator&32;timing_off*/` metacomments will make Verilator ignore
the encompassed timing controls and forks, regardless of the chosen
:vlopt:`--timing` or :vlopt:`--no-timing` option. This can also be achieved
using the :option:`timing_off` and :option:`timing_off` options in Verilator
configuration files.
.. _Language Limitations:
Language Limitations
@@ -180,12 +246,6 @@ structure from blocking, and another from non-blocking assignments is
unsupported.
Time
----
All delays (#) are ignored, as they are in synthesis.
.. _Unknown States:
Unknown States
@@ -276,17 +336,6 @@ probably expect, what C does. The default behavior of Verilog is
different.)
Generated Clocks
----------------
Verilator attempts to deal with generated and gated clocks correctly,
however some cases cause problems in the scheduling algorithm which is
optimized for performance. The safest option is to have all clocks as
primary inputs to the model, or wires directly attached to primary inputs.
For proper behavior clock enables may also need the
:option:`/*verilator&32;clock_enable*/` metacomment.
Gate Primitives
---------------
@@ -489,7 +538,7 @@ $test$plusargs, $value$plusargs
.. code-block:: C++
Verilated::commandArgs(argc, argv);
{VerilatedContext*} ->commandArgs(argc, argv);
to register the command line before calling $test$plusargs or
$value$plusargs.
+3 -3
View File
@@ -7,9 +7,9 @@
Simulating (Verilated-Model Runtime)
************************************
This section describes items related to simulating, that using a Verilated
model's executable. For the runtime arguments to a simulated model, see
:ref:`Simulation Runtime Arguments`.
This section describes items related to simulating, that is, the use of a
Verilated model's executable. For the runtime arguments to a simulated
model, see :ref:`Simulation Runtime Arguments`.
.. _Benchmarking & Optimization:
+2
View File
@@ -113,6 +113,8 @@ Hierarchy blocks have some limitations including:
hierarchical model and pass up into another hierarchical model or the top
module.
* Delays are not allowed in hierarchy blocks.
But, the following usage is supported:
* Nested hierarchy blocks. A hierarchy block may instantiate other
+121 -59
View File
@@ -5,6 +5,7 @@
Errors and Warnings
*******************
.. _Disabling Warnings:
Disabling Warnings
==================
@@ -111,6 +112,8 @@ List Of Warnings
simulators, however at one point this was a common style so disabled by
default as a code style warning.
This warning is issued only if Verilator is run with :vlopt:`--no-timing`.
.. option:: ASSIGNIN
@@ -315,16 +318,16 @@ List Of Warnings
.. option:: CLKDATA
.. TODO better example
Historical, never issued since version 5.000.
Warns that clock signal is mixed used with/as data signal. The checking
for this warning is enabled only if user has explicitly marked some
signal as clocker using command line option or in-source meta comment
(see :vlopt:`--clk`).
Warned that clock signal was mixed used with/as data signal. The
checking for this warning was enabled only if user has explicitly marked
some signal as clocker using command line option or in-source meta
comment (see :vlopt:`--clk`).
The warning can be disabled without affecting the simulation result. But
it is recommended to check the warning as this may degrade the
performance of the Verilated model.
The warning could be disabled without affecting the simulation
result. But it was recommended to check the warning as it may have
degraded the performance of the Verilated model.
.. option:: CMPCONST
@@ -462,15 +465,11 @@ List Of Warnings
.. option:: DETECTARRAY
.. TODO better example
Historical, never issued since version 3.862.
Error when Verilator tries to deal with a combinatorial loop that could
not be flattened, and which involves a datatype which Verilator cannot
handle, such as an unpacked struct or a large unpacked array. This
typically occurs when :vlopt:`-Wno-UNOPTFLAT <UNOPTFLAT>` has been used
to override an UNOPTFLAT warning (see below).
The solution is to break the loop, as described for UNOPTFLAT.
Was an error when Verilator tried to deal with a combinatorial loop that
could not be flattened, and which involves a datatype which Verilator
could not handle, such as an unpacked struct or a large unpacked array.
.. option:: DIDNOTCONVERGE
@@ -492,7 +491,7 @@ List Of Warnings
passing. Thus to prevent an infinite loop, the Verilated executable
gives the DIDNOTCONVERGE error.
To debug this, first review any UNOPT or UNOPTFLAT warnings that were
To debug this, first review any UNOPTFLAT warnings that were
ignored. Though typically it is safe to ignore UNOPTFLAT (at a
performance cost), at the time of issuing a UNOPTFLAT Verilator did not
know if the logic would eventually converge and assumed it would.
@@ -516,9 +515,10 @@ List Of Warnings
constructs (e.g. always_ff and always_latch).
Another way DIDNOTCONVERGE may occur is if # delays are used to generate
clocks. Verilator ignores the delays and gives an :option:`ASSIGNDLY`
or :option:`STMTDLY` warning. If these were suppressed, due to the
absence of the delay, the code may now oscillate.
clocks if Verilator is run with :vlopt:`--no-timing`. In this mode,
Verilator ignores the delays and gives an :option:`ASSIGNDLY` or
:option:`STMTDLY` warning. If these were suppressed, due to the absence of
the delay, the code may now oscillate.
Finally, rare, more difficult cases can be debugged like a C++ program;
either enter :command:`gdb` and use its tracing facilities, or edit the
@@ -589,9 +589,10 @@ List Of Warnings
.. option:: GENCLK
Deprecated and no longer used as a warning. Used to indicate that the
specified signal was is generated inside the model, and also being used
as a clock.
Historical, never issued since version 5.000.
Indicated that the specified signal was generated inside the model, and
was also being used as a clock.
.. option:: HIERBLOCK
@@ -653,12 +654,12 @@ List Of Warnings
.. option:: IMPERFECTSCH
.. TODO better example
Historical, never issued since version 5.000.
Warns that the scheduling of the model is not absolutely perfect, and
Warned that the scheduling of the model is not absolutely perfect, and
some manual code edits may result in faster performance. This warning
defaults to off, is not part of -Wall, and must be turned on explicitly
before the top module statement is processed.
defaulted to off, was not part of -Wall, and had to be turned on
explicitly before the top module statement is processed.
.. option:: IMPLICIT
@@ -730,9 +731,9 @@ List Of Warnings
Warns that a while or for statement has a condition that is always true.
and thus results in an infinite loop if the statement ever executes.
This might be unintended behavior if the loop body contains statements
that in other simulators would make time pass, which Verilator is
ignoring due to e.g. ``STMTDLY`` warnings being disabled.
This might be unintended behavior if Verilator is run with
:vlopt:`--no-timing` and the loop body contains statements that would make
time pass otherwise.
Ignoring this warning will only suppress the lint check, it will
simulate correctly (i.e. hang due to the infinite loop).
@@ -793,6 +794,16 @@ List Of Warnings
simulate correctly.
.. option:: MINTYPMAX
.. code-block:: sv
#(3:5:8) clk = ~clk;
Warns that minimum, typical, and maximum delay expressions are currently
unsupported. Only the typical delay value is used by Verilator.
.. option:: MODDUP
.. TODO better example
@@ -863,6 +874,13 @@ List Of Warnings
input.
.. option:: NEEDTIMINGOPT
Error when a timing-related construct, such as an event control or delay,
has been encountered, without specifying how Verilator should handle it
(neither :vlopt:`--timing` nor :vlopt:`--no-timing` option was provided).
.. option:: NOLATCH
.. TODO better example
@@ -875,6 +893,13 @@ List Of Warnings
simulate correctly.
.. option:: NOTIMING
Error when a timing-related construct that requires :vlopt:`--timing` has
been encountered. Issued only if Verilator is run with the
:vlopt:`--no-timing` option.
.. option:: NULLPORT
Warns that a null port was detected in the module definition port
@@ -1112,6 +1137,16 @@ List Of Warnings
"Duplicate macro arguments with name".
.. option:: RISEFALLDLY
.. code-block:: sv
and #(1,2,3) AND (out, a, b);
Warns that rising, falling, and turn-off delays are currently unsupported.
The first (rising) delay is used for all cases.
.. option:: SELRANGE
Warns that a selection index will go out of bounds.
@@ -1211,11 +1246,11 @@ List Of Warnings
.. include:: ../../docs/gen/ex_STMTDLY_msg.rst
This is a warning because Verilator does not support delayed statements.
It will ignore all such delays. In many cases ignoring a delay might be
harmless, but if the delayed statement is, as in this example, used to
cause some important action at a later time, it might be an important
difference.
This warning is issued only if Verilator is run with :vlopt:`--no-timing`.
All delays on statements are ignored in this mode. In many cases ignoring a
delay might be harmless, but if the delayed statement is, as in this
example, used to cause some important action at a later time, it might be an
important difference.
Some possible workarounds:
@@ -1225,6 +1260,8 @@ List Of Warnings
* Convert the statement into a FSM, or other statement that tests
against $time.
* Run Verilator with :vlopt:`--timing`.
.. option:: SYMRSVDWORD
@@ -1355,23 +1392,12 @@ List Of Warnings
.. option:: UNOPT
.. TODO better example
Historical, never issued since version 5.000.
Warns that due to some construct, optimization of the specified signal
or block is disabled. The construct should be cleaned up to improve
simulation performance.
Warned that due to some construct, optimization of the specified signal
or block was disabled.
A less obvious case of this is when a module instantiates two
submodules. Inside submodule A, signal I is input and signal O is
output. Likewise in submodule B, signal O is an input and I is an
output. A loop exists and a UNOPT warning will result if AI & AO both
come from and go to combinatorial blocks in both submodules, even if
they are unrelated always blocks. This affects performance because
Verilator would have to evaluate each submodule multiple times to
stabilize the signals crossing between the modules.
Ignoring this warning will only slow simulations, it will simulate
correctly.
Ignoring this warning only slowed simulations, it simulated correctly.
.. option:: UNOPTFLAT
@@ -1385,10 +1411,6 @@ List Of Warnings
performance; two times better performance may be possible by fixing
these warnings.
Unlike the ``UNOPT`` warning, this occurs after flattening the netlist,
and indicates a more basic problem, as the less obvious case described
under ``UNOPT`` does not apply.
Often UNOPTFLAT is caused by logic that isn't truly circular as viewed by
synthesis which analyzes interconnection per-bit, but is circular to
simulation which analyzes per-bus.
@@ -1441,15 +1463,15 @@ List Of Warnings
the conflict. If you run with `--report-unoptflat` Verilator will
suggest possible candidates for :option:`/*verilator&32;split_var*/`.
The UNOPTFLAT warning may also be due to clock enables, identified from
the reported path going through a clock gating instance. To fix these,
use the clock_enable meta comment described above.
The UNOPTFLAT warning may also occur where outputs from a block of logic
are independent, but occur in the same always block. To fix this, use
the :option:`/*verilator&32;isolate_assignments*/` metacomment described
above.
Prior to version 5.000, the UNOPTFLAT warning may also have been due to
clock enables, identified from the reported path going through a clock
gating instance. To fix these, the clock_enable meta comment was used.
To assist in resolving UNOPTFLAT, the option :vlopt:`--report-unoptflat`
can be used, which will provide suggestions for variables that can be
split up, and a graph of all the nodes connected in the loop. See the
@@ -1520,9 +1542,31 @@ List Of Warnings
.. option:: UNUSED
Disabling/enabling UNUSED is equivalent to disabling/enabling the
`UNUSEDGENVAR`, `UNUSEDPARAM` and `UNUSEDSIGNAL` warnings.
Never issued since version 5.000. Historically warned that a variable,
parameter, or signal was unused.
.. option:: UNUSEDGENVAR
.. TODO better example
Warns that the specified signal or parameter is never used/consumed.
Warns that the specified genvar is never used/consumed.
.. option:: UNUSEDPARAM
.. TODO better example
Warns that the specified parameter is never used/consumed.
.. option:: UNUSEDSIGNAL
.. TODO better example
Warns that the specified signal is never used/consumed.
Verilator is fairly liberal in the usage calculations; making a signal
public, a signal matching the :vlopt:`--unused-regexp` option (default
"\*unused\*" or accessing only a single array element marks the entire
@@ -1635,6 +1679,16 @@ List Of Warnings
To resolve, rename the variable to a unique name.
.. option:: WAITCONST
.. code-block:: sv
wait(0); // Blocks forever
Warns that a `wait` statement awaits a constant condition, which means it
either blocks forever or never blocks.
.. option:: WIDTH
Warns that based on width rules of Verilog:
@@ -1702,3 +1756,11 @@ List Of Warnings
The correct fix is to either size the 1 (:code:`32'h1`), or add the
width to the parameter definition (:code:`parameter [31:0]`), or add the
width to the parameter usage (:code:`{PAR[31:0], PAR[31:0]}`).
.. option:: ZERODLY
Warns that `#0` delays do not schedule the process to be resumed in the
Inactive region. Such processes do get resumed in the same time slot
somewhere in the Active region. Issued only if Verilator is run with the
:vlopt:`--timing` option.
+672 -13
View File
@@ -96,10 +96,11 @@ this.
Each ``AstNode`` has pointers to up to four children, accessed by the
``op1p`` through ``op4p`` methods. These methods are then abstracted in a
specific Ast\* node class to a more specific name. For example with the
``AstIf`` node (for ``if`` statements), ``ifsp`` calls ``op2p`` to give the
``AstIf`` node (for ``if`` statements), ``thensp`` calls ``op2p`` to give the
pointer to the AST for the "then" block, while ``elsesp`` calls ``op3p`` to
give the pointer to the AST for the "else" block, or NULL if there is not
one.
one. These accessors are automatically generated by ``astgen`` after
parsing the ``@astgen`` directives in the specific ``AstNode`` subclasses.
``AstNode`` has the concept of a next and previous AST - for example the
next and previous statements in a block. Pointers to the AST for these
@@ -183,6 +184,568 @@ A number of predefined derived algorithm classes and access methods are
provided and documented in ``V3GraphAlg.cpp``.
``DfgGraph``
^^^^^^^^^^^^^
The data-flow graph based combinational logic optimizer (DFG optimizer)
converts an ``AstModule`` into a ``DfgGraph``. The graph represents the
combinational equations (~continuous assignments) in the module, and for the
duration of the DFG passes, it takes over the role of the represented
``AstModule``. The ``DfgGraph`` keeps holds of the represented ``AstModule``,
and the ``AstModule`` retains all other logic that is not representable as a
data-flow graph. At the end of optimization, the combinational logic
represented by the ``DfgGraph`` is converted back into AST form and is
re-inserted into the corresponding ``AstModule``. The ``DfgGraph`` is distinct
from ``V3Graph`` for efficiency and other desirable properties which make
writing DFG passes easier.
``DfgVertex``
^^^^^^^^^^^^^
The ``DfgGraph`` represents combinational logic equations as a graph of
``DfgVertex`` vertices. Each sub-class of ``DfgVertex`` corresponds to an
expression (a sub-class of ``AstNodeMath``), a constanat, or a variable
reference. LValues and RValues referencing the same storage location are
represented by the same ``DfgVertex``. Consumers of such vertices read as the
LValue, writers of such vertices write the RValue. The bulk of the final
``DfgVertex`` sub-classes are generated by ``astgen`` from the corresponding
``AstNode`` definitions.
Scheduling
----------
Verilator implements the Active and NBA regions of the SystemVerilog scheduling
model as described in IEEE 1800-2017 chapter 4, and in particular sections
4.5 and Figure 4.1. The static (verilation time) scheduling of SystemVerilog
processes is performed by code in the ``V3Sched`` namespace. The single
entry-point to the scheduling algorithm is ``V3Sched::schedule``. Some
preparatory transformations important for scheduling are also performed in
``V3Active`` and ``V3ActiveTop``. High level evaluation functions are
constructed by ``V3Order``, which ``V3Sched`` invokes on subsets of the logic
in the design.
Scheduling deals with the problem of evaluating 'logic' in the correct order
and the correct number of times in order to compute the correct state of the
SystemVerilog program. Throughout this section, we use the term 'logic' to
refer to all SystemVerilog constructs that describe the evolution of the state
of the program. In particular, all SystemVerilog processes and continuous
assignments are considered 'logic', but not for example variable definitions
without initialization or other miscellaneous constructs.
Classes of logic
^^^^^^^^^^^^^^^^
The first step in the scheduling algorithm is to gather all the logic present
in the design, and classify it based on the conditions under which the logic
needs to be evaluated.
The classes of logic we distinguish between are:
- SystemVerilog ``initial`` processes, that need to be executed once at
startup.
- Static variable initializers. These are a separate class as they need to be
executed before ``initial`` processes.
- SystemVerilog ``final`` processes.
- Combinational logic. Any process or construct that has an implicit
sensitivity list with no explicit sensitivities is considered 'combinational'
logic. This includes among other things, ``always @*`` and ``always_comb``
processes, and continuous assignments. Verilator also converts some other
``always`` processes to combinational logic in ``V3Active`` as described
below.
- Clocked logic. Any process or construct that has an explicit sensitivity
list, with no implicit sensitivities is considered 'clocked' (or
'sequential') logic. This includes among other things ``always`` and
``always_ff`` processes with an explicit sensitivity list.
Note that the distinction between clocked logic and combinational logic is only
important for the scheduling algorithm within Verilator as we handle the two
classes differently. It is possible to convert clocked logic into combinational
logic if the explicit sensitivity list of the clocked logic is the same as the
implicit sensitivity list of the equivalent combinational logic would be. The
canonical examples are: ``always @(a) x = a;``, which is considered to be
clocked logic by Verilator, and the equivalent ``assign x = a;``, which is
considered to be combinational logic. ``V3Active`` in fact converts all clocked
logic to combinational logic whenever possible, as this provides advantages for
scheduling as described below.
There is also a 'hybrid' logic class, which has both explicit and implicit
sensitivities. This kind of logic does not arise from a SystemVerilog
construct, but is created during scheduling to break combinational cycles.
Details of this process and the hybrid logic class are described below.
Scheduling of simple classes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
SystemVerilog ``initial`` and ``final`` blocks can be scheduled (executed) in an
arbitrary order.
Static variable initializers need to be executed in source code order in case
there is a dependency between initializers, but the ordering of static variable
initialization is otherwise not defined by the SystemVerilog standard
(particularly, in the presence of hierarchical references in static variable
initializers).
The scheduling algorithm handles all three of these classes the same way and
schedules the logic in these classes in source code order. This step yields the
``_eval_static``, ``_eval_initial`` and ``_eval_final`` functions which execute
the corresponding logic constructs.
Scheduling of clocked and combinational logic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For performance, clocked and combinational logic needs to be ordered.
Conceptually this minimizes the iterations through the evaluation loop
presented in the reference algorithm in the SystemVerilog standard (IEEE
1800-2017 section 4.5), by evaluating logic constructs in data-flow order.
Without going into a lot of detail here, accept that well thought out ordering
is crucial to good simulation performance, and also enables further
optimizations later on.
At the highest level, ordering is performed by ``V3Order::order``, which is
invoked by ``V3Sched::schedule`` on various subsets of the combinational and
clocked logic as described below. The important thing to highlight now is that
``V3Order::order`` operates by assuming that the state of all variables driven
by combinational logic are consistent with that combinational logic. While this
might seem subtle, it is very important, so here is an example:
::
always_comb d = q + 2;
always @(posedge clock) q <= d;
During ordering, ``V3Order`` will assume that ``d`` equals ``q + 2`` at the
beginning of an evaluation step. As a result it will order the clocked logic
first, and all downstream combinational logic (like the assignment to ``d``)
will execute after the clocked logic that drives inputs to the combinational
logic, in data-flow (or dependency) order. At the end of the evaluation step,
this ordering restores the invariant that variables driven by combinational
logic are consistent with that combinational logic (i.e.: the circuit is in a
settled/steady state).
One of the most important optimizations for performance is to only evaluate
combinational logic, if its inputs might have changed. For example, there is no
point in evaluating the above assignment to ``d`` on a negative edge of the
clock signal. Verilator does this by pushing the combinational logic into the
same (possibly multiple) event domains as the logic driving the inputs to that
combinational logic, and only evaluating the combinational logic if at least
one driving domains have been triggered. The impact of this activity gating is
very high (observed 100x slowdown on large designs when turning it off), it is
the reason we prefer to convert clocked logic to combinational logic in
``V3Active`` whenever possible.
The ordering procedure described above works straight forward unless there are
combinational logic constructs that are circularly dependent (a.k.a.: the
UNOPTFLAT warning). Combinational scheduling loops can arise in sound
(realizable) circuits as Verilator considers each SystemVerilog process as a
unit of scheduling (albeit we do try to split processes into smaller ones to
avoid this circularity problem whenever possible, this is not always possible).
Breaking combinational loops
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Combinational loops are broken by the introduction of instances of the 'hybrid'
logic class. As described in the previous section, combinational loops require
iteration until the logic is settled, in order to restore the invariant that
combinationally driven signals are consistent with the combinational logic.
To achieve this, ``V3Sched::schedule`` calls ``V3Sched::breakCycles``, which
builds a dependency graph of all combinational logic in the design, and then
breaks all combinational cycles by converting all combinational logic that
consumes a variable driven via a 'back-edge' into hybrid logic. Here
'back-edge' just means a graph edge that points from a higher rank vertex to a
lower rank vertex in some consistent ranking of the directed graph. Variables
driven via a back-edge in the dependency graph are marked, and all
combinational logic that depends on such variables is converted into hybrid
logic, with the back-edge driven variables listed as explicit 'changed'
sensitivities.
Hybrid logic is handled by ``V3Order`` mostly in the same way as combinational
logic, with two exceptions:
- Explicit sensitivities of hybrid logic are ignored for the purposes of
data-flow ordering with respect to other combinational or hybrid logic. I.e.:
an explicit sensitivity suppresses the implicit sensitivity on the same
variable. This cold also be interpreted as ordering the hybrid logic as if
all variables listed as explicit sensitivities were substituted as constants
with their current values.
- The explicit sensitivities are included as an additional driving domain of
the logic, and also cause evaluation when triggered.
This means that hybrid logic is evaluated when either any of its implicit
sensitivities might have been updated (the same way as combinational logic, by
pushing it into the domains that write those variables), or if any of its
explicit sensitivities are triggered.
The effect of this transformation is that ``V3Order`` can proceed as if there
are no combinational cycles (or alternatively, under the assumption that the
back-edge driven variables don't change during one evaluation pass). The
evaluation loop invoking the ordered code, will then re-invoke it on a follow
on iteration, if any of the explicit sensitivities of hybrid logic have
actually changed due to the previous invocation, iterating until all the
combinational (including hybrid) logic have settled.
One might wonder if there can be a race condition between clocked logic
triggered due to a combinational signal change from the previous evaluation
pass, and a combinational loop settling due to hybrid logic, if the clocked
logic reads the not yet settled combinationally driven signal. Such a race is
indeed possible, but our evaluation is consistent with the SystemVerilog
scheduling semantics (IEEE 1800-2017 chapter 4), and therefore any program that
exhibits such a race has non-deterministic behaviour according to the
SystemVerilog semantics, so we accept this.
Settling combinational logic after initialization
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
At the beginning of simulation, once static initializer and ``initial`` blocks
have been executed, we need to evaluate all combinational logic, in order to
restore the invariant utilized by ``V3Order`` that the state of all
combinationally driven variables are consistent with the combinational logic.
To achieve this, we invoke ``V3Order::order`` on all of the combinational and
hybrid logic, and iterate the resulting evaluation function until no more
hybrid logic is triggered. This yields the `_eval_settle` function which is
invoked at the beginning of simulation, after the `_eval_initial`.
Partitioning logic for correct NBA updates
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``V3Order`` can order logic corresponding to non-blocking assignments (NBAs) to
yield correct simulation results, as long as all the sensitivity expressions of
clocked logic triggered in the Active scheduling region of the current time
step are known up front. I.e.: the ordering of NBA updates is only correct if
derived clocks that are computed in an Active region update (that is, via a
blocking or continuous assignment) are known up front.
We can ensure this by partitioning the logic into two regions. Note these
regions are a concept of the Verilator scheduling algorithm and they do not
directly correspond to the similarly named SystemVerilog scheduling regions
as defined in the standard:
- All logic (clocked, combinational and hybrid) that transitively feeds into,
or drives, via a non-blocking or continuous assignments (or via any update
that SystemVerilog executes in the Active scheduling region), a variable that
is used in the explicit sensitivity list of some clocked or hybrid logic, is
assigned to the 'act' region.
- All other logic is assigned to the 'nba' region.
For completeness, note that a subset of the 'act' region logic, specifically,
the logic related to the pre-assignments of NBA updates (i.e.: AstAssignPre
nodes), is handled separately, but is executed as part of the 'act' region.
Also note that all logic representing the committing of an NBA (i.e.: Ast*Post)
nodes) will be in the 'nba' region. This means that the evaluation of the 'act'
region logic will not commit any NBA updates. As a result, the 'act' region
logic can be iterated to compute all derived clock signals up front.
The correspondence between the SystemVerilog Active and NBA scheduling regions,
and the internal 'act' and 'nba' regions, is that 'act' contains all Active
region logic that can compute a clock signal, while 'nba' contains all other
Active and NBA region logic. For example, if the only clocks in the design are
top level inputs, then 'act' will be empty, and 'nba' will contain the whole of
the design.
The partitioning described above is performed by ``V3Sched::partition``.
Replication of combinational logic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
We will separately invoke ``V3Order::order`` on the 'act' and 'nba' region
logic.
Combinational logic that reads variables driven from both 'act' and 'nba'
region logic has the problem of needing to be re-evaluated even if only one of
the regions updates an input variable. We could pass additional trigger
expressions between the regions to make sure combinational logic is always
re-evaluated, or we can replicate combinational logic that is driven from
multiple regions, by copying it into each region that drives it. Experiments
show this simple replication works well performance-wise (and notably
``V3Combine`` is good at combining the replicated code), so this is what we do
in ``V3Sched::replicateLogic``.
In ``V3Sched::replicateLogic``, in addition to replicating logic into the 'act'
and 'nba' regions, we also replicate combinational (and hybrid) logic that
depends on top level inputs. These become a separate 'ico' region (Input
Combinational logic), which we will always evaluate at the beginning of a
time-step to ensure the combinational invariant holds even if input signals
have changed. Note that this eliminates the need of changing data and clock
signals on separate evaluations, as was necessary with earlier versions of
Verilator).
Constructing the top level `_eval` function
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To construct the top level `_eval` function, which updates the state of the
circuit to the end of the current time step, we invoke ``V3Order::order``
separately on the 'ico', 'act' and 'nba' logic, which yields the `_eval_ico`,
`_eval_act`, and `_eval_nba` functions. We then put these all together with the
corresponding functions that compute the respective trigger expressions into
the top level `_eval` function, which on the high level has the form:
::
void _eval() {
// Update combinational logic dependent on top level inptus ('ico' region)
while (true) {
_eval__triggers__ico();
// If no 'ico' region trigger is active
if (!ico_triggers.any()) break;
_eval_ico();
}
// Iterate 'act' and 'nba' regions together
while (true) {
// Iterate 'act' region, this computes all derived clocks updaed in the
// Active scheduling region, but does not commit any NBAs that executed
// in 'act' region logic.
while (true) {
_eval__triggers__act();
// If no 'act' region trigger is active
if (!act_triggers.any()) break;
// Remember what 'act' triggers were active, 'nba' uses the same
latch_act_triggers_for_nba();
_eval_act();
}
// If no 'nba' region trigger is active
if (!nba_triggers.any()) break;
// Evaluate all other Active region logic, and commti NBAs
_eval_nba();
}
}
Timing
------
Timing support in Verilator utilizes C++ coroutines, which is a new feature in
C++20. The basic idea is to represent processes and tasks that await a certain
event or simulation time as coroutines. These coroutines get suspended at the
await, and resumed whenever the triggering event occurs, or at the expected
simulation time.
There are several runtime classes used for managing such coroutines defined in
``verilated_timing.h`` and ``verilated_timing.cpp``.
``VlCoroutineHandle``
^^^^^^^^^^^^^^^^^^^^^
A thin wrapper around an ``std::coroutine_handle<>``. It forces move semantics,
destroys the coroutine if it remains suspended at the end of the design's
lifetime, and prevents multiple ``resume`` calls in the case of
``fork..join_any``.
``VlCoroutine``
^^^^^^^^^^^^^^^
Return value of all coroutines. Together with the promise type contained
within, it allows for chaining coroutines – resuming coroutines from up the
call stack. The calling coroutine's handle is saved in the promise object as a
continuation, that is, the coroutine that must be resumed after the promise's
coroutine finishes. This is necessary as C++ coroutines are stackless, meaning
each one is suspended independently of others in the call graph.
``VlDelayScheduler``
^^^^^^^^^^^^^^^^^^^^
This class manages processes suspended by delays. There is one instance of this
class per design. Coroutines ``co_await`` this object's ``delay`` function.
Internally, they are stored in a heap structure sorted by simulation time in
ascending order. When ``resume`` is called on the delay scheduler, all
coroutines awaiting the current simulation time are resumed. The current
simulation time is retrieved from a ``VerilatedContext`` object.
``VlTriggerScheduler``
^^^^^^^^^^^^^^^^^^^^^^
This class manages processes that await events (triggers). There is one such
object per each trigger awaited by coroutines. Coroutines ``co_await`` this
object's ``trigger`` function. They are stored in two stages – `uncommitted`
and `ready`. First, they land in the `uncommitted` stage, and cannot be
resumed. The ``resume`` function resumes all coroutines from the `ready` stage
and moves `uncommitted` coroutines into `ready`. The ``commit`` function only
moves `uncommitted` coroutines into `ready`.
This split is done to avoid self-triggering and triggering coroutines multiple
times. See the `Scheduling with timing` section for details on how this is
used.
``VlDynamicTriggerScheduler``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Like ``VlTriggerScheduler``, ``VlDynamicTriggerScheduler`` manages processes
that await triggers. However, it does not rely on triggers evaluated externally
by the 'act' trigger eval function. Instead, it is also responsible for trigger
evaluation. Coroutines that make use of this scheduler must adhere to a certain
procedure:
::
__Vtrigger = 0;
<locals and inits required for trigger eval>
while (!__Vtrigger) {
co_await __VdynSched.evaluation();
<pre updates>;
__Vtrigger = <trigger eval>;
[optionally] co_await __VdynSched.postUpdate();
<post updates>;
}
co_await __VdynSched.resumption();
The coroutines get resumed at trigger evaluation time, evaluate their local
triggers, optionally await the post update step, and if the trigger is set,
await proper resumption in the 'act' eval step.
``VlForkSync``
^^^^^^^^^^^^^^
Used for synchronizing ``fork..join`` and ``fork..join_any``. Forking
coroutines ``co_await`` its ``join`` function, and forked ones call ``done``
when they're finished. Once the required number of coroutines (set using
``setCounter``) finish execution, the forking coroutine is resumed.
Awaitable utilities
^^^^^^^^^^^^^^^^^^^
There are also two small utility awaitable types:
* ``VlNow`` is an awaitable that suspends and immediately resumes coroutines.
It is used for forcing a coroutine to be moved onto the heap. See the `Forks`
section for more detail.
* ``VlForever`` is used for blocking a coroutine forever. See the `Timing pass`
section for more detail.
Timing pass
^^^^^^^^^^^
The visitor in ``V3Timing.cpp`` transforms each timing control into a ``co_await``.
* event controls are turned into ``co_await`` on a trigger scheduler's
``trigger`` method. The awaited trigger scheduler is the one corresponding to
the sentree referenced by the event control. This sentree is also referenced
by the ``AstCAwait`` node, to be used later by the static scheduling code.
* if an event control waits on a local variable or class member, it uses a
local trigger which it evaluates inline. It awaits a dynamic trigger
scheduler multiple times: for trigger evaluation, updates, and resumption.
The dynamic trigger scheduler is responsible for resuming the coroutine at
the correct point of evaluation.
* delays are turned into ``co_await`` on a delay scheduler's ``delay`` method.
The created ``AstCAwait`` nodes also reference a special sentree related to
delays, to be used later by the static scheduling code.
* ``join`` and ``join_any`` are turned into ``co_await`` on a ``VlForkSync``'s
``join`` method. Each forked process gets a ``VlForkSync::done`` call at the
end.
Assignments with intra-assignment timing controls are simplified into
assignments after those timing controls, with the LHS and RHS values evaluated
before them and stored in temporary variables.
``wait`` statements are transformed into while loops that check the condition
and then await changes in variables used in the condition. If the condition is
always false, the ``wait`` statement is replaced by a ``co_await`` on a
``VlForever``. This is done instead of a return in case the ``wait`` is deep in
a call stack (otherwise the coroutine's caller would continue execution).
Each sub-statement of a ``fork`` is put in an ``AstBegin`` node for easier
grouping. In a later step, each of these gets transformed into a new, separate
function. See the `Forks` section for more detail.
Processes that use awaits are marked as suspendable. Later, during ``V3Sched``,
they are transformed into coroutines. Functions that use awaits get the return
type of ``VlCoroutine``. This immediately makes them coroutines. Note that if a
process calls a function that is a coroutine, the call gets wrapped in an
await, which means the process itself will be marked as suspendable. A virtual
function is a coroutine if any of its overriding or overridden functions are
coroutines. The visitor keeps a dependency graph of functions and processes to
handle such cases.
Scheduling with timing
^^^^^^^^^^^^^^^^^^^^^^
Timing features in Verilator are built on top of the static scheduler. Triggers
are used for determining which delay or trigger schedulers should resume. A
special trigger is used for the delay scheduler. This trigger is set if there
are any coroutines awaiting the current simulation time
(``VlDelayScheduler::awaitingCurrentTime()``).
All triggers used by a suspendable process are mapped to variables written in
that process. When ordering code using ``V3Order``, these triggers are provided
as external domains of these variables. This ensures that the necessary
combinational logic is triggered after a coroutine resumption.
There are two functions for managing timing logic called by ``_eval()``:
* ``_timing_commit()``, which commits all coroutines whose triggers were not set
in the current iteration,
* ``_timing_resume()``, which calls `resume()` on all trigger and delay
schedulers whose triggers were set in the current iteration.
Thanks to this separation, a coroutine awaiting a trigger cannot be suspended
and resumed in the same iteration, and it cannot be resumed before it suspends.
All coroutines are committed and resumed in the 'act' eval loop. With timing
features enabled, the ``_eval()`` function takes this form:
::
void _eval() {
while (true) {
_eval__triggers__ico();
if (!ico_triggers.any()) break;
_eval_ico();
}
while (true) {
while (true) {
_eval__triggers__act();
// Commit all non-triggered coroutines
_timing_commit();
if (!act_triggers.any()) break;
latch_act_triggers_for_nba();
// Resume all triggered coroutines
_timing_resume();
_eval_act();
}
if (!nba_triggers.any()) break;
_eval_nba();
}
}
Forks
^^^^^
After the scheduling step, forks sub-statements are transformed into separate
functions, and these functions are called in place of the sub-statements. These
calls must be without ``co_await``, so that suspension of a forked process
doesn't suspend the forking process.
In forked processes, references to local variables are only allowed in
``fork..join``, as this is the only case that ensures the lifetime of these
locals is at least as long as the execution of the forked processes. This is
where ``VlNow`` is used, to ensure the locals are moved to the heap before they
are passed by reference to the forked processes.
Multithreaded Mode
------------------
@@ -501,22 +1064,107 @@ code:
The ``astgen`` Script
---------------------
Some of the code implementing passes is extremely repetitive, and must be
implemented for each sub-class of ``AstNode``. However, while repetitive,
there is more variability than can be handled in C++ macros.
The ``astgen`` script is used to generate some of the repetitive C++ code
related to the ``AstNode`` type hierarchy. An example is the abstract ``visit``
methods in ``VNVisitor``. There are other uses, please see the ``*__gen*``
files in the bulid directories and the ``astgen`` script itself for details. A
description of the more advanced features of ``astgen`` are provided here.
In Verilator this is implemented by using a script, ``astgen`` to
pre-process the C++ code. For example in ``V3Const.cpp`` this is used to
implement the ``visit()`` functions for each binary operation using the
``TREEOP`` macro.
The original C source code is transformed into C code in the ``obj_opt``
Generating ``AstNode`` members
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Some of the member s of ``AstNode`` sub-classes are generated by ``astgen``.
These are emitted as pre-processor macro definitions, which then need to be
added to the ``AstNode`` sub-classes they correspond to. Specifically ``class
AstFoo`` should contain an instance of ``ASTGEN_MEMBERS_AstFoo;`` at class
scope. The ``astgen`` script checks and errors if this is not present. The
method generated depends on whether the class is a concrete final class, or an
abstract ``AstNode*`` base-class, and on ``@astgen`` directives present in
comment sections in the body of the ``AstNode`` sub-class definitions.
List of ``@astgen`` directives
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``@astgen`` directives in comments contained in the body of ``AstNode``
sub-class definitions are parsed and contribute to the code generated by
``astgen``. The general syntax is ``@astgen <keywords> := <description>``,
where ``<keywords>`` determines what is being defined, and ``<description>`` is
a ``<keywords>`` dependent description of the definition. The list of
``@astgen`` directives is as follows:
``op<N>`` operand directives
"""""""""""""""""""""""""""""
The ``op1``, ``op2``, ``op3`` and ``op4`` directives are used to describe the
name and type of the up to 4 child operands of a node. The syntax of the
``<description>`` field is ``<identifier> : <type>``, where ``<identifier>``
will be used as the base name of the generated operand accessors, and
``<type>`` is one of:
1. An ``AstNode`` sub-class, defining the operand to be of that type, always
no-null, and with an always null ``nextp()``. That is, the child node is
always present, and is a single ``AstNode`` (as opposed to a list).
2. ``Optional[<AstNode sub-class>]``. This is just like in point 1 above, but
defines the child node to be optional, meaning it may be null.
3. ``List[AstNode sub-class]`` describes a list operand, which means the child
node may have a non-null ``nextp()`` and in addition the child itself may be
null, representing an empty list.
An example of the full syntax of the directive is
``@astgen op1 := lhsp : AstNodeMath``.
``astnode`` generates accessors for the child nodes based on these directives.
For non-list children, the names of the getter and setter both are that of the
given ``<identifier>``. For list type children, the getter is ``<identifier>``,
and instead of the setter, there an ``add<Identifier>`` method is generated
that appends new nodes (or lists of nodes) to the child list.
``alias op<N>`` operand alias directives
""""""""""""""""""""""""""""""""""""""""
If a super-class already defined a name and type for a child node using the
``op<N>`` directive, but a more appropriate name exists in the context of a
sub-class, then the alias directive can be used to introduce an additional name
for the child node. The is ``alias op<N> := <identifier>`` where
``<identifier>`` is the new name. ``op<N>`` must have been defined in some
super-class of the current node.
Example: ``@astgen alias op1 := condp``
Generating ``DfgVertex`` sub-classes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Most of the ``DfgVertex`` sub-classes are generated by ``astgen``, from the
definitions of the corresponding ``AstNode`` vertices.
Additional features of ``astgen``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In addition to generating ``AstNode`` members as described above,
``astgen`` is also use to handle some of the repetitive implementation code
that is still variable enough not to be handled in C++ macros.
In particular, ``astgen`` is used to pre-process some of the C++ source
files. For example in ``V3Const.cpp``, it is used to implement the
``visit()`` functions for each binary operation using the ``TREEOP`` macro.
The original C++ source code is transformed into C++ code in the ``obj_opt``
and ``obj_dbg`` sub-directories (the former for the optimized version of
Verilator, the latter for the debug version). So for example
``V3Const.cpp`` into ``V3Const__gen.cpp``.
Visitor Functions -----------------
Visitor Functions
-----------------
Verilator uses the "Visitor" design pattern to implement its refinement and
optimization passes. This allows separation of the pass algorithm from the
@@ -1059,6 +1707,16 @@ Similarly the ``NETLIST`` has a list of modules referred to by its
``op1p()`` pointer.
.tree.dot Output
----------------
``*.tree.dot`` files are dumps of the AST Tree in `Graphviz
<https://www.graphviz.org>`__ dot format. This can be used to
visualize the AST Tree. The vertices correspond to ``AstNode``
instances, and the edges represent the pointers (``op1p``,
``op2p``, etc) between the nodes.
Debugging with GDB
------------------
@@ -1153,8 +1811,9 @@ Generally what would you do to add a new feature?
language and has a lot of back-and-forth with Verilator's grammar. Copy
the appropriate rules to src/verilog.y and modify the productions.
4. If a new Ast type is needed, add it to V3AstNodes.h. Follow the
convention described above about the AstNode type hierarchy.
4. If a new Ast type is needed, add it to the appropriate V3AstNode*.h.
Follow the convention described above about the AstNode type hierarchy.
Ordering of definitions is enforced by ``astgen``.
5. Now you can run "test_regress/t/t_<newtestcase>.pl --debug" and it'll
probably fail but you'll see a
+17
View File
@@ -7,12 +7,14 @@ Ami
Amir
Anastasiadis
Anikin
Antmicro
Antonin
Antwerpen
Arasanipalai
Arjen
Asciidoc
Ashutosh
Ast
Atmel
Aurelien
Bagri
@@ -108,8 +110,10 @@ Goessling
Gonnen
Goorah
Gossner
Graphviz
Graybeal
Grobman
Grulfen
Gunter
Guo
Hao
@@ -131,6 +135,7 @@ Iles
Inlines
Inout
Iru
Iyer
Iztok
Jacko
Jae
@@ -157,6 +162,7 @@ Karge
Karlsson
Katz
Katzman
Kelin
Keren
Keyi
Kimmitt
@@ -340,6 +346,7 @@ Verilating
Verilation
Verilator
Verilog
Vighnesh
Viktor
Vm
Vukobratovic
@@ -376,6 +383,7 @@ agrobman
ahouska
al
ala
algrobman
andit
ar
architected
@@ -397,6 +405,7 @@ bbox
benchmarking
biguint
biops
bisonpre
bitOpTree
bitOpTree
bitop
@@ -441,6 +450,7 @@ const
constexpr
constpool
coredump
coroutine
countbits
countones
cout
@@ -463,6 +473,7 @@ defenv
defname
defparam
demangling
dep
der
dereference
desassign
@@ -480,6 +491,7 @@ dsvf
dtor
dumpall
dumpfile
dumpi
dumplimit
dumpoff
dumpon
@@ -537,6 +549,7 @@ filesystem
filt
flto
flushCall
fno
fopen
forceable
foreach
@@ -600,6 +613,7 @@ inouts
inserted
instantiation
instantiations
intra
iostream
ish
isunbounded
@@ -725,6 +739,7 @@ profiler
prototyptes
ps
pthread
ptr
pulldown
pulldowns
pullup
@@ -750,6 +765,7 @@ reloop
resetall
respecified
rodata
rolloverSize
rr
rst
runtime
@@ -764,6 +780,7 @@ seg
setuphold
sformat
sformatf
shareefj
shortint
shortreal
signame
+3 -21
View File
@@ -39,30 +39,12 @@ ifneq ($(CMAKE_GT_3_8),true)
TARGET := oldcmake
else
# Test existence of SYSTEMC_INCLUDE and SYSTEMC_LIBDIR environment variabless
ifneq (,$(SYSTEMC_INCLUDE))
ifneq (,${SYSTEMC_LIBDIR})
SYSTEMC_SET := true
endif
endif
# Test existence of SYSTEMC_ROOT environment variable
ifneq (SYSTEMC_SET, true)
ifneq (,${SYSTEMC_ROOT})
SYSTEMC_SET := true
endif
endif
# Test existence of SYSTEMC environment variable
ifneq (SYSTEMC_SET, true)
ifneq (,${SYSTEMC})
SYSTEMC_SET := true
endif
endif
# Check if SC exists via a verilator call (empty if not)
SYSTEMC_EXISTS := $(shell $(VERILATOR) --get-supported SYSTEMC)
# Test whether SystemC is installed with CMake support
# This will print a CMake error about processing arguments that can (currently) be ignored.
ifneq (SYSTEMC_SET, true)
ifneq (,$(SYSTEMC_EXISTS))
FINDSC := $(shell mkdir -p build && cd build && cmake --find-package -DNAME=SystemCLanguage -DCMAKE_USE_PTHREADS_INIT=ON -DCOMPILER_ID=GNU -DLANGUAGE=CXX -DMODE=EXIST -DThreads_FOUND=ON)
ifneq (,$(findstring SystemCLanguage found,$(FINDSC)))
SYSTEMC_SET := true
+3 -21
View File
@@ -39,30 +39,12 @@ ifneq ($(CMAKE_GT_3_8),true)
TARGET := oldcmake
else
# Test existence of SYSTEMC_INCLUDE and SYSTEMC_LIBDIR environment variabless
ifneq (,$(SYSTEMC_INCLUDE))
ifneq (,${SYSTEMC_LIBDIR})
SYSTEMC_SET := true
endif
endif
# Test existence of SYSTEMC_ROOT environment variable
ifneq (SYSTEMC_SET, true)
ifneq (,${SYSTEMC_ROOT})
SYSTEMC_SET := true
endif
endif
# Test existence of SYSTEMC environment variable
ifneq (SYSTEMC_SET, true)
ifneq (,${SYSTEMC})
SYSTEMC_SET := true
endif
endif
# Check if SC exists via a verilator call (empty if not)
SYSTEMC_EXISTS := $(shell $(VERILATOR) --get-supported SYSTEMC)
# Test whether SystemC is installed with CMake support
# This will print a CMake error about processing arguments that can (currently) be ignored.
ifneq (SYSTEMC_SET, true)
ifneq (,$(SYSTEMC_EXISTS))
FINDSC := $(shell mkdir -p build && cd build && cmake --find-package -DNAME=SystemCLanguage -DCMAKE_USE_PTHREADS_INIT=ON -DCOMPILER_ID=GNU -DLANGUAGE=CXX -DMODE=EXIST -DThreads_FOUND=ON)
ifneq (,$(findstring SystemCLanguage found,$(FINDSC)))
SYSTEMC_SET := true
+6
View File
@@ -0,0 +1,6 @@
*.dmp
*.log
*.csrc
*.vcd
obj_*
logs
+49
View File
@@ -0,0 +1,49 @@
######################################################################
#
# DESCRIPTION: Verilator Example: Small Makefile
#
# This calls the object directory makefile. That allows the objects to
# be placed in the "current directory" which simplifies the Makefile.
#
# This file ONLY is placed under the Creative Commons Public Domain, for
# any use, without warranty, 2020 by Wilson Snyder.
# SPDX-License-Identifier: CC0-1.0
#
######################################################################
# Check for sanity to avoid later confusion
ifneq ($(words $(CURDIR)),1)
$(error Unsupported: GNU Make cannot build in directories containing spaces, build elsewhere: '$(CURDIR)')
endif
######################################################################
# This is intended to be a minimal example. Before copying this to start a
# real project, it is better to start with a more complete example,
# e.g. examples/make_tracing_c.
# If $VERILATOR_ROOT isn't in the environment, we assume it is part of a
# package install, and verilator is in your path. Otherwise find the
# binary relative to $VERILATOR_ROOT (such as when inside the git sources).
ifeq ($(VERILATOR_ROOT),)
VERILATOR = verilator
else
export VERILATOR_ROOT
VERILATOR = $(VERILATOR_ROOT)/bin/verilator
endif
default:
@echo "-- Verilator hello-world simple binary example"
@echo "-- VERILATE & BUILD --------"
$(VERILATOR) --binary -j 0 top.v
@echo "-- RUN ---------------------"
obj_dir/Vtop
@echo "-- DONE --------------------"
@echo "Note: Once this example is understood, see examples/make_hello_c."
@echo "Note: See also https://verilator.org/guide/latest/examples.html"
######################################################################
maintainer-copy::
clean mostlyclean distclean maintainer-clean::
-rm -rf obj_dir *.log *.dmp *.vpd core
+14
View File
@@ -0,0 +1,14 @@
// DESCRIPTION: Verilator: Verilog example module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2017 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
// See also https://verilator.org/guide/latest/examples.html"
module top;
initial begin
$display("Hello World!");
$finish;
end
endmodule
+5 -2
View File
@@ -21,11 +21,14 @@ int main(int argc, char** argv, char** env) {
// Prevent unused variable warnings
if (false && argc && argv && env) {}
// Construct a VerilatedContext to hold simulation time, etc.
VerilatedContext* contextp = new VerilatedContext;
// Construct the Verilated model, from Vtop.h generated from Verilating "top.v"
Vtop* top = new Vtop;
Vtop* top = new Vtop{contextp};
// Simulate until $finish
while (!Verilated::gotFinish()) {
while (!contextp->gotFinish()) {
// Evaluate model
top->eval();
+1 -1
View File
@@ -33,7 +33,7 @@ VERILATOR = $(VERILATOR_ROOT)/bin/verilator
endif
# Check if SC exists via a verilator call (empty if not)
SYSTEMC_EXISTS := $(shell $(VERILATOR) --getenv SYSTEMC_INCLUDE)
SYSTEMC_EXISTS := $(shell $(VERILATOR) --get-supported SYSTEMC)
ifneq ($(SYSTEMC_EXISTS),)
default: run
+1 -1
View File
@@ -32,7 +32,7 @@ int main(int argc, char** argv, char** env) {
// When tracing, the contents of the secret module will not be seen
VerilatedVcdC* tfp = nullptr;
const char* flag = contextp->commandArgsPlusMatch("trace");
if (flag && 0 == strcmp(flag, "+trace")) {
if (flag && 0 == std::strcmp(flag, "+trace")) {
contextp->traceEverOn(true);
VL_PRINTF("Enabling waves into logs/vlt_dump.vcd...\n");
tfp = new VerilatedVcdC;
+1 -1
View File
@@ -55,7 +55,7 @@ VERILATOR_FLAGS += --coverage
VERILATOR_INPUT = -f input.vc top.v sc_main.cpp
# Check if SC exists via a verilator call (empty if not)
SYSTEMC_EXISTS := $(shell $(VERILATOR) --getenv SYSTEMC_INCLUDE)
SYSTEMC_EXISTS := $(shell $(VERILATOR) --get-supported SYSTEMC)
######################################################################
+2 -2
View File
@@ -82,14 +82,14 @@ int sc_main(int argc, char* argv[]) {
// You must do one evaluation before enabling waves, in order to allow
// SystemC to interconnect everything for testing.
sc_start(1, SC_NS);
sc_start(SC_ZERO_TIME);
#if VM_TRACE
// If verilator was invoked with --trace argument,
// and if at run time passed the +trace argument, turn on tracing
VerilatedVcdSc* tfp = nullptr;
const char* flag = Verilated::commandArgsPlusMatch("trace");
if (flag && 0 == strcmp(flag, "+trace")) {
if (flag && 0 == std::strcmp(flag, "+trace")) {
cout << "Enabling waves into logs/vlt_dump.vcd...\n";
tfp = new VerilatedVcdSc;
top->trace(tfp, 99); // Trace 99 levels of hierarchy
+5 -4
View File
@@ -16,12 +16,13 @@ from shutil import copy2
class VlFileCopy:
def __init__(
self,
verilator_args, # presently all verilator options are passed-thru
# ideally this script would check against options mentioned in help
self,
verilator_args, # presently all verilator options are passed-thru
# ideally this script would check against options mentioned in help
debug=0,
output_dir='copied'): # directory name we output file uses
output_dir='copied'): # directory name we output file uses
self.debug = debug
+5 -4
View File
@@ -14,12 +14,13 @@ import xml.etree.ElementTree as ET
class VlHierGraph:
def __init__(
self,
verilator_args, # presently all verilator options are passed-thru
# ideally this script would check against options mentioned in help
self,
verilator_args, # presently all verilator options are passed-thru
# ideally this script would check against options mentioned in help
debug=0,
output_filename='graph.dot'): # output filename
output_filename='graph.dot'): # output filename
self.debug = debug
self.next_vertex_number = 0
self.name_to_number = {}
+1 -1
View File
@@ -140,7 +140,7 @@ void **JenkinsIns(void *base_i, const unsigned char *mem, uint32_t length, uint3
#include <sys/sysctl.h>
#endif
#if defined(FST_MACOSX) || defined(__MINGW32__) || defined(__OpenBSD__) || defined(__FreeBSD__)
#if defined(FST_MACOSX) || defined(__MINGW32__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__)
#define FST_UNBUFFERED_IO
#endif
+222 -108
View File
@@ -47,27 +47,34 @@
#define VERILATOR_VERILATED_CPP_
#include "verilatedos.h"
#include "verilated_imp.h"
#include "verilated_config.h"
#include "verilatedos.h"
#include "verilated_imp.h"
#include <algorithm>
#include <cctype>
#include <cerrno>
#include <cstdlib>
#include <sstream>
#include <sys/stat.h> // mkdir
#include <list>
#include <limits>
#include <list>
#include <sstream>
#include <utility>
#include <sys/stat.h> // mkdir
// clang-format off
#if defined(_WIN32) || defined(__MINGW32__)
# include <direct.h> // mkdir
#endif
#ifdef VL_THREADED
# include "verilated_threads.h"
#endif
// clang-format on
#include "verilated_trace.h"
// Max characters in static char string for VL_VALUE_STRING
constexpr unsigned VL_VALUE_STRING_MAX_WIDTH = 8192;
@@ -673,7 +680,10 @@ std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t w
if (std::numeric_limits<T>::is_integer) {
constexpr int b = 128;
constexpr int w = VL_WORDS_I(b);
VlWide<w> tmp0, tmp1, tmp2, tmp3;
VlWide<w> tmp0;
VlWide<w> tmp1;
VlWide<w> tmp2;
VlWide<w> tmp3;
WDataInP shifted = VL_EXTEND_WQ(b, 0, tmp0, static_cast<QData>(ld));
if (shift < 0) {
@@ -691,7 +701,8 @@ std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t w
= VL_EXTEND_WQ(b, 0, tmp2, std::numeric_limits<uint64_t>::max()); // breaks shifted
if (VL_GT_W(w, integer, max64Bit)) {
WDataOutP v = VL_ASSIGN_W(b, tmp3, integer); // breaks fracDigitsPow10
VlWide<w> zero, ten;
VlWide<w> zero;
VlWide<w> ten;
VL_ZERO_W(b, zero);
VL_EXTEND_WI(b, 0, ten, 10);
char buf[128]; // 128B is obviously long enough to represent 128bit integer in decimal
@@ -894,15 +905,22 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
digits = append.length();
}
const int needmore = width - digits;
std::string padding;
if (needmore > 0) {
if (pctp && pctp[0] && pctp[1] == '0') { // %0
padding.append(needmore, '0'); // Pre-pad zero
} else {
std::string padding;
if (left) {
padding.append(needmore, ' '); // Pre-pad spaces
output += append + padding;
} else {
if (pctp && pctp[0] && pctp[1] == '0') { // %0
padding.append(needmore, '0'); // Pre-pad zero
} else {
padding.append(needmore, ' '); // Pre-pad spaces
}
output += padding + append;
}
} else {
output += append;
}
output += left ? (append + padding) : (padding + append);
break;
}
case '#': { // Unsigned decimal
@@ -916,15 +934,22 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
digits = append.length();
}
const int needmore = width - digits;
std::string padding;
if (needmore > 0) {
if (pctp && pctp[0] && pctp[1] == '0') { // %0
padding.append(needmore, '0'); // Pre-pad zero
} else {
std::string padding;
if (left) {
padding.append(needmore, ' '); // Pre-pad spaces
output += append + padding;
} else {
if (pctp && pctp[0] && pctp[1] == '0') { // %0
padding.append(needmore, '0'); // Pre-pad zero
} else {
padding.append(needmore, ' '); // Pre-pad spaces
}
output += padding + append;
}
} else {
output += append;
}
output += left ? (append + padding) : (padding + append);
break;
}
case 't': { // Time
@@ -933,21 +958,62 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
output += _vl_vsformat_time(t_tmp, ld, timeunit, left, width);
break;
}
case 'b':
for (; lsb >= 0; --lsb) output += (VL_BITRSHIFT_W(lwp, lsb) & 1) + '0';
break;
case 'o':
for (; lsb >= 0; --lsb) {
lsb = (lsb / 3) * 3; // Next digit
// Octal numbers may span more than one wide word,
// so we need to grab each bit separately and check for overrun
// Octal is rare, so we'll do it a slow simple way
output += static_cast<char>(
'0' + ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 0)) ? 1 : 0)
+ ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 1)) ? 2 : 0)
+ ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 2)) ? 4 : 0));
case 'b': // FALLTHRU
case 'o': // FALLTHRU
case 'x': {
if (widthSet || left) {
lsb = VL_MOSTSETBITP1_W(VL_WORDS_I(lbits), lwp);
lsb = (lsb < 1) ? 0 : (lsb - 1);
}
std::string append;
int digits;
switch (fmt) {
case 'b': {
digits = lsb + 1;
for (; lsb >= 0; --lsb) append += (VL_BITRSHIFT_W(lwp, lsb) & 1) + '0';
break;
}
case 'o': {
digits = (lsb + 1 + 2) / 3;
for (; lsb >= 0; --lsb) {
lsb = (lsb / 3) * 3; // Next digit
// Octal numbers may span more than one wide word,
// so we need to grab each bit separately and check for overrun
// Octal is rare, so we'll do it a slow simple way
append += static_cast<char>(
'0' + ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 0)) ? 1 : 0)
+ ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 1)) ? 2 : 0)
+ ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 2)) ? 4 : 0));
}
break;
}
default: { // 'x'
digits = (lsb + 1 + 3) / 4;
for (; lsb >= 0; --lsb) {
lsb = (lsb / 4) * 4; // Next digit
const IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xf;
append += "0123456789abcdef"[charval];
}
break;
}
} // switch
const int needmore = width - digits;
if (needmore > 0) {
std::string padding;
if (left) {
padding.append(needmore, ' '); // Pre-pad spaces
output += append + padding;
} else {
padding.append(needmore, '0'); // Pre-pad zero
output += padding + append;
}
} else {
output += append;
}
break;
} // b / o / x
case 'u':
case 'z': { // Packed 4-state
const bool is_4_state = (fmt == 'z');
@@ -973,13 +1039,6 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
}
}
break;
case 'x':
for (; lsb >= 0; --lsb) {
lsb = (lsb / 4) * 4; // Next digit
const IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xf;
output += "0123456789abcdef"[charval];
}
break;
default: { // LCOV_EXCL_START
const std::string msg = std::string{"Unknown _vl_vsformat code: "} + pos[0];
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
@@ -992,22 +1051,22 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
}
}
static inline bool _vl_vsss_eof(FILE* fp, int floc) VL_MT_SAFE {
static bool _vl_vsss_eof(FILE* fp, int floc) VL_MT_SAFE {
if (VL_LIKELY(fp)) {
return std::feof(fp) ? true : false; // true : false to prevent MSVC++ warning
} else {
return floc < 0;
}
}
static inline void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE {
static void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE {
if (VL_LIKELY(fp)) {
std::fgetc(fp);
} else {
floc -= 8;
}
}
static inline int _vl_vsss_peek(FILE* fp, int& floc, const WDataInP fromp,
const std::string& fstr) VL_MT_SAFE {
static int _vl_vsss_peek(FILE* fp, int& floc, const WDataInP fromp,
const std::string& fstr) VL_MT_SAFE {
// Get a character without advancing
if (VL_LIKELY(fp)) {
const int data = std::fgetc(fp);
@@ -1024,17 +1083,16 @@ static inline int _vl_vsss_peek(FILE* fp, int& floc, const WDataInP fromp,
}
}
}
static inline void _vl_vsss_skipspace(FILE* fp, int& floc, const WDataInP fromp,
const std::string& fstr) VL_MT_SAFE {
static void _vl_vsss_skipspace(FILE* fp, int& floc, const WDataInP fromp,
const std::string& fstr) VL_MT_SAFE {
while (true) {
const int c = _vl_vsss_peek(fp, floc, fromp, fstr);
if (c == EOF || !std::isspace(c)) return;
_vl_vsss_advance(fp, floc);
}
}
static inline void _vl_vsss_read_str(FILE* fp, int& floc, const WDataInP fromp,
const std::string& fstr, char* tmpp,
const char* acceptp) VL_MT_SAFE {
static void _vl_vsss_read_str(FILE* fp, int& floc, const WDataInP fromp, const std::string& fstr,
char* tmpp, const char* acceptp) VL_MT_SAFE {
// Read into tmp, consisting of characters from acceptp list
char* cp = tmpp;
while (true) {
@@ -1048,9 +1106,8 @@ static inline void _vl_vsss_read_str(FILE* fp, int& floc, const WDataInP fromp,
*cp++ = '\0';
// VL_DBG_MSGF(" _read got='"<<tmpp<<"'\n");
}
static inline char* _vl_vsss_read_bin(FILE* fp, int& floc, const WDataInP fromp,
const std::string& fstr, char* beginp, std::size_t n,
const bool inhibit = false) {
static char* _vl_vsss_read_bin(FILE* fp, int& floc, const WDataInP fromp, const std::string& fstr,
char* beginp, std::size_t n, const bool inhibit = false) {
// Variant of _vl_vsss_read_str using the same underlying I/O functions but optimized
// specifically for block reads of N bytes (read operations are not demarcated by
// whitespace). In the fp case, except descriptor to have been opened in binary mode.
@@ -1062,12 +1119,11 @@ static inline char* _vl_vsss_read_bin(FILE* fp, int& floc, const WDataInP fromp,
}
return beginp;
}
static inline void _vl_vsss_setbit(WDataOutP owp, int obits, int lsb, int nbits,
IData ld) VL_MT_SAFE {
static void _vl_vsss_setbit(WDataOutP owp, int obits, int lsb, int nbits, IData ld) VL_MT_SAFE {
for (; nbits && lsb < obits; nbits--, lsb++, ld >>= 1) { VL_ASSIGNBIT_WI(lsb, owp, ld & 1); }
}
static inline void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp,
size_t posstart, size_t posend) VL_MT_SAFE {
static void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp,
size_t posstart, size_t posend) VL_MT_SAFE {
// Read in base "2^^baseLog2" digits from strp[posstart..posend-1] into owp of size obits.
int lsb = 0;
for (int i = 0, pos = static_cast<int>(posend) - 1;
@@ -1453,11 +1509,12 @@ void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...) VL_MT_SAFE {
void VL_SFORMAT_X(int obits_ignored, std::string& output, const char* formatp, ...) VL_MT_SAFE {
if (obits_ignored) {}
output = "";
std::string temp_output;
va_list ap;
va_start(ap, formatp);
_vl_vsformat(output, formatp, ap);
_vl_vsformat(temp_output, formatp, ap);
va_end(ap);
output = temp_output;
}
std::string VL_SFORMATF_NX(const char* formatp, ...) VL_MT_SAFE {
@@ -1606,8 +1663,8 @@ IData VL_SYSTEM_IW(int lhswords, const WDataInP lhsp) VL_MT_SAFE {
return code >> 8; // Want exit status
}
IData VL_TESTPLUSARGS_I(const char* formatp) VL_MT_SAFE {
const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(formatp);
IData VL_TESTPLUSARGS_I(const std::string& format) VL_MT_SAFE {
const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(format.c_str());
return match.empty() ? 0 : 1;
}
@@ -1840,8 +1897,7 @@ VlReadMem::VlReadMem(bool hex, int bits, const std::string& filename, QData star
, m_bits{bits}
, m_filename(filename) // Need () or GCC 4.8 false warning
, m_end{end}
, m_addr{start}
, m_linenum{0} {
, m_addr{start} {
m_fp = std::fopen(filename.c_str(), "r");
if (VL_UNLIKELY(!m_fp)) {
// We don't report the Verilog source filename as it slow to have to pass it down
@@ -1975,8 +2031,7 @@ void VlReadMem::setData(void* valuep, const std::string& rhs) {
VlWriteMem::VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end)
: m_hex{hex}
, m_bits{bits}
, m_addr{0} {
, m_bits{bits} {
if (VL_UNLIKELY(start > end)) {
VL_FATAL_MT(filename.c_str(), 0, "", "$writemem invalid address range");
return;
@@ -2158,29 +2213,6 @@ void VL_WRITEMEM_N(bool hex, // Hex format, else binary
//===========================================================================
// Timescale conversion
// Helper function for conversion of timescale strings
// Converts (1|10|100)(s|ms|us|ns|ps|fs) to power of then
int VL_TIME_STR_CONVERT(const char* strp) VL_PURE {
int scale = 0;
if (!strp) return 0;
if (*strp++ != '1') return 0;
while (*strp == '0') {
++scale;
++strp;
}
switch (*strp++) {
case 's': break;
case 'm': scale -= 3; break;
case 'u': scale -= 6; break;
case 'n': scale -= 9; break;
case 'p': scale -= 12; break;
case 'f': scale -= 15; break;
default: return 0;
}
if ((scale < 0) && (*strp++ != 's')) return 0;
if (*strp) return 0;
return scale;
}
static const char* vl_time_str(int scale) VL_PURE {
static const char* const names[]
= {"100s", "10s", "1s", "100ms", "10ms", "1ms", "100us", "10us", "1us",
@@ -2302,8 +2334,9 @@ void VerilatedContext::checkMagic(const VerilatedContext* contextp) {
}
VerilatedContext::Serialized::Serialized() {
m_timeunit = VL_TIME_UNIT; // Initial value until overriden by _Vconfigure
m_timeprecision = VL_TIME_PRECISION; // Initial value until overriden by _Vconfigure
constexpr int8_t picosecond = -12;
m_timeunit = picosecond; // Initial value until overriden by _Vconfigure
m_timeprecision = picosecond; // Initial value until overriden by _Vconfigure
}
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
@@ -2323,9 +2356,9 @@ std::string VerilatedContext::dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMut
return m_dumpfile;
}
std::string VerilatedContext::dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
const std::string out = dumpfile();
std::string out = dumpfile();
if (VL_UNLIKELY(out.empty())) {
VL_PRINTF_MT("%%Warning: $dumpvar ignored as not proceeded by $dumpfile\n");
VL_PRINTF_MT("%%Warning: $dumpvar ignored as not preceded by $dumpfile\n");
return "";
}
return out;
@@ -2393,23 +2426,25 @@ void VerilatedContext::timeunit(int value) VL_MT_SAFE {
}
void VerilatedContext::timeprecision(int value) VL_MT_SAFE {
if (value < 0) value = -value; // Stored as 0..15
const VerilatedLockGuard lock{m_mutex};
m_s.m_timeprecision = value;
#ifdef SYSTEMC_VERSION
const sc_time sc_res = sc_get_time_resolution();
int sc_prec = 99;
if (sc_res == sc_time(1, SC_SEC)) {
sc_prec = 0;
} else if (sc_res == sc_time(1, SC_MS)) {
sc_prec = 3;
} else if (sc_res == sc_time(1, SC_US)) {
sc_prec = 6;
} else if (sc_res == sc_time(1, SC_NS)) {
sc_prec = 9;
} else if (sc_res == sc_time(1, SC_PS)) {
sc_prec = 12;
} else if (sc_res == sc_time(1, SC_FS)) {
sc_prec = 15;
{
const VerilatedLockGuard lock{m_mutex};
m_s.m_timeprecision = value;
#ifdef SYSTEMC_VERSION
const sc_time sc_res = sc_get_time_resolution();
if (sc_res == sc_time(1, SC_SEC)) {
sc_prec = 0;
} else if (sc_res == sc_time(1, SC_MS)) {
sc_prec = 3;
} else if (sc_res == sc_time(1, SC_US)) {
sc_prec = 6;
} else if (sc_res == sc_time(1, SC_NS)) {
sc_prec = 9;
} else if (sc_res == sc_time(1, SC_PS)) {
sc_prec = 12;
} else if (sc_res == sc_time(1, SC_FS)) {
sc_prec = 15;
}
}
if (value != sc_prec) {
std::ostringstream msg;
@@ -2421,6 +2456,8 @@ void VerilatedContext::timeprecision(int value) VL_MT_SAFE {
const std::string msgs = msg.str();
VL_FATAL_MT("", 0, "", msgs.c_str());
}
#else
}
#endif
}
const char* VerilatedContext::timeunitString() const VL_MT_SAFE { return vl_time_str(timeunit()); }
@@ -2428,6 +2465,33 @@ const char* VerilatedContext::timeprecisionString() const VL_MT_SAFE {
return vl_time_str(timeprecision());
}
void VerilatedContext::threads(unsigned n) {
if (n == 0) VL_FATAL_MT(__FILE__, __LINE__, "", "%Error: Simulation threads must be >= 1");
if (m_threadPool) {
VL_FATAL_MT(
__FILE__, __LINE__, "",
"%Error: Cannot set simulation threads after the thread pool has been created.");
}
#if VL_THREADED
if (m_threads == n) return; // To avoid unnecessary warnings
m_threads = n;
const unsigned hardwareThreadsAvailable = std::thread::hardware_concurrency();
if (m_threads > hardwareThreadsAvailable) {
VL_PRINTF_MT("%%Warning: System has %u hardware threads but simulation thread count set "
"to %u. This will likely cause significant slowdown.\n",
hardwareThreadsAvailable, m_threads);
}
#else
if (n > 1) {
VL_PRINTF_MT("%%Warning: Verilator run-time library built without VL_THREADS. Ignoring "
"call to 'VerilatedContext::threads' with argument %u.\n",
n);
}
#endif
}
void VerilatedContext::commandArgs(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex) {
const VerilatedLockGuard lock{m_argMutex};
m_args.m_argVec.clear(); // Empty first, then add
@@ -2458,6 +2522,33 @@ void VerilatedContext::internalsDump() const VL_MT_SAFE {
VerilatedImp::userDump();
}
void VerilatedContext::addModel(VerilatedModel* modelp) {
threadPoolp(); // Ensure thread pool is created, so m_threads cannot change any more
if (modelp->threads() > m_threads) {
std::ostringstream msg;
msg << "VerilatedContext has " << m_threads << " threads but model '"
<< modelp->modelName() << "' (instantiated as '" << modelp->hierName()
<< "') was Verilated with --threads " << modelp->threads() << ".\n";
const std::string str = msg.str();
VL_FATAL_MT(__FILE__, __LINE__, modelp->hierName(), str.c_str());
}
}
VerilatedVirtualBase* VerilatedContext::threadPoolp() {
if (m_threads == 1) return nullptr;
#if VL_THREADED
if (!m_threadPool) m_threadPool.reset(new VlThreadPool{this, m_threads - 1});
#endif
return m_threadPool.get();
}
VerilatedVirtualBase*
VerilatedContext::enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&)) {
if (!m_executionProfiler) m_executionProfiler.reset(construct(*this));
return m_executionProfiler.get();
}
//======================================================================
// VerilatedContextImp:: Methods - command line
@@ -2767,7 +2858,7 @@ void Verilated::runFlushCallbacks() VL_MT_SAFE {
// When running internal code coverage (gcc --coverage, as opposed to
// verilator --coverage), dump coverage data to properly cover failing
// tests.
VL_GCOV_FLUSH();
VL_GCOV_DUMP();
}
void Verilated::addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE {
@@ -2797,7 +2888,7 @@ const char* Verilated::productVersion() VL_PURE { return VERILATOR_VERSION; }
void Verilated::nullPointerError(const char* filename, int linenum) VL_MT_SAFE {
// Slowpath - Called only on error
VL_FATAL_MT(filename, linenum, "", "Null pointer dereferenced");
VL_UNREACHABLE
VL_UNREACHABLE;
}
void Verilated::overWidthError(const char* signame) VL_MT_SAFE {
@@ -2805,7 +2896,7 @@ void Verilated::overWidthError(const char* signame) VL_MT_SAFE {
const std::string msg = (std::string{"Testbench C set input '"} + signame
+ "' to value that overflows what the signal's width can fit");
VL_FATAL_MT("unknown", 0, "", msg.c_str());
VL_UNREACHABLE
VL_UNREACHABLE;
}
void Verilated::mkdir(const char* dirname) VL_MT_UNSAFE {
@@ -2850,6 +2941,14 @@ void VerilatedImp::versionDump() VL_MT_SAFE {
VL_PRINTF_MT(" Version: %s %s\n", Verilated::productName(), Verilated::productVersion());
}
//===========================================================================
// VerilatedModel:: Methods
VerilatedModel::VerilatedModel(VerilatedContext& context)
: m_context{context} {}
std::unique_ptr<VerilatedTraceConfig> VerilatedModel::traceConfig() const { return nullptr; }
//===========================================================================
// VerilatedModule:: Methods
@@ -3048,3 +3147,18 @@ void VerilatedAssertOneThread::fatal_different() VL_MT_SAFE {
#endif
//===========================================================================
// VlDeleter:: Methods
void VlDeleter::deleteAll() {
while (true) {
VerilatedLockGuard lock{m_mutex};
if (m_newGarbage.empty()) break;
VerilatedLockGuard deleteLock{m_deleteMutex};
std::swap(m_newGarbage, m_toDelete);
lock.unlock(); // So destuctors can enqueue new objects
for (VlClass* const objp : m_toDelete) delete objp;
m_toDelete.clear();
}
}
//===========================================================================
+76 -20
View File
@@ -81,6 +81,7 @@
#endif
// clang-format on
class VerilatedContext;
class VerilatedContextImp;
class VerilatedContextImpData;
class VerilatedCovContext;
@@ -90,6 +91,9 @@ class VerilatedFstC;
class VerilatedFstSc;
class VerilatedScope;
class VerilatedScopeNameMap;
template <class, class>
class VerilatedTrace;
class VerilatedTraceConfig;
class VerilatedVar;
class VerilatedVarNameMap;
class VerilatedVcd;
@@ -160,7 +164,7 @@ public:
~VerilatedMutex() = default;
const VerilatedMutex& operator!() const { return *this; } // For -fthread_safety
/// Acquire/lock mutex
void lock() VL_ACQUIRE() {
void lock() VL_ACQUIRE() VL_MT_SAFE {
// Try to acquire the lock by spinning. If the wait is short,
// avoids a trap to the OS plus OS scheduler overhead.
if (VL_LIKELY(try_lock())) return; // Short circuit loop
@@ -172,9 +176,9 @@ public:
m_mutex.lock();
}
/// Release/unlock mutex
void unlock() VL_RELEASE() { m_mutex.unlock(); }
void unlock() VL_RELEASE() VL_MT_SAFE { m_mutex.unlock(); }
/// Try to acquire mutex. Returns true on success, and false on failure.
bool try_lock() VL_TRY_ACQUIRE(true) { return m_mutex.try_lock(); }
bool try_lock() VL_TRY_ACQUIRE(true) VL_MT_SAFE { return m_mutex.try_lock(); }
};
/// Lock guard for mutex (ala std::unique_lock), wrapped to allow -fthread_safety checks
@@ -186,16 +190,16 @@ private:
public:
/// Construct and hold given mutex lock until destruction or unlock()
explicit VerilatedLockGuard(VerilatedMutex& mutexr) VL_ACQUIRE(mutexr)
explicit VerilatedLockGuard(VerilatedMutex& mutexr) VL_ACQUIRE(mutexr) VL_MT_SAFE
: m_mutexr(mutexr) { // Need () or GCC 4.8 false warning
m_mutexr.lock();
}
/// Destruct and unlock the mutex
~VerilatedLockGuard() VL_RELEASE() { m_mutexr.unlock(); }
/// Unlock the mutex
void lock() VL_ACQUIRE() { m_mutexr.lock(); }
void lock() VL_ACQUIRE() VL_MT_SAFE { m_mutexr.lock(); }
/// Lock the mutex
void unlock() VL_RELEASE() { m_mutexr.unlock(); }
void unlock() VL_RELEASE() VL_MT_SAFE { m_mutexr.unlock(); }
};
#else // !VL_THREADED
@@ -252,6 +256,40 @@ public:
#endif
};
//=========================================================================
/// Base class of a Verilator generated (Verilated) model.
///
/// VerilatedModel is a base class of the user facing primary class generated
/// by Verilator.
class VerilatedModel VL_NOT_FINAL {
VL_UNCOPYABLE(VerilatedModel);
VerilatedContext& m_context; // The VerilatedContext this model is instantiated under
protected:
explicit VerilatedModel(VerilatedContext& context);
virtual ~VerilatedModel() = default;
public:
/// Returns the VerilatedContext this model is instantiated under
/// Used to get to e.g. simulation time via contextp()->time()
VerilatedContext* contextp() const { return &m_context; }
/// Returns the hierarchical name of this module instance.
virtual const char* hierName() const = 0;
/// Returns the name of this model (the name of the generated model class).
virtual const char* modelName() const = 0;
/// Returns the thread level parallelism, this model was Verilated with. Always 1 or higher.
virtual unsigned threads() const = 0;
private:
// The following are for use by Verilator internals only
template <class, class>
friend class VerilatedTrace;
// Run-time trace configuration requested by this model
virtual std::unique_ptr<VerilatedTraceConfig> traceConfig() const;
};
//=========================================================================
/// Base class for all Verilated module classes.
@@ -266,10 +304,6 @@ public:
const char* name() const { return m_namep; } ///< Return name of module
};
/// Declare a module, ala SC_MODULE
#define VL_MODULE(modname) class modname VL_NOT_FINAL : public VerilatedModule
// Not class final in VL_MODULE, as users might be abstracting our models (--hierarchical)
//=========================================================================
// Functions overridable by user defines
// (Internals however must use VL_PRINTF_MT, which calls these.)
@@ -362,6 +396,16 @@ protected:
// Implementation details
const std::unique_ptr<VerilatedContextImpData> m_impdatap;
// Number of threads to use for simulation (size of m_threadPool + 1 for main thread)
#ifdef VL_THREADED
unsigned m_threads = std::thread::hardware_concurrency();
#else
const unsigned m_threads = 1;
#endif
// The thread pool shared by all models added to this context
std::unique_ptr<VerilatedVirtualBase> m_threadPool;
// The execution profiler shared by all models added to this context
std::unique_ptr<VerilatedVirtualBase> m_executionProfiler;
// Coverage access
std::unique_ptr<VerilatedVirtualBase> m_coveragep; // Pointer for coveragep()
@@ -413,9 +457,9 @@ public:
VerilatedCovContext* coveragep() VL_MT_SAFE;
/// Set debug level
/// Debug is currently global, but for forward compatibility have a per-context method
static void debug(int val) VL_MT_SAFE;
static inline void debug(int val) VL_MT_SAFE;
/// Return debug level
static int debug() VL_MT_SAFE;
static inline int debug() VL_MT_SAFE;
/// Set current number of errors/assertions
void errorCount(int val) VL_MT_SAFE;
/// Increment current number of errors/assertions
@@ -477,7 +521,7 @@ public:
///
/// * Else, time comes from the legacy 'double sc_time_stamp()' which
/// must be a function defined by the user's wrapper.
uint64_t time() const VL_MT_SAFE;
inline uint64_t time() const VL_MT_SAFE;
/// Set current simulation time. See time() for side effect details
void time(uint64_t value) VL_MT_SAFE { m_s.m_time = value; }
/// Advance current simulation time. See time() for side effect details
@@ -495,6 +539,12 @@ public:
/// Get time precision as IEEE-standard text
const char* timeprecisionString() const VL_MT_SAFE;
/// Get number of threads used for simulation (including the main thread)
unsigned threads() const { return m_threads; }
/// Set number of threads used for simulation (including the main thread)
/// Can only be called before the thread pool is created (before first model is added).
void threads(unsigned n);
/// Allow traces to at some point be enabled (disables some optimizations)
void traceEverOn(bool flag) VL_MT_SAFE {
if (flag) calcUnusedSigs(true);
@@ -510,13 +560,20 @@ public:
/// releases - contact the authors before production use.
void scopesDump() const VL_MT_SAFE;
public: // But for internal use only
// METHODS - public but for internal use only
// Internal: access to implementation class
VerilatedContextImp* impp() { return reinterpret_cast<VerilatedContextImp*>(this); }
const VerilatedContextImp* impp() const {
return reinterpret_cast<const VerilatedContextImp*>(this);
}
void addModel(VerilatedModel*);
VerilatedVirtualBase* threadPoolp();
VerilatedVirtualBase*
enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&));
// Internal: $dumpfile
void dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
std::string dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
@@ -595,13 +652,13 @@ public: // But internals only - called from VerilatedModule's
const char* name() const { return m_namep; }
const char* identifier() const { return m_identifierp; }
int8_t timeunit() const { return m_timeunit; }
inline VerilatedSyms* symsp() const { return m_symsp; }
VerilatedSyms* symsp() const { return m_symsp; }
VerilatedVar* varFind(const char* namep) const VL_MT_SAFE_POSTINIT;
VerilatedVarNameMap* varsp() const VL_MT_SAFE_POSTINIT { return m_varsp; }
void scopeDump() const;
void* exportFindError(int funcnum) const;
static void* exportFindNullError(int funcnum) VL_MT_SAFE;
static inline void* exportFind(const VerilatedScope* scopep, int funcnum) VL_MT_SAFE {
static void* exportFind(const VerilatedScope* scopep, int funcnum) VL_MT_SAFE {
if (VL_UNLIKELY(!scopep)) return exportFindNullError(funcnum);
if (VL_LIKELY(funcnum < scopep->m_funcnumMax)) {
// m_callbacksp must be declared, as Max'es are > 0
@@ -670,7 +727,7 @@ public:
/// Return debug level
/// When multithreaded this may not immediately react to another thread
/// changing the level (no mutex)
static inline int debug() VL_MT_SAFE { return s_debug; }
static int debug() VL_MT_SAFE { return s_debug; }
#else
/// Return constant 0 debug level, so C++'s optimizer rips up
static constexpr int debug() VL_PURE { return 0; }
@@ -830,7 +887,6 @@ public:
}
#endif
public:
// METHODS - INTERNAL USE ONLY (but public due to what uses it)
// Internal: Create a new module name by concatenating two strings
// Returns pointer to thread-local static data (overwritten on next call)
@@ -877,8 +933,8 @@ private:
#endif
};
inline void VerilatedContext::debug(int val) VL_MT_SAFE { Verilated::debug(val); }
inline int VerilatedContext::debug() VL_MT_SAFE { return Verilated::debug(); }
void VerilatedContext::debug(int val) VL_MT_SAFE { Verilated::debug(val); }
int VerilatedContext::debug() VL_MT_SAFE { return Verilated::debug(); }
//=========================================================================
// Data Types
+6 -18
View File
@@ -27,6 +27,8 @@ CFG_CXXFLAGS_STD_NEWEST = @CFG_CXXFLAGS_STD_NEWEST@
CFG_CXXFLAGS_NO_UNUSED = @CFG_CXXFLAGS_NO_UNUSED@
# Compiler flags that turn on extra warnings
CFG_CXXFLAGS_WEXTRA = @CFG_CXXFLAGS_WEXTRA@
# Compiler flags that enable coroutine support
CFG_CXXFLAGS_COROUTINES = @CFG_CXXFLAGS_COROUTINES@
# Linker libraries for multithreading
CFG_LDLIBS_THREADS = @CFG_LDLIBS_THREADS@
@@ -142,23 +144,9 @@ ifneq ($(VM_THREADS),0)
endif
endif
ifneq ($(VM_TRACE_THREADS),0)
ifneq ($(VM_TRACE_THREADS),)
ifeq ($(findstring -DVL_THREADED,$(CPPFLAGS)),)
$(error VM_TRACE_THREADS requires VM_THREADS)
endif
CPPFLAGS += -DVL_TRACE_THREADED
VK_C11=1
VK_LIBS_THREADED=1
endif
endif
ifneq ($(VM_TRACE_FST_WRITER_THREAD),0)
ifneq ($(VM_TRACE_FST_WRITER_THREAD),)
CPPFLAGS += -DVL_TRACE_FST_WRITER_THREAD
VK_C11=1
VK_LIBS_THREADED=1
ifneq ($(VM_TIMING),0)
ifneq ($(VM_TIMING),)
CPPFLAGS += $(CFG_CXXFLAGS_COROUTINES)
endif
endif
@@ -187,7 +175,7 @@ VM_SLOW += $(VM_CLASSES_SLOW) $(VM_SUPPORT_SLOW)
VK_FAST_OBJS = $(addsuffix .o, $(VM_FAST))
VK_SLOW_OBJS = $(addsuffix .o, $(VM_SLOW))
VK_USER_OBJS = $(addsuffix .o, $(VM_USER_CLASSES))
VK_USER_OBJS = $(addsuffix .o, $(VM_USER_CLASSES))
# Note VM_GLOBAL_FAST and VM_GLOBAL_SLOW holds the files required from the
# run-time library. In practice everything is actually in VM_GLOBAL_FAST,
+13 -11
View File
@@ -22,8 +22,10 @@
//=============================================================================
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_cov.h"
#include "verilated.h"
#include "verilated_cov_key.h"
#include <deque>
@@ -69,22 +71,23 @@ public: // But only local to this file
// This isn't in the header file for auto-magic conversion because it
// inlines to too much code and makes compilation too slow.
template <class T> class VerilatedCoverItemSpec final : public VerilatedCovImpItem {
template <class T>
class VerilatedCoverItemSpec final : public VerilatedCovImpItem {
private:
// MEMBERS
T* m_countp; // Count value
public:
// METHODS
// cppcheck-suppress truncLongCastReturn
virtual uint64_t count() const override { return *m_countp; }
virtual void zero() const override { *m_countp = 0; }
uint64_t count() const override { return *m_countp; }
void zero() const override { *m_countp = 0; }
// CONSTRUCTORS
// cppcheck-suppress noExplicitConstructor
explicit VerilatedCoverItemSpec(T* countp)
: m_countp{countp} {
*m_countp = 0;
}
virtual ~VerilatedCoverItemSpec() override = default;
~VerilatedCoverItemSpec() override = default;
};
//=============================================================================
@@ -122,7 +125,7 @@ public:
protected:
friend class VerilatedCovContext;
virtual ~VerilatedCovImp() override { clearGuts(); }
~VerilatedCovImp() override { clearGuts(); }
private:
// PRIVATE METHODS
@@ -205,7 +208,7 @@ private:
// Forward to . so we have a whole word
const std::string suffix = *bpost ? std::string{bpost + 1} : "";
const std::string out = prefix + "*" + suffix;
std::string out = prefix + "*" + suffix;
// cout << "\nch pre="<<prefix<<" s="<<suffix<<"\nch a="<<old<<"\nch b="<<add
// <<"\ncho="<<out<<endl;
@@ -354,9 +357,6 @@ public:
void write(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce();
const VerilatedLockGuard lock{m_mutex};
#ifndef VM_COVERAGE
VL_FATAL_MT("", 0, "", "%Error: Called VerilatedCov::write when VM_COVERAGE disabled");
#endif
selftest();
std::ofstream os{filename};
@@ -511,9 +511,11 @@ VerilatedCovContext* VerilatedCov::threadCovp() VL_MT_SAFE {
VerilatedCovContext* VerilatedContext::coveragep() VL_MT_SAFE {
static VerilatedMutex s_mutex;
// cppcheck-suppress identicalInnerCondition
if (VL_UNLIKELY(!m_coveragep)) {
const VerilatedLockGuard lock{s_mutex};
if (VL_LIKELY(!m_coveragep)) { // Not redundant, prevents race
// cppcheck-suppress identicalInnerCondition
if (VL_LIKELY(!m_coveragep)) { // LCOV_EXCL_LINE // Not redundant, prevents race
m_coveragep.reset(new VerilatedCovImp);
}
}
+11 -21
View File
@@ -26,6 +26,7 @@
#define VERILATOR_VERILATED_COV_H_
#include "verilatedos.h"
#include "verilated.h"
#include <iostream>
@@ -34,22 +35,6 @@
class VerilatedCovImp;
//=============================================================================
/// Conditionally compile statements only when doing coverage (when
/// VM_COVERAGE is defined)
// clang-format off
#ifdef VM_COVERAGE
# define VL_IF_COVER(stmts) \
do { stmts; } while (false)
#else
# define VL_IF_COVER(stmts) \
do { \
if (false) { stmts; } \
} while (false)
#endif
// clang-format on
//=============================================================================
/// Insert a item for coverage analysis.
/// The first argument is a pointer to the count to be dumped.
@@ -82,13 +67,17 @@ class VerilatedCovImp;
/// }
#define VL_COVER_INSERT(covcontextp, countp, ...) \
VL_IF_COVER(covcontextp->_inserti(countp); covcontextp->_insertf(__FILE__, __LINE__); \
covcontextp->_insertp("hier", name(), __VA_ARGS__))
do { \
covcontextp->_inserti(countp); \
covcontextp->_insertf(__FILE__, __LINE__); \
covcontextp->_insertp("hier", name(), __VA_ARGS__); \
} while (false)
//=============================================================================
// Convert VL_COVER_INSERT value arguments to strings, is \internal
template <class T> std::string vlCovCvtToStr(const T& t) VL_PURE {
template <class T>
std::string vlCovCvtToStr(const T& t) VL_PURE {
std::ostringstream os;
os << t;
return os.str();
@@ -120,7 +109,8 @@ public:
/// Zero coverage points
void zero() VL_MT_SAFE;
public: // But Internal use only
// METHODS - public but Internal use only
// Insert a coverage item
// We accept from 1-30 key/value pairs, all as strings.
// Call _insert1, followed by _insert2 and _insert3
@@ -158,7 +148,7 @@ protected:
// CONSTRUCTORS
// Internal: Only made as part of VerilatedCovImp
VerilatedCovContext() = default;
virtual ~VerilatedCovContext() = default;
~VerilatedCovContext() override = default;
// METHODS
// Internal: access to implementation class
+3 -1
View File
@@ -28,7 +28,9 @@
#define VERILATOR_VERILATED_DPI_CPP_
#include "verilatedos.h"
#include "verilated_dpi.h"
#include "verilated_imp.h"
// On MSVC++ we need svdpi.h to declare exports, not imports
@@ -179,7 +181,7 @@ void svPutPartselLogic(svLogicVecVal* dp, const svLogicVecVal s, int lbit, int w
//======================================================================
// Open array internals
static inline const VerilatedDpiOpenVar* _vl_openhandle_varp(const svOpenArrayHandle h) {
static const VerilatedDpiOpenVar* _vl_openhandle_varp(const svOpenArrayHandle h) {
if (VL_UNLIKELY(!h)) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"%%Error: DPI svOpenArrayHandle function called with nullptr handle");
+1
View File
@@ -27,6 +27,7 @@
#define VERILATOR_VERILATED_DPI_H_
#include "verilatedos.h"
#include "verilated.h" // Also presumably included by caller
#include "verilated_sym_props.h"
+25 -15
View File
@@ -28,7 +28,7 @@
#include "verilated_fst_c.h"
// GTKWave configuration
#ifdef VL_TRACE_FST_WRITER_THREAD
#ifdef VL_THREADED
# define HAVE_LIBPTHREAD
# define FST_WRITER_PARALLEL
#endif
@@ -92,8 +92,7 @@ static_assert(static_cast<int>(FST_ST_VCD_PROGRAM) == static_cast<int>(VLT_TRACE
//=============================================================================
// VerilatedFst
VerilatedFst::VerilatedFst(void* fst)
: m_fst{fst} {}
VerilatedFst::VerilatedFst(void* /*fst*/) {}
VerilatedFst::~VerilatedFst() {
if (m_fst) fstWriterClose(m_fst);
@@ -106,9 +105,7 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
m_fst = fstWriterCreate(filename, 1);
fstWriterSetPackType(m_fst, FST_WR_PT_LZ4);
fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str()); // lintok-begin-on-ref
#ifdef VL_TRACE_FST_WRITER_THREAD
fstWriterSetParallelMode(m_fst, 1);
#endif
if (m_useFstWriterThread) fstWriterSetParallelMode(m_fst, 1);
fullDump(true); // First dump must be full for fst
m_curScope.clear();
@@ -250,23 +247,36 @@ void VerilatedFst::declDouble(uint32_t code, const char* name, int dtypenum, fst
//=============================================================================
// Get/commit trace buffer
VerilatedFstBuffer* VerilatedFst::getTraceBuffer() { return new VerilatedFstBuffer{*this}; }
VerilatedFst::Buffer* VerilatedFst::getTraceBuffer() {
#ifdef VL_THREADED
if (offload()) return new OffloadBuffer{*this};
#endif
return new Buffer{*this};
}
void VerilatedFst::commitTraceBuffer(VerilatedFstBuffer* bufp) {
#ifdef VL_TRACE_OFFLOAD
if (bufp->m_offloadBufferWritep) {
m_offloadBufferWritep = bufp->m_offloadBufferWritep;
return; // Buffer will be deleted by the offload thread
void VerilatedFst::commitTraceBuffer(VerilatedFst::Buffer* bufp) {
#ifdef VL_THREADED
if (offload()) {
OffloadBuffer* const offloadBufferp = static_cast<OffloadBuffer*>(bufp);
if (offloadBufferp->m_offloadBufferWritep) {
m_offloadBufferWritep = offloadBufferp->m_offloadBufferWritep;
return; // Buffer will be deleted by the offload thread
}
}
#endif
delete bufp;
}
//=============================================================================
// VerilatedFstBuffer implementation
// Configure
VerilatedFstBuffer::VerilatedFstBuffer(VerilatedFst& owner)
: VerilatedTraceBuffer<VerilatedFst, VerilatedFstBuffer>{owner} {}
void VerilatedFst::configure(const VerilatedTraceConfig& config) {
// If at least one model requests the FST writer thread, then use it
m_useFstWriterThread |= config.m_useFstWriterThread;
}
//=============================================================================
// VerilatedFstBuffer implementation
//=============================================================================
// Trace rendering primitives
+40 -25
View File
@@ -43,18 +43,20 @@ public:
using Super = VerilatedTrace<VerilatedFst, VerilatedFstBuffer>;
private:
friend Buffer; // Give the buffer access to the private bits
friend VerilatedFstBuffer; // Give the buffer access to the private bits
//=========================================================================
// FST specific internals
void* m_fst;
void* m_fst = nullptr;
std::map<uint32_t, fstHandle> m_code2symbol;
std::map<int, fstEnumHandle> m_local2fstdtype;
std::list<std::string> m_curScope;
fstHandle* m_symbolp = nullptr; // same as m_code2symbol, but as an array
char* m_strbuf = nullptr; // String buffer long enough to hold maxBits() chars
bool m_useFstWriterThread = false; // Whether to use the separate FST writer thread
// CONSTRUCTORS
VL_UNCOPYABLE(VerilatedFst);
void declare(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
@@ -65,15 +67,18 @@ protected:
// Implementation of VerilatedTrace interface
// Called when the trace moves forward to a new time point
virtual void emitTimeChange(uint64_t timeui) override;
void emitTimeChange(uint64_t timeui) override;
// Hooks called from VerilatedTrace
virtual bool preFullDump() override { return isOpen(); }
virtual bool preChangeDump() override { return isOpen(); }
bool preFullDump() override { return isOpen(); }
bool preChangeDump() override { return isOpen(); }
// Trace buffer management
virtual VerilatedFstBuffer* getTraceBuffer() override;
virtual void commitTraceBuffer(VerilatedFstBuffer*) override;
Buffer* getTraceBuffer() override;
void commitTraceBuffer(Buffer*) override;
// Configure sub-class
void configure(const VerilatedTraceConfig&) override;
public:
//=========================================================================
@@ -113,21 +118,31 @@ public:
#ifndef DOXYGEN
// Declare specialization here as it's used in VerilatedFstC just below
template <> void VerilatedFst::Super::dump(uint64_t time);
template <> void VerilatedFst::Super::set_time_unit(const char* unitp);
template <> void VerilatedFst::Super::set_time_unit(const std::string& unit);
template <> void VerilatedFst::Super::set_time_resolution(const char* unitp);
template <> void VerilatedFst::Super::set_time_resolution(const std::string& unit);
template <> void VerilatedFst::Super::dumpvars(int level, const std::string& hier);
template <>
void VerilatedFst::Super::dump(uint64_t time);
template <>
void VerilatedFst::Super::set_time_unit(const char* unitp);
template <>
void VerilatedFst::Super::set_time_unit(const std::string& unit);
template <>
void VerilatedFst::Super::set_time_resolution(const char* unitp);
template <>
void VerilatedFst::Super::set_time_resolution(const std::string& unit);
template <>
void VerilatedFst::Super::dumpvars(int level, const std::string& hier);
#endif
//=============================================================================
// VerilatedFstBuffer
class VerilatedFstBuffer final : public VerilatedTraceBuffer<VerilatedFst, VerilatedFstBuffer> {
class VerilatedFstBuffer VL_NOT_FINAL {
// Give the trace file access to the private bits
friend VerilatedFst;
friend VerilatedFst::Super;
friend VerilatedFst::Buffer;
friend VerilatedFst::OffloadBuffer;
VerilatedFst& m_owner; // Trace file owning this buffer. Required by subclasses.
// The FST file handle
void* const m_fst = m_owner.m_fst;
@@ -136,23 +151,23 @@ class VerilatedFstBuffer final : public VerilatedTraceBuffer<VerilatedFst, Veril
// String buffer long enough to hold maxBits() chars
char* const m_strbuf = m_owner.m_strbuf;
public:
// CONSTRUCTOR
explicit VerilatedFstBuffer(VerilatedFst& owner);
~VerilatedFstBuffer() = default;
explicit VerilatedFstBuffer(VerilatedFst& owner)
: m_owner{owner} {}
virtual ~VerilatedFstBuffer() = default;
//=========================================================================
// Implementation of VerilatedTraceBuffer interface
// Implementations of duck-typed methods for VerilatedTraceBuffer. These are
// called from only one place (the full* methods), so always inline them.
VL_ATTR_ALWINLINE inline void emitBit(uint32_t code, CData newval);
VL_ATTR_ALWINLINE inline void emitCData(uint32_t code, CData newval, int bits);
VL_ATTR_ALWINLINE inline void emitSData(uint32_t code, SData newval, int bits);
VL_ATTR_ALWINLINE inline void emitIData(uint32_t code, IData newval, int bits);
VL_ATTR_ALWINLINE inline void emitQData(uint32_t code, QData newval, int bits);
VL_ATTR_ALWINLINE inline void emitWData(uint32_t code, const WData* newvalp, int bits);
VL_ATTR_ALWINLINE inline void emitDouble(uint32_t code, double newval);
VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval);
VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits);
VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits);
VL_ATTR_ALWINLINE void emitIData(uint32_t code, IData newval, int bits);
VL_ATTR_ALWINLINE void emitQData(uint32_t code, QData newval, int bits);
VL_ATTR_ALWINLINE void emitWData(uint32_t code, const WData* newvalp, int bits);
VL_ATTR_ALWINLINE void emitDouble(uint32_t code, double newval);
};
//=============================================================================
@@ -217,7 +232,7 @@ public:
}
// Internal class access
inline VerilatedFst* spTrace() { return &m_sptrace; }
VerilatedFst* spTrace() { return &m_sptrace; }
};
#endif // guard
+2 -4
View File
@@ -22,6 +22,7 @@
//=============================================================================
#include "verilatedos.h"
#include "verilated_fst_sc.h"
//======================================================================
@@ -30,7 +31,7 @@
void VerilatedFstSc::open(const char* filename) {
if (!sc_core::sc_get_curr_simcontext()->elaboration_done()) {
vl_fatal(__FILE__, __LINE__, "VerilatedFstSc",
("%Error: VerilatedFstSc::open(\"" + std::string(filename)
("%Error: VerilatedFstSc::open(\"" + std::string{filename}
+ "\") is called before sc_core::sc_start(). "
"Run sc_core::sc_start(sc_core::SC_ZERO_TIME) before opening a wave file.")
.c_str());
@@ -63,9 +64,6 @@ void VerilatedFstSc::trace(const unsigned int&, const std::string&, const char**
DECL_TRACE_METHOD_B( unsigned short )
DECL_TRACE_METHOD_B( unsigned int )
DECL_TRACE_METHOD_B( unsigned long )
#ifdef SYSTEMC_64BIT_PATCHES
DECL_TRACE_METHOD_B( unsigned long long)
#endif
DECL_TRACE_METHOD_B( char )
DECL_TRACE_METHOD_B( short )
DECL_TRACE_METHOD_B( int )
+14 -13
View File
@@ -23,8 +23,9 @@
#define _VERILATED_FST_SC_H_ 1
#include "verilatedos.h"
#include "verilated_sc.h"
#include "verilated_fst_c.h"
#include "verilated_sc.h"
#include <string>
@@ -51,37 +52,39 @@ public:
}
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
}
virtual ~VerilatedFstSc() { close(); }
~VerilatedFstSc() override { close(); }
// METHODS
/// Called by SystemC simulate()
virtual void cycle(bool delta_cycle) {
void cycle(bool delta_cycle) override {
if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); }
}
// Override VerilatedFstC. Must be called after starting simulation.
virtual void open(const char* filename) /*override*/ VL_MT_SAFE;
// Note: this is not a virtual function in the base class, so no 'override'
virtual void open(const char* filename) VL_MT_SAFE;
private:
/// Fake outs for linker
#ifdef NC_SYSTEMC
// Cadence Incisive has these as abstract functions so we must create them
virtual void set_time_unit(int exponent10_seconds) {} // deprecated
void set_time_unit(int exponent10_seconds) override {} // deprecated
#endif
virtual void set_time_unit(double v, sc_time_unit tu) {} // LCOV_EXCL_LINE
void set_time_unit(double v, sc_time_unit tu) override {} // LCOV_EXCL_LINE
//--------------------------------------------------
// SystemC 2.1.v1
#define DECL_TRACE_METHOD_A(tp) virtual void trace(const tp& object, const std::string& name);
#define DECL_TRACE_METHOD_A(tp) void trace(const tp& object, const std::string& name) override;
#define DECL_TRACE_METHOD_B(tp) \
virtual void trace(const tp& object, const std::string& name, int width);
void trace(const tp& object, const std::string& name, int width) override;
virtual void write_comment(const std::string&);
virtual void trace(const unsigned int&, const std::string&, const char**);
void write_comment(const std::string&) override;
void trace(const unsigned int&, const std::string&, const char**) override;
// clang-format off
// Formatting matches that of sc_trace.h
// LCOV_EXCL_START
#if (SYSTEMC_VERSION >= 20171012)
DECL_TRACE_METHOD_A( sc_event )
DECL_TRACE_METHOD_A( sc_time )
@@ -95,9 +98,6 @@ private:
DECL_TRACE_METHOD_B( unsigned short )
DECL_TRACE_METHOD_B( unsigned int )
DECL_TRACE_METHOD_B( unsigned long )
#ifdef SYSTEMC_64BIT_PATCHES
DECL_TRACE_METHOD_B( unsigned long long)
#endif
DECL_TRACE_METHOD_B( char )
DECL_TRACE_METHOD_B( short )
DECL_TRACE_METHOD_B( int )
@@ -119,6 +119,7 @@ private:
DECL_TRACE_METHOD_A( sc_dt::sc_bv_base )
DECL_TRACE_METHOD_A( sc_dt::sc_lv_base )
// LCOV_EXCL_STOP
// clang-format on
#undef DECL_TRACE_METHOD_A
+27 -28
View File
@@ -146,7 +146,7 @@ extern IData VL_SYSTEM_IW(int lhswords, WDataInP const lhsp);
extern IData VL_SYSTEM_IQ(QData lhs);
inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); }
extern IData VL_TESTPLUSARGS_I(const char* formatp);
extern IData VL_TESTPLUSARGS_I(const std::string& format);
extern const char* vl_mc_scan_plusargs(const char* prefixp); // PLIish
//=========================================================================
@@ -256,25 +256,7 @@ extern void _vl_debug_print_w(int lbits, WDataInP const iwp);
//=========================================================================
// Pli macros
extern int VL_TIME_STR_CONVERT(const char* strp) VL_PURE;
// These are deprecated and used only to establish the default precision/units.
// Use Verilator timescale-override for better control.
// clang-format off
#ifndef VL_TIME_PRECISION
# ifdef VL_TIME_PRECISION_STR
# define VL_TIME_PRECISION VL_TIME_STR_CONVERT(VL_STRINGIFY(VL_TIME_PRECISION_STR))
# else
# define VL_TIME_PRECISION (-12) ///< Timescale default units if not in Verilog - picoseconds
# endif
#endif
#ifndef VL_TIME_UNIT
# ifdef VL_TIME_UNIT_STR
# define VL_TIME_UNIT VL_TIME_STR_CONVERT(VL_STRINGIFY(VL_TIME_PRECISION_STR))
# else
# define VL_TIME_UNIT (-12) ///< Timescale default units if not in Verilog - picoseconds
# endif
#endif
#if defined(SYSTEMC_VERSION)
/// Return current simulation time
@@ -300,7 +282,7 @@ inline uint64_t vl_time_stamp64() {
# endif
#endif
inline uint64_t VerilatedContext::time() const VL_MT_SAFE {
uint64_t VerilatedContext::time() const VL_MT_SAFE {
// When using non-default context, fastest path is return time
if (VL_LIKELY(m_s.m_time)) return m_s.m_time;
#if defined(SYSTEMC_VERSION) || (!defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY))
@@ -466,10 +448,17 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE {
{ \
const int words = VL_WORDS_I(obits); \
sc_biguint<(obits)> _butemp = (svar).read(); \
for (int i = 0; i < words; ++i) { \
int msb = ((i + 1) * VL_IDATASIZE) - 1; \
msb = (msb >= (obits)) ? ((obits)-1) : msb; \
(owp)[i] = _butemp.range(msb, i * VL_IDATASIZE).to_uint(); \
uint32_t* chunk = _butemp.get_raw(); \
int32_t lsb = 0; \
while (lsb < obits - BITS_PER_DIGIT) { \
const uint32_t data = *chunk; \
++chunk; \
_vl_insert_WI(owp.data(), data, lsb + BITS_PER_DIGIT - 1, lsb); \
lsb += BITS_PER_DIGIT; \
} \
if (lsb < obits) { \
const uint32_t msb_data = *chunk; \
_vl_insert_WI(owp.data(), msb_data, obits - 1, lsb); \
} \
(owp)[words - 1] &= VL_MASK_E(obits); \
}
@@ -510,13 +499,23 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE {
{ (svar).write(rd); }
#define VL_ASSIGN_SBQ(obits, svar, rd) \
{ (svar).write(rd); }
#define VL_SC_BITS_PER_DIGIT 30 // This comes from sc_nbdefs.h BITS_PER_DIGIT
#define VL_ASSIGN_SBW(obits, svar, rwp) \
{ \
sc_biguint<(obits)> _butemp; \
for (int i = 0; i < VL_WORDS_I(obits); ++i) { \
int msb = ((i + 1) * VL_IDATASIZE) - 1; \
msb = (msb >= (obits)) ? ((obits)-1) : msb; \
_butemp.range(msb, i* VL_IDATASIZE) = (rwp)[i]; \
int32_t lsb = 0; \
uint32_t* chunk = _butemp.get_raw(); \
while (lsb + VL_SC_BITS_PER_DIGIT < (obits)) { \
static_assert(std::is_same<IData, EData>::value, "IData and EData missmatch"); \
const uint32_t data = VL_SEL_IWII(lsb + VL_SC_BITS_PER_DIGIT + 1, (rwp).data(), lsb, \
VL_SC_BITS_PER_DIGIT); \
*chunk = data & VL_MASK_E(VL_SC_BITS_PER_DIGIT); \
++chunk; \
lsb += VL_SC_BITS_PER_DIGIT; \
} \
if (lsb < (obits)) { \
const uint32_t msb_data = VL_SEL_IWII((obits) + 1, (rwp).data(), lsb, (obits)-lsb); \
*chunk = msb_data & VL_MASK_E((obits)-lsb); \
} \
(svar).write(_butemp); \
}
+19 -24
View File
@@ -143,11 +143,10 @@ class VerilatedThreadMsgQueue final {
public:
// CONSTRUCTORS
VerilatedThreadMsgQueue() {}
~VerilatedThreadMsgQueue() {
// The only call of this with a non-empty queue is a fatal error.
// So this does not flush the queue, as the destination queue is not known to this class.
}
VerilatedThreadMsgQueue() = default;
~VerilatedThreadMsgQueue() = default;
// The only call of destructor with a non-empty queue is a fatal error.
// So this does not flush the queue, as the destination queue is not known to this class.
private:
VL_UNCOPYABLE(VerilatedThreadMsgQueue);
@@ -188,11 +187,11 @@ class VerilatedFpList final {
public:
using const_iterator = FILE* const*;
explicit VerilatedFpList() {}
explicit VerilatedFpList() = default;
const_iterator begin() const { return m_fp; }
const_iterator end() const { return m_fp + m_sz; }
std::size_t size() const { return m_sz; }
std::size_t capacity() const { return 31; }
static std::size_t capacity() { return 31; }
void push_back(FILE* fd) {
if (VL_LIKELY(size() < capacity())) m_fp[m_sz++] = fd;
}
@@ -236,12 +235,11 @@ class VerilatedContextImp final : VerilatedContext {
return s_s;
}
private:
public: // But only for verilated*.cpp
// CONSTRUCTORS - no data can live here, use only VerilatedContext
VerilatedContextImp() = delete;
~VerilatedContextImp() = delete;
public: // But only for verilated*.cpp
// METHODS - extending into VerilatedContext, call via impp()->
// Random seed handling
@@ -272,13 +270,12 @@ public: // But only for verilated*.cpp
std::string argPlusMatch(const char* prefixp) VL_MT_SAFE_EXCLUDES(m_argMutex);
std::pair<int, char**> argc_argv() VL_MT_SAFE_EXCLUDES(m_argMutex);
public: // But only for verilated*.cpp
// METHODS - scope name
// METHODS - scope name - INTERNAL only for verilated*.cpp
void scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE;
void scopeErase(const VerilatedScope* scopep) VL_MT_SAFE;
public: // But only for verilated*.cpp
// METHODS - file IO
// METHODS - file IO - INTERNAL only for verilated*.cpp
IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock{m_fdMutex};
if (m_fdFreeMct.empty()) return 0;
@@ -298,7 +295,7 @@ public: // But only for verilated*.cpp
const std::size_t start = std::max<std::size_t>(31UL + 1UL + 3UL, m_fdps.size());
const std::size_t excess = 10;
m_fdps.resize(start + excess);
std::fill(m_fdps.begin() + start, m_fdps.end(), (FILE*)0);
std::fill(m_fdps.begin() + start, m_fdps.end(), static_cast<FILE*>(nullptr));
m_fdFree.resize(excess);
for (std::size_t i = 0, id = start; i < m_fdFree.size(); ++i, ++id) {
m_fdFree[i] = id;
@@ -344,7 +341,7 @@ public: // But only for verilated*.cpp
if (VL_UNLIKELY(idx <= 2)) return; // stdout/stdin/stderr
if (VL_UNLIKELY(!m_fdps[idx])) return; // Already free
std::fclose(m_fdps[idx]);
m_fdps[idx] = (FILE*)0;
m_fdps[idx] = nullptr;
m_fdFree.push_back(idx);
} else {
// MCD case
@@ -359,7 +356,7 @@ public: // But only for verilated*.cpp
}
}
}
inline FILE* fdToFp(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
FILE* fdToFp(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
if (VL_UNLIKELY(fdlist.size() != 1)) return nullptr;
@@ -464,13 +461,12 @@ public:
// METHODS - debug
static void versionDump() VL_MT_SAFE;
public:
// METHODS - user scope tracking
// We implement this as a single large map instead of one map per scope.
// There's often many more scopes than userdata's and thus having a ~48byte
// per map overhead * N scopes would take much more space and cache thrashing.
// As scopep's are pointers, this implicitly handles multiple Context's
static inline void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE {
static void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE {
const VerilatedLockGuard lock{s().m_userMapMutex};
const auto it = s().m_userMap.find(std::make_pair(scopep, userKey));
if (it != s().m_userMap.end()) {
@@ -479,14 +475,15 @@ public:
s().m_userMap.emplace(std::make_pair(scopep, userKey), userData);
}
}
static inline void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
static void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
const VerilatedLockGuard lock{s().m_userMapMutex};
const auto& it = vlstd::as_const(s().m_userMap).find(std::make_pair(scopep, userKey));
if (VL_UNLIKELY(it == s().m_userMap.end())) return nullptr;
return it->second;
}
public: // But only for verilated.cpp
// METHODS - But only for verilated.cpp
// Symbol table destruction cleans up the entries for each scope.
static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE {
// Slow ok - called once/scope on destruction, so we only iterate.
@@ -512,8 +509,7 @@ public: // But only for verilated.cpp
}
}
public: // But only for verilated*.cpp
// METHODS - hierarchy
// METHODS - hierarchy - only for verilated*.cpp
static void hierarchyAdd(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE {
// Slow ok - called at construction for VPI accessible elements
const VerilatedLockGuard lock{s().m_hierMapMutex};
@@ -534,8 +530,7 @@ public: // But only for verilated*.cpp
return &s().m_hierMap;
}
public: // But only for verilated*.cpp
// METHODS - export names
// METHODS - export names - only for verilated*.cpp
// Each function prototype is converted to a function number which we
// then use to index a 2D table also indexed by scope number, because we
+44 -10
View File
@@ -17,6 +17,7 @@
//=============================================================================
#include "verilatedos.h"
#include "verilated_profiler.h"
#if VL_THREADED
@@ -60,54 +61,87 @@ uint16_t VlExecutionRecord::getcpu() {
//=============================================================================
// VlExecutionProfiler implementation
template <size_t N> static size_t roundUptoMultipleOf(size_t value) {
template <size_t N>
static size_t roundUptoMultipleOf(size_t value) {
static_assert((N & (N - 1)) == 0, "'N' must be a power of 2");
size_t mask = N - 1;
return (value + mask) & ~mask;
}
VlExecutionProfiler::VlExecutionProfiler() {
VlExecutionProfiler::VlExecutionProfiler(VerilatedContext& context)
: m_context{context} {
// Setup profiling on main thread
setupThread(0);
}
void VlExecutionProfiler::configure(const VerilatedContext& context) {
void VlExecutionProfiler::configure() {
if (VL_UNLIKELY(m_enabled)) {
--m_windowCount;
if (VL_UNLIKELY(m_windowCount == context.profExecWindow())) {
if (VL_UNLIKELY(m_windowCount == m_context.profExecWindow())) {
VL_DEBUG_IF(VL_DBG_MSGF("+ profile start collection\n"););
clear(); // Clear the profile after the cache warm-up cycles.
m_tickBegin = VL_CPU_TICK();
} else if (VL_UNLIKELY(m_windowCount == 0)) {
const uint64_t tickEnd = VL_CPU_TICK();
VL_DEBUG_IF(VL_DBG_MSGF("+ profile end\n"););
const std::string& fileName = context.profExecFilename();
const std::string& fileName = m_context.profExecFilename();
dump(fileName.c_str(), tickEnd);
m_enabled = false;
}
return;
}
const uint64_t startReq = context.profExecStart() + 1; // + 1, so we can start at time 0
const uint64_t startReq = m_context.profExecStart() + 1; // + 1, so we can start at time 0
if (VL_UNLIKELY(m_lastStartReq < startReq && VL_TIME_Q() >= context.profExecStart())) {
if (VL_UNLIKELY(m_lastStartReq < startReq && VL_TIME_Q() >= m_context.profExecStart())) {
VL_DEBUG_IF(VL_DBG_MSGF("+ profile start warmup\n"););
VL_DEBUG_IF(assert(m_windowCount == 0););
m_enabled = true;
m_windowCount = context.profExecWindow() * 2;
m_windowCount = m_context.profExecWindow() * 2;
m_lastStartReq = startReq;
}
}
VerilatedVirtualBase* VlExecutionProfiler::construct(VerilatedContext& context) {
VlExecutionProfiler* const selfp = new VlExecutionProfiler{context};
#if VL_THREADED
if (VlThreadPool* const threadPoolp = static_cast<VlThreadPool*>(context.threadPoolp())) {
for (int i = 0; i < threadPoolp->numThreads(); ++i) {
// Data to pass to worker thread initialization
struct Data {
VlExecutionProfiler* const selfp;
const uint32_t threadId;
} data{selfp, static_cast<uint32_t>(i + 1)};
// Initialize worker thread
threadPoolp->workerp(i)->addTask(
[](void* userp, bool) {
Data* const datap = static_cast<Data*>(userp);
datap->selfp->setupThread(datap->threadId);
},
&data);
// Wait until initializationis complete
threadPoolp->workerp(i)->wait();
}
}
#endif
return selfp;
}
void VlExecutionProfiler::setupThread(uint32_t threadId) {
// Reserve some space in the thread-local profiling buffer, in order to try to avoid malloc
// while profiling.
t_trace.reserve(RESERVED_TRACE_CAPACITY);
// Register thread-local buffer in list of all buffers
bool exists;
{
const VerilatedLockGuard lock{m_mutex};
bool exists = !m_traceps.emplace(threadId, &t_trace).second;
assert(!exists);
exists = !m_traceps.emplace(threadId, &t_trace).second;
}
if (VL_UNLIKELY(exists)) {
VL_FATAL_MT(__FILE__, __LINE__, "", "multiple initialization of profiler on some thread");
}
}
+16 -14
View File
@@ -24,10 +24,6 @@
#include "verilatedos.h"
#ifndef VL_PROFILER
#error "verilated_profiler.h/cpp expects VL_PROFILER (from --prof-{exec, pgo}"
#endif
#include "verilated.h"
#include <array>
@@ -38,18 +34,18 @@
#include <vector>
class VlExecutionProfiler;
class VlThreadPool;
//=============================================================================
// Macros to simplify generated code
#define VL_EXEC_TRACE_ADD_RECORD(vlSymsp) \
if (VL_UNLIKELY((vlSymsp)->__Vm_executionProfiler.enabled())) \
(vlSymsp)->__Vm_executionProfiler.addRecord()
if (VL_UNLIKELY((vlSymsp)->__Vm_executionProfilerp->enabled())) \
(vlSymsp)->__Vm_executionProfilerp->addRecord()
//=============================================================================
// Return high-precision counter for profiling, or 0x0 if not available
VL_ATTR_ALWINLINE
inline QData VL_CPU_TICK() {
VL_ATTR_ALWINLINE QData VL_CPU_TICK() {
uint64_t val;
VL_GET_CPU_TICK(val);
return val;
@@ -136,7 +132,7 @@ static_assert(std::is_trivially_destructible<VlExecutionRecord>::value,
//=============================================================================
// VlExecutionProfiler is for collecting profiling data about model execution
class VlExecutionProfiler final {
class VlExecutionProfiler final : public VerilatedVirtualBase {
// CONSTANTS
// In order to try to avoid dynamic memory allocations during the actual profiling phase,
@@ -154,6 +150,7 @@ class VlExecutionProfiler final {
using ExecutionTrace = std::vector<VlExecutionRecord>;
// STATE
VerilatedContext& m_context; // The context this profiler is under
static VL_THREAD_LOCAL ExecutionTrace t_trace; // thread-local trace buffers
mutable VerilatedMutex m_mutex;
// Map from thread id to &t_trace of given thread
@@ -167,31 +164,36 @@ class VlExecutionProfiler final {
public:
// CONSTRUCTOR
VlExecutionProfiler();
explicit VlExecutionProfiler(VerilatedContext& context);
~VlExecutionProfiler() override = default;
// METHODS
// Is profiling enabled
inline bool enabled() const { return m_enabled; }
bool enabled() const { return m_enabled; }
// Append a trace record to the trace buffer of the current thread
inline VlExecutionRecord& addRecord() {
static VlExecutionRecord& addRecord() {
t_trace.emplace_back();
return t_trace.back();
}
// Configure profiler (called in beginning of 'eval')
void configure(const VerilatedContext&);
void configure();
// Setup profiling on a particular thread;
void setupThread(uint32_t threadId);
// Clear all profiling data
void clear() VL_MT_SAFE_EXCLUDES(m_mutex);
// Write profiling data into file
void dump(const char* filenamep, uint64_t tickEnd) VL_MT_SAFE_EXCLUDES(m_mutex);
// Passed to VerilatedContext to create the VlExecutionProfiler profiler instance
static VerilatedVirtualBase* construct(VerilatedContext& context);
};
//=============================================================================
// VlPgoProfiler is for collecting profiling data for PGO
template <std::size_t T_Entries> class VlPgoProfiler final {
template <std::size_t T_Entries>
class VlPgoProfiler final {
// TYPES
struct Record final {
const std::string m_name; // Hashed name of mtask/etc
+6 -4
View File
@@ -24,8 +24,10 @@
#define VERILATOR_VERILATED_SAVE_CPP_
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_save.h"
#include "verilated.h"
#include "verilated_imp.h"
#include <cerrno>
@@ -38,13 +40,13 @@
# include <unistd.h>
#endif
#ifndef O_LARGEFILE // For example on WIN32
#ifndef O_LARGEFILE // WIN32 headers omit this
# define O_LARGEFILE 0
#endif
#ifndef O_NONBLOCK
#ifndef O_NONBLOCK // WIN32 headers omit this
# define O_NONBLOCK 0
#endif
#ifndef O_CLOEXEC
#ifndef O_CLOEXEC // WIN32 headers omit this
# define O_CLOEXEC 0
#endif
// clang-format on
+11 -9
View File
@@ -23,6 +23,7 @@
#define VERILATOR_VERILATED_SAVE_C_H_
#include "verilatedos.h"
#include "verilated.h"
#include <string>
@@ -77,7 +78,7 @@ public:
virtual void flush() VL_MT_UNSAFE_ONE {}
/// Write data to stream
VerilatedSerialize& write(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
const uint8_t* __restrict dp = (const uint8_t* __restrict)datap;
const uint8_t* __restrict dp = static_cast<const uint8_t* __restrict>(datap);
while (size) {
bufferCheck();
size_t blk = size;
@@ -194,16 +195,16 @@ public:
/// Construct new object
VerilatedSave() = default;
/// Flush, close and destruct
virtual ~VerilatedSave() override { close(); }
~VerilatedSave() override { close(); }
// METHODS
/// Open the file; call isOpen() to see if errors
void open(const char* filenamep) VL_MT_UNSAFE_ONE;
/// Open the file; call isOpen() to see if errors
void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); }
/// Flush and close the file
virtual void close() override VL_MT_UNSAFE_ONE;
void close() override VL_MT_UNSAFE_ONE;
/// Flush data to file
virtual void flush() override VL_MT_UNSAFE_ONE;
void flush() override VL_MT_UNSAFE_ONE;
};
//=============================================================================
@@ -221,7 +222,7 @@ public:
/// Construct new object
VerilatedRestore() = default;
/// Flush, close and destruct
virtual ~VerilatedRestore() override { close(); }
~VerilatedRestore() override { close(); }
// METHODS
/// Open the file; call isOpen() to see if errors
@@ -229,9 +230,9 @@ public:
/// Open the file; call isOpen() to see if errors
void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); }
/// Close the file
virtual void close() override VL_MT_UNSAFE_ONE;
virtual void flush() override VL_MT_UNSAFE_ONE {}
virtual void fill() override VL_MT_UNSAFE_ONE;
void close() override VL_MT_UNSAFE_ONE;
void flush() override VL_MT_UNSAFE_ONE {}
void fill() override VL_MT_UNSAFE_ONE;
};
//=============================================================================
@@ -287,7 +288,8 @@ inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, std::string& r
uint32_t len = 0;
os >> len;
rhs.resize(len);
return os.read((void*)rhs.data(), len);
// C cast is required below
return os.read((void*)(rhs.data()), len);
}
VerilatedSerialize& operator<<(VerilatedSerialize& os, VerilatedContext* rhsp);
VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhsp);
-1
View File
@@ -134,7 +134,6 @@ public:
initUnpacked(ulims);
}
public:
~VerilatedVarProps() = default;
// METHODS
bool magicOk() const { return m_magic == MAGIC; }
+1
View File
@@ -30,6 +30,7 @@
#define VERILATOR_VERILATED_SYMS_H_
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_sym_props.h"
+34 -46
View File
@@ -22,11 +22,8 @@
//=============================================================================
#include "verilatedos.h"
#include "verilated_threads.h"
#ifdef VL_PROFILER
#include "verilated_profiler.h"
#endif
#include "verilated_threads.h"
#include <cstdio>
#include <memory>
@@ -51,68 +48,59 @@ VlMTaskVertex::VlMTaskVertex(uint32_t upstreamDepCount)
//=============================================================================
// VlWorkerThread
VlWorkerThread::VlWorkerThread(uint32_t threadId, VerilatedContext* contextp,
VlExecutionProfiler* profilerp)
VlWorkerThread::VlWorkerThread(VerilatedContext* contextp)
: m_ready_size{0}
, m_exiting{false}
, m_cthread{startWorker, this, threadId, profilerp}
, m_contextp{contextp} {}
, m_cthread{startWorker, this, contextp} {}
VlWorkerThread::~VlWorkerThread() {
m_exiting.store(true, std::memory_order_release);
wakeUp();
shutdown();
// The thread should exit; join it.
m_cthread.join();
}
static void shutdownTask(void*, bool) { // LCOV_EXCL_LINE
// Deliberately empty, we use the address of this function as a magic number
}
void VlWorkerThread::shutdown() { addTask(shutdownTask, nullptr); }
void VlWorkerThread::wait() {
// Enqueue a task that sets this flag. Execution is in-order so this ensures completion.
std::atomic<bool> flag{false};
addTask([](void* flagp, bool) { static_cast<std::atomic<bool>*>(flagp)->store(true); }, &flag);
// Spin wait
for (unsigned i = 0; i < VL_LOCK_SPINS; ++i) {
if (flag.load()) return;
VL_CPU_RELAX();
}
// Yield wait
while (!flag.load()) std::this_thread::yield();
}
void VlWorkerThread::workerLoop() {
ExecRec work;
work.m_fnp = nullptr;
// Wait for the first task without spinning, in case the thread is never actually used.
dequeWork</* SpinWait: */ false>(&work);
while (true) {
if (VL_LIKELY(!work.m_fnp)) dequeWork(&work);
// Do this here, not above, to avoid a race with the destructor.
if (VL_UNLIKELY(m_exiting.load(std::memory_order_acquire))) break;
if (VL_LIKELY(work.m_fnp)) {
work.m_fnp(work.m_selfp, work.m_evenCycle);
work.m_fnp = nullptr;
}
if (VL_UNLIKELY(work.m_fnp == shutdownTask)) break;
work.m_fnp(work.m_selfp, work.m_evenCycle);
// Wait for next task with spinning.
dequeWork</* SpinWait: */ true>(&work);
}
}
void VlWorkerThread::startWorker(VlWorkerThread* workerp, uint32_t threadId,
VlExecutionProfiler* profilerp) {
Verilated::threadContextp(workerp->m_contextp);
#ifdef VL_PROFILER
// Note: setupThread is not defined without VL_PROFILER, hence the #ifdef. Still, we might
// not be profiling execution (e.g.: PGO only), so profilerp might still be nullptr.
if (profilerp) profilerp->setupThread(threadId);
#endif
void VlWorkerThread::startWorker(VlWorkerThread* workerp, VerilatedContext* contextp) {
Verilated::threadContextp(contextp);
workerp->workerLoop();
}
//=============================================================================
// VlThreadPool
VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads,
VlExecutionProfiler* profiler) {
// --threads N passes nThreads=N-1, as the "main" threads counts as 1
++nThreads;
const unsigned cpus = std::thread::hardware_concurrency();
if (cpus < nThreads) {
static int warnedOnce = 0;
if (!warnedOnce++) {
VL_PRINTF_MT("%%Warning: System has %u CPUs but model Verilated with"
" --threads %d; may run slow.\n",
cpus, nThreads);
}
}
// Create worker threads
for (uint32_t threadId = 1; threadId < nThreads; ++threadId) {
m_workers.push_back(new VlWorkerThread{threadId, contextp, profiler});
}
VlThreadPool::VlThreadPool(VerilatedContext* contextp, unsigned nThreads) {
for (unsigned i = 0; i < nThreads; ++i) m_workers.push_back(new VlWorkerThread{contextp});
}
VlThreadPool::~VlThreadPool() {
+29 -31
View File
@@ -24,6 +24,7 @@
#define VERILATOR_VERILATED_THREADS_H_
#include "verilatedos.h"
#include "verilated.h" // for VerilatedMutex and clang annotations
#ifndef VL_THREADED
@@ -50,6 +51,9 @@
#endif
// clang-format on
class VlExecutionProfiler;
class VlThreadPool;
// VlMTaskVertex and VlThreadpool will work with multiple model class types.
// Since the type is opaque to VlMTaskVertex and VlThreadPool, represent it
// as a void* here.
@@ -99,7 +103,7 @@ public:
// Upstream mtasks must call this when they complete.
// Returns true when the current MTaskVertex becomes ready to execute,
// false while it's still waiting on more dependencies.
inline bool signalUpstreamDone(bool evenCycle) {
bool signalUpstreamDone(bool evenCycle) {
if (evenCycle) {
const uint32_t upstreamDepsDone
= 1 + m_upstreamDepsDone.fetch_add(1, std::memory_order_release);
@@ -112,11 +116,11 @@ public:
return (upstreamDepsDone_prev == 1);
}
}
inline bool areUpstreamDepsDone(bool evenCycle) const {
bool areUpstreamDepsDone(bool evenCycle) const {
const uint32_t target = evenCycle ? m_upstreamDepCount : 0;
return m_upstreamDepsDone.load(std::memory_order_acquire) == target;
}
inline void waitUntilUpstreamDone(bool evenCycle) const {
void waitUntilUpstreamDone(bool evenCycle) const {
unsigned ct = 0;
while (VL_UNLIKELY(!areUpstreamDepsDone(evenCycle))) {
VL_CPU_RELAX();
@@ -129,20 +133,14 @@ public:
}
};
class VlExecutionProfiler;
class VlThreadPool;
class VlWorkerThread final {
private:
// TYPES
struct ExecRec {
VlExecFnp m_fnp; // Function to execute
VlSelfP m_selfp; // Symbol table to execute
bool m_evenCycle; // Even/odd for flag alternation
ExecRec()
: m_fnp{nullptr}
, m_selfp{nullptr}
, m_evenCycle{false} {}
VlExecFnp m_fnp = nullptr; // Function to execute
VlSelfP m_selfp = nullptr; // Symbol table to execute
bool m_evenCycle = false; // Even/odd for flag alternation
ExecRec() = default;
ExecRec(VlExecFnp fnp, VlSelfP selfp, bool evenCycle)
: m_fnp{fnp}
, m_selfp{selfp}
@@ -162,26 +160,24 @@ private:
// Store the size atomically, so we can spin wait
std::atomic<size_t> m_ready_size;
std::atomic<bool> m_exiting; // Worker thread should exit
std::thread m_cthread; // Underlying C++ thread record
VerilatedContext* const m_contextp; // Context for spawned thread
VL_UNCOPYABLE(VlWorkerThread);
public:
// CONSTRUCTORS
explicit VlWorkerThread(uint32_t threadId, VerilatedContext* contextp,
VlExecutionProfiler* profilerp);
explicit VlWorkerThread(VerilatedContext* contextp);
~VlWorkerThread();
// METHODS
inline void dequeWork(ExecRec* workp) VL_MT_SAFE_EXCLUDES(m_mutex) {
template <bool SpinWait>
void dequeWork(ExecRec* workp) VL_MT_SAFE_EXCLUDES(m_mutex) {
// Spin for a while, waiting for new data
for (int i = 0; i < VL_LOCK_SPINS; ++i) {
if (VL_LIKELY(m_ready_size.load(std::memory_order_relaxed))) { //
break;
if VL_CONSTEXPR_CXX17 (SpinWait) {
for (unsigned i = 0; i < VL_LOCK_SPINS; ++i) {
if (VL_LIKELY(m_ready_size.load(std::memory_order_relaxed))) break;
VL_CPU_RELAX();
}
VL_CPU_RELAX();
}
VerilatedLockGuard lock{m_mutex};
while (m_ready.empty()) {
@@ -195,8 +191,7 @@ public:
m_ready.erase(m_ready.begin());
m_ready_size.fetch_sub(1, std::memory_order_relaxed);
}
inline void wakeUp() { addTask(nullptr, nullptr, false); }
inline void addTask(VlExecFnp fnp, VlSelfP selfp, bool evenCycle)
void addTask(VlExecFnp fnp, VlSelfP selfp, bool evenCycle = false)
VL_MT_SAFE_EXCLUDES(m_mutex) {
bool notify;
{
@@ -207,12 +202,15 @@ public:
}
if (notify) m_cv.notify_one();
}
void shutdown(); // Finish current tasks, then terminate thread
void wait(); // Blocks calling thread until all tasks complete in this thread
void workerLoop();
static void startWorker(VlWorkerThread* workerp, uint32_t threadId,
VlExecutionProfiler* profilerp);
static void startWorker(VlWorkerThread* workerp, VerilatedContext* contextp);
};
class VlThreadPool final {
class VlThreadPool final : public VerilatedVirtualBase {
// MEMBERS
std::vector<VlWorkerThread*> m_workers; // our workers
@@ -221,12 +219,12 @@ public:
// Construct a thread pool with 'nThreads' dedicated threads. The thread
// pool will create these threads and make them available to execute tasks
// via this->workerp(index)->addTask(...)
VlThreadPool(VerilatedContext* contextp, int nThreads, VlExecutionProfiler* profilerp);
~VlThreadPool();
VlThreadPool(VerilatedContext* contextp, unsigned nThreads);
~VlThreadPool() override;
// METHODS
inline int numThreads() const { return m_workers.size(); }
inline VlWorkerThread* workerp(int index) {
int numThreads() const { return m_workers.size(); }
VlWorkerThread* workerp(int index) {
assert(index >= 0);
assert(index < m_workers.size());
return m_workers[index];
+223
View File
@@ -0,0 +1,223 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Code available from: https://verilator.org
//
// Copyright 2022 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU Lesser
// General Public License Version 3 or the Perl Artistic License Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//=========================================================================
///
/// \file
/// \brief Verilated timing implementation code
///
/// This file must be compiled and linked against all Verilated objects
/// that use timing features.
///
/// See the internals documentation docs/internals.rst for details.
///
//=========================================================================
#include "verilated_timing.h"
//======================================================================
// VlCoroutineHandle:: Methods
void VlCoroutineHandle::resume() {
// Only null if we have a fork..join_any and one of the other child processes resumed the
// main process
if (VL_LIKELY(m_coro)) {
VL_DEBUG_IF(VL_DBG_MSGF(" Resuming: "); dump(););
m_coro();
m_coro = nullptr;
}
}
#ifdef VL_DEBUG
void VlCoroutineHandle::dump() const {
VL_PRINTF("Process waiting at %s:%d\n", m_fileline.filename(), m_fileline.lineno());
}
#endif
//======================================================================
// VlDelayScheduler:: Methods
#ifdef VL_DEBUG
void VlDelayScheduler::VlDelayedCoroutine::dump() const {
VL_DBG_MSGF(" Awaiting time %" PRIu64 ": ", m_timestep);
m_handle.dump();
}
#endif
void VlDelayScheduler::resume() {
#ifdef VL_DEBUG
VL_DEBUG_IF(dump(); VL_DBG_MSGF(" Resuming delayed processes\n"););
#endif
while (awaitingCurrentTime()) {
if (m_queue.front().m_timestep != m_context.time()) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"%Error: Encountered process that should've been resumed at an "
"earlier simulation time. Missed a time slot?");
}
// Move max element in the heap to the end
std::pop_heap(m_queue.begin(), m_queue.end());
VlCoroutineHandle handle = std::move(m_queue.back().m_handle);
m_queue.pop_back();
handle.resume();
}
}
uint64_t VlDelayScheduler::nextTimeSlot() const {
if (empty()) {
VL_FATAL_MT(__FILE__, __LINE__, "", "%Error: There is no next time slot scheduled");
}
return m_queue.front().m_timestep;
}
#ifdef VL_DEBUG
void VlDelayScheduler::dump() const {
if (m_queue.empty()) {
VL_DBG_MSGF(" No delayed processes:\n");
} else {
VL_DBG_MSGF(" Delayed processes:\n");
for (const auto& susp : m_queue) susp.dump();
}
}
#endif
//======================================================================
// VlTriggerScheduler:: Methods
void VlTriggerScheduler::resume(const char* eventDescription) {
#ifdef VL_DEBUG
VL_DEBUG_IF(dump(eventDescription);
VL_DBG_MSGF(" Resuming processes waiting for %s\n", eventDescription););
#endif
for (auto& susp : m_ready) susp.resume();
m_ready.clear();
commit(eventDescription);
}
void VlTriggerScheduler::commit(const char* eventDescription) {
#ifdef VL_DEBUG
if (!m_uncommitted.empty()) {
VL_DEBUG_IF(
VL_DBG_MSGF(" Committing processes waiting for %s:\n", eventDescription);
for (const auto& susp
: m_uncommitted) {
VL_DBG_MSGF(" - ");
susp.dump();
});
}
#endif
m_ready.reserve(m_ready.size() + m_uncommitted.size());
m_ready.insert(m_ready.end(), std::make_move_iterator(m_uncommitted.begin()),
std::make_move_iterator(m_uncommitted.end()));
m_uncommitted.clear();
}
#ifdef VL_DEBUG
void VlTriggerScheduler::dump(const char* eventDescription) const {
if (m_ready.empty()) {
VL_DBG_MSGF(" No ready processes waiting for %s\n", eventDescription);
} else {
for (const auto& susp : m_ready) {
VL_DBG_MSGF(" Ready processes waiting for %s:\n", eventDescription);
VL_DBG_MSGF(" - ");
susp.dump();
}
}
if (!m_uncommitted.empty()) {
VL_DBG_MSGF(" Uncommitted processes waiting for %s:\n", eventDescription);
for (const auto& susp : m_uncommitted) {
VL_DBG_MSGF(" - ");
susp.dump();
}
}
}
#endif
//======================================================================
// VlDynamicTriggerScheduler:: Methods
bool VlDynamicTriggerScheduler::evaluate() {
VL_DEBUG_IF(dump(););
std::swap(m_suspended, m_evaluated);
for (auto& coro : m_evaluated) coro.resume();
m_evaluated.clear();
return !m_triggered.empty();
}
void VlDynamicTriggerScheduler::doPostUpdates() {
VL_DEBUG_IF(if (!m_post.empty())
VL_DBG_MSGF(" Doing post updates for processes:\n"); //
for (const auto& susp
: m_post) {
VL_DBG_MSGF(" - ");
susp.dump();
});
for (auto& coro : m_post) coro.resume();
m_post.clear();
}
void VlDynamicTriggerScheduler::resume() {
VL_DEBUG_IF(if (!m_triggered.empty()) VL_DBG_MSGF(" Resuming processes:\n"); //
for (const auto& susp
: m_triggered) {
VL_DBG_MSGF(" - ");
susp.dump();
});
for (auto& coro : m_triggered) coro.resume();
m_triggered.clear();
}
#ifdef VL_DEBUG
void VlDynamicTriggerScheduler::dump() const {
if (m_suspended.empty()) {
VL_DBG_MSGF(" No suspended processes waiting for dynamic trigger evaluation\n");
} else {
for (const auto& susp : m_suspended) {
VL_DBG_MSGF(" Suspended processes waiting for dynamic trigger evaluation:\n");
VL_DBG_MSGF(" - ");
susp.dump();
}
}
}
#endif
//======================================================================
// VlForkSync:: Methods
void VlForkSync::done(const char* filename, int lineno) {
VL_DEBUG_IF(VL_DBG_MSGF(" Process forked at %s:%d finished\n", filename, lineno););
if (m_join->m_counter > 0) m_join->m_counter--;
if (m_join->m_counter == 0) m_join->m_susp.resume();
}
//======================================================================
// VlCoroutine:: Methods
VlCoroutine::VlPromise::~VlPromise() {
// Indicate to the return object that the coroutine has finished or been destroyed
if (m_corop) m_corop->m_promisep = nullptr;
// If there is a continuation, destroy it
if (m_continuation) m_continuation.destroy();
}
std::suspend_never VlCoroutine::VlPromise::final_suspend() noexcept {
// Indicate to the return object that the coroutine has finished
if (m_corop) {
m_corop->m_promisep = nullptr;
// Forget the return value, we won't need it and it won't be able to let us know if
// it's destroyed
m_corop = nullptr;
}
// If there is a continuation, resume it
if (m_continuation) {
m_continuation();
m_continuation = nullptr;
}
return {};
}
+429
View File
@@ -0,0 +1,429 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Code available from: https://verilator.org
//
// Copyright 2022 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU Lesser
// General Public License Version 3 or the Perl Artistic License Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
///
/// \file
/// \brief Verilated timing header
///
/// This file is included automatically by Verilator in some of the C++ files
/// it generates if timing features are used.
///
/// This file is not part of the Verilated public-facing API.
/// It is only for internal use.
///
/// See the internals documentation docs/internals.rst for details.
///
//*************************************************************************
#ifndef VERILATOR_VERILATED_TIMING_H_
#define VERILATOR_VERILATED_TIMING_H_
#include "verilated.h"
// clang-format off
// Some preprocessor magic to support both Clang and GCC coroutines with both libc++ and libstdc++
#if defined _LIBCPP_VERSION // libc++
# if __clang_major__ > 13 // Clang > 13 warns that coroutine types in std::experimental are deprecated
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-experimental-coroutine"
# endif
# include <experimental/coroutine>
namespace std {
using namespace experimental; // Bring std::experimental into the std namespace
}
#else
# if defined __clang__ && defined __GLIBCXX__
# define __cpp_impl_coroutine 1 // Clang doesn't define this, but it's needed for libstdc++
# endif
# include <coroutine>
# if __clang_major__ < 14
namespace std { // Bring coroutine library into std::experimental, as Clang < 14 expects it to be there
namespace experimental {
using namespace std;
}
}
# endif
#endif
// clang-format on
// Placeholder for compiling with --protect-ids
#define VL_UNKNOWN "<unknown>"
//=============================================================================
// VlFileLineDebug stores a SystemVerilog source code location. Used in VlCoroutineHandle for
// debugging purposes.
class VlFileLineDebug final {
// MEMBERS
#ifdef VL_DEBUG
const char* m_filename = nullptr;
int m_lineno = 0;
#endif
public:
// CONSTRUCTORS
// Construct
VlFileLineDebug() = default;
VlFileLineDebug(const char* filename, int lineno)
#ifdef VL_DEBUG
: m_filename{filename}
, m_lineno{lineno}
#endif
{
}
// METHODS
#ifdef VL_DEBUG
const char* filename() const { return m_filename; }
int lineno() const { return m_lineno; }
#endif
};
//=============================================================================
// VlCoroutineHandle is a non-copyable (but movable) coroutine handle. On resume, the handle is
// cleared, as we assume that either the coroutine has finished and deleted itself, or, if it got
// suspended, another VlCoroutineHandle was created to manage it.
class VlCoroutineHandle final {
VL_UNCOPYABLE(VlCoroutineHandle);
// MEMBERS
std::coroutine_handle<> m_coro; // The wrapped coroutine handle
VlFileLineDebug m_fileline;
public:
// CONSTRUCTORS
// Construct
VlCoroutineHandle()
: m_coro{nullptr} {}
VlCoroutineHandle(std::coroutine_handle<> coro, VlFileLineDebug fileline)
: m_coro{coro}
, m_fileline{fileline} {}
// Move the handle, leaving a nullptr
VlCoroutineHandle(VlCoroutineHandle&& moved)
: m_coro{std::exchange(moved.m_coro, nullptr)}
, m_fileline{moved.m_fileline} {}
// Destroy if the handle isn't null
~VlCoroutineHandle() {
// Usually these coroutines should get resumed; we only need to clean up if we destroy a
// model with some coroutines suspended
if (VL_UNLIKELY(m_coro)) m_coro.destroy();
}
// METHODS
// Move the handle, leaving a null handle
auto& operator=(VlCoroutineHandle&& moved) {
m_coro = std::exchange(moved.m_coro, nullptr);
return *this;
}
// Resume the coroutine if the handle isn't null
void resume();
#ifdef VL_DEBUG
void dump() const;
#endif
};
//=============================================================================
// VlDelayScheduler stores coroutines to be resumed at a certain simulation time. If the current
// time is equal to a coroutine's resume time, the coroutine gets resumed.
class VlDelayScheduler final {
// TYPES
struct VlDelayedCoroutine {
uint64_t m_timestep; // Simulation time when the coroutine should be resumed
VlCoroutineHandle m_handle; // The suspended coroutine to be resumed
// Comparison operator for std::push_heap(), std::pop_heap()
bool operator<(const VlDelayedCoroutine& other) const {
return m_timestep > other.m_timestep;
}
#ifdef VL_DEBUG
void dump() const;
#endif
};
using VlDelayedCoroutineQueue = std::vector<VlDelayedCoroutine>;
// MEMBERS
VerilatedContext& m_context;
VlDelayedCoroutineQueue m_queue; // Coroutines to be restored at a certain simulation time
public:
// CONSTRUCTORS
explicit VlDelayScheduler(VerilatedContext& context)
: m_context{context} {}
// METHODS
// Resume coroutines waiting for the current simulation time
void resume();
// Returns the simulation time of the next time slot (aborts if there are no delayed
// coroutines)
uint64_t nextTimeSlot() const;
// Are there no delayed coroutines awaiting?
bool empty() const { return m_queue.empty(); }
// Are there coroutines to resume at the current simulation time?
bool awaitingCurrentTime() const {
return !empty() && m_queue.front().m_timestep <= m_context.time();
}
#ifdef VL_DEBUG
void dump() const;
#endif
// Used by coroutines for co_awaiting a certain simulation time
auto delay(uint64_t delay, const char* filename = VL_UNKNOWN, int lineno = 0) {
struct Awaitable {
VlDelayedCoroutineQueue& queue;
uint64_t delay;
VlFileLineDebug fileline;
bool await_ready() const { return false; } // Always suspend
void await_suspend(std::coroutine_handle<> coro) {
queue.push_back({delay, VlCoroutineHandle{coro, fileline}});
// Move last element to the proper place in the max-heap
std::push_heap(queue.begin(), queue.end());
}
void await_resume() const {}
};
return Awaitable{m_queue, m_context.time() + delay, VlFileLineDebug{filename, lineno}};
}
};
//=============================================================================
// VlTriggerScheduler stores coroutines to be resumed by a trigger. It does not keep track of its
// trigger, relying on calling code to resume when appropriate. Coroutines are kept in two stages
// - 'uncommitted' and 'ready'. Whenever a coroutine is suspended, it lands in the 'uncommited'
// stage. Only when commit() is called, these coroutines get moved to the 'ready' stage. That's
// when they can be resumed. This is done to avoid resuming processes before they start waiting.
class VlTriggerScheduler final {
// TYPES
using VlCoroutineVec = std::vector<VlCoroutineHandle>;
// MEMBERS
VlCoroutineVec m_uncommitted; // Coroutines suspended before commit() was called
// (not resumable)
VlCoroutineVec m_ready; // Coroutines that can be resumed (all coros from m_uncommitted are
// moved here in commit())
public:
// METHODS
// Resumes all coroutines from the 'ready' stage
void resume(const char* eventDescription = VL_UNKNOWN);
// Moves all coroutines from m_uncommitted to m_ready
void commit(const char* eventDescription = VL_UNKNOWN);
// Are there no coroutines awaiting?
bool empty() const { return m_ready.empty() && m_uncommitted.empty(); }
#ifdef VL_DEBUG
void dump(const char* eventDescription) const;
#endif
// Used by coroutines for co_awaiting a certain trigger
auto trigger(const char* eventDescription = VL_UNKNOWN, const char* filename = VL_UNKNOWN,
int lineno = 0) {
VL_DEBUG_IF(VL_DBG_MSGF(" Suspending process waiting for %s at %s:%d\n",
eventDescription, filename, lineno););
struct Awaitable {
VlCoroutineVec& suspended; // Coros waiting on trigger
VlFileLineDebug fileline;
bool await_ready() const { return false; } // Always suspend
void await_suspend(std::coroutine_handle<> coro) {
suspended.emplace_back(coro, fileline);
}
void await_resume() const {}
};
return Awaitable{m_uncommitted, VlFileLineDebug{filename, lineno}};
}
};
//=============================================================================
// VlDynamicTriggerScheduler is used for cases where triggers cannot be statically referenced and
// evaluated. Coroutines that make use of this scheduler must adhere to a certain procedure:
// __Vtrigger = 0;
// <locals and inits required for trigger eval>
// while (!__Vtrigger) {
// co_await __VdynSched.evaluation();
// <pre updates>;
// __Vtrigger = <trigger eval>;
// [optionally] co_await __VdynSched.postUpdate();
// <post updates>;
// }
// co_await __VdynSched.resumption();
// The coroutines get resumed at trigger evaluation time, evaluate their local triggers, optionally
// await the post update step, and if the trigger is set, await proper resumption in the 'act' eval
// step.
class VlDynamicTriggerScheduler final {
// TYPES
using VlCoroutineVec = std::vector<VlCoroutineHandle>;
// MEMBERS
VlCoroutineVec m_suspended; // Suspended coroutines awaiting trigger evaluation
VlCoroutineVec m_evaluated; // Coroutines currently being evaluated (for evaluate())
VlCoroutineVec m_triggered; // Coroutines whose triggers were set, and are awaiting resumption
VlCoroutineVec m_post; // Coroutines awaiting the post update step (only relevant for triggers
// with destructive post updates, e.g. named events)
// METHODS
auto awaitable(VlCoroutineVec& queue, const char* filename, int lineno) {
struct Awaitable {
VlCoroutineVec& suspended; // Coros waiting on trigger
VlFileLineDebug fileline;
bool await_ready() const { return false; } // Always suspend
void await_suspend(std::coroutine_handle<> coro) {
suspended.emplace_back(coro, fileline);
}
void await_resume() const {}
};
return Awaitable{queue, VlFileLineDebug{filename, lineno}};
}
public:
// Evaluates all dynamic triggers (resumed coroutines that co_await evaluation())
bool evaluate();
// Runs post updates for all dynamic triggers (resumes coroutines that co_await postUpdate())
void doPostUpdates();
// Resumes all coroutines whose triggers are set (those that co_await resumption())
void resume();
#ifdef VL_DEBUG
void dump() const;
#endif
// Used by coroutines for co_awaiting trigger evaluation
auto evaluation(const char* eventDescription, const char* filename, int lineno) {
VL_DEBUG_IF(VL_DBG_MSGF(" Suspending process waiting for %s at %s:%d\n",
eventDescription, filename, lineno););
return awaitable(m_suspended, filename, lineno);
}
// Used by coroutines for co_awaiting the trigger post update step
auto postUpdate(const char* eventDescription, const char* filename, int lineno) {
VL_DEBUG_IF(
VL_DBG_MSGF(" Process waiting for %s at %s:%d awaiting the post update step\n",
eventDescription, filename, lineno););
return awaitable(m_post, filename, lineno);
}
// Used by coroutines for co_awaiting the resumption step (in 'act' eval)
auto resumption(const char* eventDescription, const char* filename, int lineno) {
VL_DEBUG_IF(VL_DBG_MSGF(" Process waiting for %s at %s:%d awaiting resumption\n",
eventDescription, filename, lineno););
return awaitable(m_triggered, filename, lineno);
}
};
//=============================================================================
// VlNow is a helper awaitable type that always suspends, and then immediately resumes a coroutine.
// Allows forcing the move of coroutine locals to the heap.
struct VlNow {
bool await_ready() const { return false; } // Always suspend
bool await_suspend(std::coroutine_handle<>) const { return false; } // Resume immediately
void await_resume() const {}
};
//=============================================================================
// VlForever is a helper awaitable type for suspending coroutines forever. Used for constant
// wait statements.
struct VlForever {
bool await_ready() const { return false; } // Always suspend
void await_suspend(std::coroutine_handle<> coro) const { coro.destroy(); }
void await_resume() const {}
};
//=============================================================================
// VlForkSync is used to manage fork..join and fork..join_any constructs.
class VlForkSync final {
// VlJoin stores the handle of a suspended coroutine that did a fork..join or fork..join_any.
// If the counter reaches 0, the suspended coroutine shall be resumed.
struct VlJoin {
size_t m_counter = 0; // When reaches 0, resume suspended coroutine
VlCoroutineHandle m_susp; // Coroutine to resume
};
// The join info is shared among all forked processes
std::shared_ptr<VlJoin> m_join;
public:
// Create the join object and set the counter to the specified number
void init(size_t count) { m_join.reset(new VlJoin{count, {}}); }
// Called whenever any of the forked processes finishes. If the join counter reaches 0, the
// main process gets resumed
void done(const char* filename = VL_UNKNOWN, int lineno = 0);
// Used by coroutines for co_awaiting a join
auto join(const char* filename = VL_UNKNOWN, int lineno = 0) {
assert(m_join);
VL_DEBUG_IF(
VL_DBG_MSGF(" Awaiting join of fork at: %s:%d\n", filename, lineno););
struct Awaitable {
const std::shared_ptr<VlJoin> join; // Join to await on
VlFileLineDebug fileline;
bool await_ready() { return join->m_counter == 0; } // Suspend if join still exists
void await_suspend(std::coroutine_handle<> coro) { join->m_susp = {coro, fileline}; }
void await_resume() const {}
};
return Awaitable{m_join, VlFileLineDebug{filename, lineno}};
}
};
//=============================================================================
// VlCoroutine
// Return value of a coroutine. Used for chaining coroutine suspension/resumption.
class VlCoroutine final {
private:
// TYPES
struct VlPromise {
std::coroutine_handle<> m_continuation; // Coroutine to resume after this one finishes
VlCoroutine* m_corop = nullptr; // Pointer to the coroutine return object
~VlPromise();
VlCoroutine get_return_object() { return {this}; }
// Never suspend at the start of the coroutine
std::suspend_never initial_suspend() const { return {}; }
// Never suspend at the end of the coroutine (thanks to this, the coroutine will clean up
// after itself)
std::suspend_never final_suspend() noexcept;
void unhandled_exception() const { std::abort(); }
void return_void() const {}
};
// MEMBERS
VlPromise* m_promisep; // The promise created for this coroutine
public:
// TYPES
using promise_type = VlPromise; // promise_type has to be public
// CONSTRUCTORS
// Construct
VlCoroutine(VlPromise* promisep)
: m_promisep{promisep} {
m_promisep->m_corop = this;
}
// Move. Update the pointers each time the return object is moved
VlCoroutine(VlCoroutine&& other)
: m_promisep{std::exchange(other.m_promisep, nullptr)} {
if (m_promisep) m_promisep->m_corop = this;
}
~VlCoroutine() {
// Indicate to the promise that the return object is gone
if (m_promisep) m_promisep->m_corop = nullptr;
}
// METHODS
// Suspend the awaiter if the coroutine is suspended (the promise exists)
bool await_ready() const noexcept { return !m_promisep; }
// Set the awaiting coroutine as the continuation of the current coroutine
void await_suspend(std::coroutine_handle<> coro) { m_promisep->m_continuation = coro; }
void await_resume() const noexcept {}
};
#endif // Guard
+169 -141
View File
@@ -24,21 +24,6 @@
// clang-format off
// In FST mode, VL_TRACE_THREADED enables offloading, but only if we also have
// the FST writer thread. This means with --trace-threads 1, we get the FST
// writer thread only, and with --trace-threads 2 we get offloading as well
#if defined(VL_TRACE_FST_WRITER_THREAD) && defined(VL_TRACE_THREADED)
# define VL_TRACE_OFFLOAD
#endif
// VCD tracing can happen fully in parallel
#if defined(VM_TRACE_VCD) && VM_TRACE_VCD && defined(VL_TRACE_THREADED)
# define VL_TRACE_PARALLEL
#endif
#if defined(VL_TRACE_PARALLEL) && defined(VL_TRACE_OFFLOAD)
# error "Cannot have VL_TRACE_PARALLEL and VL_TRACE_OFFLOAD together"
#endif
#include "verilated.h"
#include "verilated_trace_defs.h"
@@ -47,9 +32,10 @@
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#ifdef VL_TRACE_OFFLOAD
#ifdef VL_THREADED
# include <deque>
# include <thread>
#endif
@@ -57,14 +43,18 @@
// clang-format on
class VlThreadPool;
template <class T_Trace, class T_Buffer> class VerilatedTraceBuffer;
template <class T_Buffer>
class VerilatedTraceBuffer;
template <class T_Buffer>
class VerilatedTraceOffloadBuffer;
#ifdef VL_TRACE_OFFLOAD
#ifdef VL_THREADED
//=============================================================================
// Offloaded tracing
// A simple synchronized first in first out queue
template <class T> class VerilatedThreadQueue final { // LCOV_EXCL_LINE // lcov bug
template <class T>
class VerilatedThreadQueue final { // LCOV_EXCL_LINE // lcov bug
private:
mutable VerilatedMutex m_mutex; // Protects m_queue
std::condition_variable_any m_cv;
@@ -129,27 +119,47 @@ public:
};
#endif
//=============================================================================
// VerilatedTraceConfig
// Simple data representing trace configuration required by generated models.
class VerilatedTraceConfig final {
public:
const bool m_useParallel; // Use parallel tracing
const bool m_useOffloading; // Offloading trace rendering
const bool m_useFstWriterThread; // Use the separate FST writer thread
VerilatedTraceConfig(bool useParallel, bool useOffloading, bool useFstWriterThread)
: m_useParallel{useParallel}
, m_useOffloading{useOffloading}
, m_useFstWriterThread{useFstWriterThread} {}
};
//=============================================================================
// VerilatedTrace
// T_Trace is the format specific subclass of VerilatedTrace.
// T_Buffer is the format specific subclass of VerilatedTraceBuffer.
template <class T_Trace, class T_Buffer> class VerilatedTrace VL_NOT_FINAL {
// Give the buffer (both base and derived) access to the private bits
friend VerilatedTraceBuffer<T_Trace, T_Buffer>;
friend T_Buffer;
// T_Buffer is the format specific base class of VerilatedTraceBuffer.
template <class T_Trace, class T_Buffer>
class VerilatedTrace VL_NOT_FINAL {
public:
using Buffer = T_Buffer;
using Buffer = VerilatedTraceBuffer<T_Buffer>;
using OffloadBuffer = VerilatedTraceOffloadBuffer<T_Buffer>;
//=========================================================================
// Generic tracing internals
using initCb_t = void (*)(void*, T_Trace*, uint32_t); // Type of init callbacks
using dumpCb_t = void (*)(void*, Buffer*); // Type of dump callbacks
using dumpOffloadCb_t = void (*)(void*, OffloadBuffer*); // Type of offload dump callbacks
using cleanupCb_t = void (*)(void*, T_Trace*); // Type of cleanup callbacks
private:
// Give the buffer (both base and derived) access to the private bits
friend T_Buffer;
friend Buffer;
friend OffloadBuffer;
struct CallbackRecord {
// Note: would make these fields const, but some old STL implementations
// (the one in Ubuntu 14.04 with GCC 4.8.4 in particular) use the
@@ -158,6 +168,7 @@ private:
union { // The callback
initCb_t m_initCb;
dumpCb_t m_dumpCb;
dumpOffloadCb_t m_dumpOffloadCb;
cleanupCb_t m_cleanupCb;
};
void* m_userp; // The user pointer to pass to the callback (the symbol table)
@@ -167,12 +178,18 @@ private:
CallbackRecord(dumpCb_t cb, void* userp)
: m_dumpCb{cb}
, m_userp{userp} {}
CallbackRecord(dumpOffloadCb_t cb, void* userp)
: m_dumpOffloadCb{cb}
, m_userp{userp} {}
CallbackRecord(cleanupCb_t cb, void* userp)
: m_cleanupCb{cb}
, m_userp{userp} {}
};
#ifdef VL_TRACE_PARALLEL
bool m_offload = false; // Use the offload thread (ignored if !VL_THREADED)
bool m_parallel = false; // Use parallel tracing (ignored if !VL_THREADED)
#ifdef VL_THREADED
struct ParallelWorkerData {
const dumpCb_t m_cb; // The callback
void* const m_userp; // The use pointer to pass to the callback
@@ -194,19 +211,17 @@ private:
static void parallelWorkerTask(void*, bool);
#endif
using ParallelCallbackMap = std::unordered_map<VlThreadPool*, std::vector<CallbackRecord>>;
protected:
uint32_t* m_sigs_oldvalp = nullptr; // Previous value store
EData* m_sigs_enabledp = nullptr; // Bit vector of enabled codes (nullptr = all on)
private:
uint64_t m_timeLastDump = 0; // Last time we did a dump
std::vector<bool> m_sigs_enabledVec; // Staging for m_sigs_enabledp
std::vector<CallbackRecord> m_initCbs; // Routines to initialize tracing
ParallelCallbackMap m_fullCbs; // Routines to perform full dump
ParallelCallbackMap m_chgCbs; // Routines to perform incremental dump
std::vector<CallbackRecord> m_fullCbs; // Routines to perform full dump
std::vector<CallbackRecord> m_fullOffloadCbs; // Routines to perform offloaded full dump
std::vector<CallbackRecord> m_chgCbs; // Routines to perform incremental dump
std::vector<CallbackRecord> m_chgOffloadCbs; // Routines to perform offloaded incremental dump
std::vector<CallbackRecord> m_cleanupCbs; // Routines to call at the end of dump
std::vector<VlThreadPool*> m_threadPoolps; // All thread pools, in insertion order
bool m_fullDump = true; // Whether a full dump is required on the next call to 'dump'
uint32_t m_nextCode = 0; // Next code number to assign
uint32_t m_numSignals = 0; // Number of distinct signals
@@ -216,24 +231,27 @@ private:
char m_scopeEscape = '.';
double m_timeRes = 1e-9; // Time resolution (ns/ms etc)
double m_timeUnit = 1e-0; // Time units (ns/ms etc)
uint64_t m_timeLastDump = 0; // Last time we did a dump
bool m_didSomeDump = false; // Did at least one dump (i.e.: m_timeLastDump is valid)
VerilatedContext* m_contextp = nullptr; // The context used by the traced models
std::unordered_set<const VerilatedModel*> m_models; // The collection of models being traced
void addThreadPool(VlThreadPool* threadPoolp) VL_MT_SAFE_EXCLUDES(m_mutex);
void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord& cbRec)
void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord&& cbRec)
VL_MT_SAFE_EXCLUDES(m_mutex);
// Equivalent to 'this' but is of the sub-type 'T_Trace*'. Use 'self()->'
// to access duck-typed functions to avoid a virtual function call.
T_Trace* self() { return static_cast<T_Trace*>(this); }
void runParallelCallbacks(const ParallelCallbackMap& cbMap);
void runCallbacks(const std::vector<CallbackRecord>& cbVec);
void runOffloadedCallbacks(const std::vector<CallbackRecord>& cbVec);
// Flush any remaining data for this file
static void onFlush(void* selfp) VL_MT_UNSAFE_ONE;
// Close the file on termination
static void onExit(void* selfp) VL_MT_UNSAFE_ONE;
#ifdef VL_TRACE_OFFLOAD
#ifdef VL_THREADED
// Number of total offload buffers that have been allocated
uint32_t m_numOffloadBuffers = 0;
// Size of offload buffers
@@ -279,7 +297,6 @@ protected:
uint32_t numSignals() const { return m_numSignals; }
uint32_t maxBits() const { return m_maxBits; }
void fullDump(bool value) { m_fullDump = value; }
uint64_t timeLastDump() { return m_timeLastDump; }
double timeRes() const { return m_timeRes; }
double timeUnit() const { return m_timeUnit; }
@@ -300,6 +317,14 @@ protected:
void closeBase();
void flushBase();
#ifdef VL_THREADED
bool offload() const { return m_offload; }
bool parallel() const { return m_parallel; }
#else
static constexpr bool offload() { return false; }
static constexpr bool parallel() { return false; }
#endif
//=========================================================================
// Virtual functions to be provided by the format specific implementation
@@ -315,6 +340,9 @@ protected:
virtual Buffer* getTraceBuffer() = 0;
virtual void commitTraceBuffer(Buffer*) = 0;
// Configure sub-class
virtual void configure(const VerilatedTraceConfig&) = 0;
public:
//=========================================================================
// External interface to client code
@@ -341,9 +369,12 @@ public:
//=========================================================================
// Non-hot path internal interface to Verilator generated code
void addModel(VerilatedModel*) VL_MT_SAFE_EXCLUDES(m_mutex);
void addInitCb(initCb_t cb, void* userp) VL_MT_SAFE;
void addFullCb(dumpCb_t cb, void* userp, VlThreadPool* = nullptr) VL_MT_SAFE;
void addChgCb(dumpCb_t cb, void* userp, VlThreadPool* = nullptr) VL_MT_SAFE;
void addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
void addFullCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE;
void addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
void addChgCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE;
void addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE;
void scopeEscape(char flag) { m_scopeEscape = flag; }
@@ -355,33 +386,26 @@ public:
//=============================================================================
// VerilatedTraceBuffer
// T_Trace is the format specific subclass of VerilatedTrace.
// T_Buffer is the format specific subclass of VerilatedTraceBuffer.
// T_Buffer is the format specific base class of VerilatedTraceBuffer.
// The format-specific hot-path methods use duck-typing via T_Buffer for performance.
template <class T_Trace, class T_Buffer> class VerilatedTraceBuffer VL_NOT_FINAL {
friend T_Trace; // Give the trace file access to the private bits
template <class T_Buffer>
class VerilatedTraceBuffer VL_NOT_FINAL : public T_Buffer {
protected:
T_Trace& m_owner; // The VerilatedTrace subclass that owns this buffer
// Type of the owner trace file
using Trace = typename std::remove_cv<
typename std::remove_reference<decltype(T_Buffer::m_owner)>::type>::type;
// Previous value store
uint32_t* const m_sigs_oldvalp = m_owner.m_sigs_oldvalp;
// Bit vector of enabled codes (nullptr = all on)
EData* const m_sigs_enabledp = m_owner.m_sigs_enabledp;
static_assert(std::has_virtual_destructor<T_Buffer>::value, "");
static_assert(std::is_base_of<VerilatedTrace<Trace, T_Buffer>, Trace>::value, "");
#ifdef VL_TRACE_OFFLOAD
// Write pointer into current buffer
uint32_t* m_offloadBufferWritep = m_owner.m_offloadBufferWritep;
// End of offload buffer
uint32_t* const m_offloadBufferEndp = m_owner.m_offloadBufferEndp;
#endif
friend Trace; // Give the trace file access to the private bits
friend std::default_delete<VerilatedTraceBuffer<T_Buffer>>;
// Equivalent to 'this' but is of the sub-type 'T_Derived*'. Use 'self()->'
// to access duck-typed functions to avoid a virtual function call.
inline T_Buffer* self() { return static_cast<T_Buffer*>(this); }
uint32_t* const m_sigs_oldvalp; // Previous value store
EData* const m_sigs_enabledp; // Bit vector of enabled codes (nullptr = all on)
explicit VerilatedTraceBuffer(T_Trace& owner);
virtual ~VerilatedTraceBuffer() = default;
explicit VerilatedTraceBuffer(Trace& owner);
~VerilatedTraceBuffer() override = default;
public:
//=========================================================================
@@ -401,7 +425,7 @@ public:
// duck-typed void emitWData(uint32_t code, const WData* newvalp, int bits) = 0;
// duck-typed void emitDouble(uint32_t code, double newval) = 0;
VL_ATTR_ALWINLINE inline uint32_t* oldp(uint32_t code) { return m_sigs_oldvalp + code; }
VL_ATTR_ALWINLINE uint32_t* oldp(uint32_t code) { return m_sigs_oldvalp + code; }
// Write to previous value buffer value and emit trace entry.
void fullBit(uint32_t* oldp, CData newval);
@@ -412,93 +436,32 @@ public:
void fullWData(uint32_t* oldp, const WData* newvalp, int bits);
void fullDouble(uint32_t* oldp, double newval);
#ifdef VL_TRACE_OFFLOAD
// Offloaded tracing. Just dump everything in the offload buffer
inline void chgBit(uint32_t code, CData newval) {
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_BIT_0 | newval;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep += 2;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
inline void chgCData(uint32_t code, CData newval, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_CDATA;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep[2] = newval;
m_offloadBufferWritep += 3;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
inline void chgSData(uint32_t code, SData newval, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_SDATA;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep[2] = newval;
m_offloadBufferWritep += 3;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
inline void chgIData(uint32_t code, IData newval, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_IDATA;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep[2] = newval;
m_offloadBufferWritep += 3;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
inline void chgQData(uint32_t code, QData newval, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_QDATA;
m_offloadBufferWritep[1] = code;
*reinterpret_cast<QData*>(m_offloadBufferWritep + 2) = newval;
m_offloadBufferWritep += 4;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
inline void chgWData(uint32_t code, const WData* newvalp, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_WDATA;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep += 2;
for (int i = 0; i < (bits + 31) / 32; ++i) { *m_offloadBufferWritep++ = newvalp[i]; }
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
inline void chgDouble(uint32_t code, double newval) {
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_DOUBLE;
m_offloadBufferWritep[1] = code;
// cppcheck-suppress invalidPointerCast
*reinterpret_cast<double*>(m_offloadBufferWritep + 2) = newval;
m_offloadBufferWritep += 4;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
#define chgBit chgBitImpl
#define chgCData chgCDataImpl
#define chgSData chgSDataImpl
#define chgIData chgIDataImpl
#define chgQData chgQDataImpl
#define chgWData chgWDataImpl
#define chgDouble chgDoubleImpl
#endif
// In non-offload mode, these are called directly by the trace callbacks,
// and are called chg*. In offload mode, they are called by the worker
// thread and are called chg*Impl
// Check previous dumped value of signal. If changed, then emit trace entry
VL_ATTR_ALWINLINE inline void chgBit(uint32_t* oldp, CData newval) {
VL_ATTR_ALWINLINE void chgBit(uint32_t* oldp, CData newval) {
const uint32_t diff = *oldp ^ newval;
if (VL_UNLIKELY(diff)) fullBit(oldp, newval);
}
VL_ATTR_ALWINLINE inline void chgCData(uint32_t* oldp, CData newval, int bits) {
VL_ATTR_ALWINLINE void chgCData(uint32_t* oldp, CData newval, int bits) {
const uint32_t diff = *oldp ^ newval;
if (VL_UNLIKELY(diff)) fullCData(oldp, newval, bits);
}
VL_ATTR_ALWINLINE inline void chgSData(uint32_t* oldp, SData newval, int bits) {
VL_ATTR_ALWINLINE void chgSData(uint32_t* oldp, SData newval, int bits) {
const uint32_t diff = *oldp ^ newval;
if (VL_UNLIKELY(diff)) fullSData(oldp, newval, bits);
}
VL_ATTR_ALWINLINE inline void chgIData(uint32_t* oldp, IData newval, int bits) {
VL_ATTR_ALWINLINE void chgIData(uint32_t* oldp, IData newval, int bits) {
const uint32_t diff = *oldp ^ newval;
if (VL_UNLIKELY(diff)) fullIData(oldp, newval, bits);
}
VL_ATTR_ALWINLINE inline void chgQData(uint32_t* oldp, QData newval, int bits) {
VL_ATTR_ALWINLINE void chgQData(uint32_t* oldp, QData newval, int bits) {
const uint64_t diff = *reinterpret_cast<QData*>(oldp) ^ newval;
if (VL_UNLIKELY(diff)) fullQData(oldp, newval, bits);
}
VL_ATTR_ALWINLINE inline void chgWData(uint32_t* oldp, const WData* newvalp, int bits) {
VL_ATTR_ALWINLINE void chgWData(uint32_t* oldp, const WData* newvalp, int bits) {
for (int i = 0; i < (bits + 31) / 32; ++i) {
if (VL_UNLIKELY(oldp[i] ^ newvalp[i])) {
fullWData(oldp, newvalp, bits);
@@ -506,20 +469,85 @@ public:
}
}
}
VL_ATTR_ALWINLINE inline void chgDouble(uint32_t* oldp, double newval) {
VL_ATTR_ALWINLINE void chgDouble(uint32_t* oldp, double newval) {
// cppcheck-suppress invalidPointerCast
if (VL_UNLIKELY(*reinterpret_cast<double*>(oldp) != newval)) fullDouble(oldp, newval);
}
#ifdef VL_TRACE_OFFLOAD
#undef chgBit
#undef chgCData
#undef chgSData
#undef chgIData
#undef chgQData
#undef chgWData
#undef chgDouble
#endif
};
#ifdef VL_THREADED
//=============================================================================
// VerilatedTraceOffloadBuffer
// T_Buffer is the format specific base class of VerilatedTraceBuffer.
// The format-specific hot-path methods use duck-typing via T_Buffer for performance.
template <class T_Buffer>
class VerilatedTraceOffloadBuffer final : public VerilatedTraceBuffer<T_Buffer> {
using typename VerilatedTraceBuffer<T_Buffer>::Trace;
friend Trace; // Give the trace file access to the private bits
uint32_t* m_offloadBufferWritep; // Write pointer into current buffer
uint32_t* const m_offloadBufferEndp; // End of offload buffer
explicit VerilatedTraceOffloadBuffer(Trace& owner);
~VerilatedTraceOffloadBuffer() override = default;
public:
//=========================================================================
// Hot path internal interface to Verilator generated code
// Offloaded tracing. Just dump everything in the offload buffer
void chgBit(uint32_t code, CData newval) {
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_BIT_0 | newval;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep += 2;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
void chgCData(uint32_t code, CData newval, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_CDATA;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep[2] = newval;
m_offloadBufferWritep += 3;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
void chgSData(uint32_t code, SData newval, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_SDATA;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep[2] = newval;
m_offloadBufferWritep += 3;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
void chgIData(uint32_t code, IData newval, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_IDATA;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep[2] = newval;
m_offloadBufferWritep += 3;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
void chgQData(uint32_t code, QData newval, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_QDATA;
m_offloadBufferWritep[1] = code;
*reinterpret_cast<QData*>(m_offloadBufferWritep + 2) = newval;
m_offloadBufferWritep += 4;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
void chgWData(uint32_t code, const WData* newvalp, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_WDATA;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep += 2;
for (int i = 0; i < (bits + 31) / 32; ++i) { *m_offloadBufferWritep++ = newvalp[i]; }
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
void chgDouble(uint32_t code, double newval) {
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_DOUBLE;
m_offloadBufferWritep[1] = code;
// cppcheck-suppress invalidPointerCast
*reinterpret_cast<double*>(m_offloadBufferWritep + 2) = newval;
m_offloadBufferWritep += 4;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
};
#endif
#endif // guard
+279 -178
View File
@@ -26,7 +26,7 @@
#include "verilated_intrinsics.h"
#include "verilated_trace.h"
#ifdef VL_TRACE_PARALLEL
#ifdef VL_THREADED
# include "verilated_threads.h"
# include <list>
#endif
@@ -78,11 +78,12 @@ static std::string doubleToTimescale(double value) {
return valuestr; // Gets converted to string, so no ref to stack
}
#ifdef VL_TRACE_OFFLOAD
#ifdef VL_THREADED
//=========================================================================
// Buffer management
template <> uint32_t* VerilatedTrace<VL_SUB_T, VL_BUF_T>::getOffloadBuffer() {
template <>
uint32_t* VerilatedTrace<VL_SUB_T, VL_BUF_T>::getOffloadBuffer() {
uint32_t* bufferp;
// Some jitter is expected, so some number of alternative offlaod buffers are
// required, but don't allocate more than 8 buffers.
@@ -101,7 +102,8 @@ template <> uint32_t* VerilatedTrace<VL_SUB_T, VL_BUF_T>::getOffloadBuffer() {
return bufferp;
}
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::waitForOffloadBuffer(const uint32_t* buffp) {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::waitForOffloadBuffer(const uint32_t* buffp) {
// Slow path code only called on flush/shutdown, so use a simple algorithm.
// Collect buffers from worker and stash them until we get the one we want.
std::deque<uint32_t*> stash;
@@ -116,7 +118,8 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::waitForOffloadBuffer(const
//=========================================================================
// Worker thread
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
bool shutdown = false;
do {
@@ -127,7 +130,7 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
const uint32_t* readp = bufferp;
std::unique_ptr<VL_BUF_T> traceBufp; // We own the passed tracebuffer
std::unique_ptr<Buffer> traceBufp; // We own the passed tracebuffer
while (true) {
const uint32_t cmd = readp[0];
@@ -143,44 +146,44 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
// CHG_* commands
case VerilatedTraceOffloadCommand::CHG_BIT_0:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_0 " << top);
traceBufp->chgBitImpl(oldp, 0);
traceBufp->chgBit(oldp, 0);
continue;
case VerilatedTraceOffloadCommand::CHG_BIT_1:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_1 " << top);
traceBufp->chgBitImpl(oldp, 1);
traceBufp->chgBit(oldp, 1);
continue;
case VerilatedTraceOffloadCommand::CHG_CDATA:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_CDATA " << top);
// Bits stored in bottom byte of command
traceBufp->chgCDataImpl(oldp, *readp, top);
traceBufp->chgCData(oldp, *readp, top);
readp += 1;
continue;
case VerilatedTraceOffloadCommand::CHG_SDATA:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_SDATA " << top);
// Bits stored in bottom byte of command
traceBufp->chgSDataImpl(oldp, *readp, top);
traceBufp->chgSData(oldp, *readp, top);
readp += 1;
continue;
case VerilatedTraceOffloadCommand::CHG_IDATA:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_IDATA " << top);
// Bits stored in bottom byte of command
traceBufp->chgIDataImpl(oldp, *readp, top);
traceBufp->chgIData(oldp, *readp, top);
readp += 1;
continue;
case VerilatedTraceOffloadCommand::CHG_QDATA:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_QDATA " << top);
// Bits stored in bottom byte of command
traceBufp->chgQDataImpl(oldp, *reinterpret_cast<const QData*>(readp), top);
traceBufp->chgQData(oldp, *reinterpret_cast<const QData*>(readp), top);
readp += 2;
continue;
case VerilatedTraceOffloadCommand::CHG_WDATA:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_WDATA " << top);
traceBufp->chgWDataImpl(oldp, readp, top);
traceBufp->chgWData(oldp, readp, top);
readp += VL_WORDS_I(top);
continue;
case VerilatedTraceOffloadCommand::CHG_DOUBLE:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_DOUBLE " << top);
traceBufp->chgDoubleImpl(oldp, *reinterpret_cast<const double*>(readp));
traceBufp->chgDouble(oldp, *reinterpret_cast<const double*>(readp));
readp += 2;
continue;
@@ -196,7 +199,7 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
case VerilatedTraceOffloadCommand::TRACE_BUFFER:
VL_TRACE_OFFLOAD_DEBUG("Command TRACE_BUFFER " << top);
readp -= 1; // No code in this command, undo increment
traceBufp.reset(*reinterpret_cast<VL_BUF_T* const*>(readp));
traceBufp.reset(*reinterpret_cast<Buffer* const*>(readp));
readp += 2;
continue;
@@ -231,7 +234,8 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
} while (VL_LIKELY(!shutdown));
}
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::shutdownOffloadWorker() {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::shutdownOffloadWorker() {
// If the worker thread is not running, done..
if (!m_workerThread) return;
@@ -251,37 +255,45 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::shutdownOffloadWorker() {
//=============================================================================
// Life cycle
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::closeBase() {
#ifdef VL_TRACE_OFFLOAD
shutdownOffloadWorker();
while (m_numOffloadBuffers) {
delete[] m_offloadBuffersFromWorker.get();
--m_numOffloadBuffers;
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::closeBase() {
#ifdef VL_THREADED
if (offload()) {
shutdownOffloadWorker();
while (m_numOffloadBuffers) {
delete[] m_offloadBuffersFromWorker.get();
--m_numOffloadBuffers;
}
}
#endif
}
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::flushBase() {
#ifdef VL_TRACE_OFFLOAD
// Hand an empty buffer to the worker thread
uint32_t* const bufferp = getOffloadBuffer();
*bufferp = VerilatedTraceOffloadCommand::END;
m_offloadBuffersToWorker.put(bufferp);
// Wait for it to be returned. As the processing is in-order,
// this ensures all previous buffers have been processed.
waitForOffloadBuffer(bufferp);
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::flushBase() {
#ifdef VL_THREADED
if (offload()) {
// Hand an empty buffer to the worker thread
uint32_t* const bufferp = getOffloadBuffer();
*bufferp = VerilatedTraceOffloadCommand::END;
m_offloadBuffersToWorker.put(bufferp);
// Wait for it to be returned. As the processing is in-order,
// this ensures all previous buffers have been processed.
waitForOffloadBuffer(bufferp);
}
#endif
}
//=============================================================================
// Callbacks to run on global events
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush(void* selfp) {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush(void* selfp) {
// This calls 'flush' on the derived class (which must then get any mutex)
reinterpret_cast<VL_SUB_T*>(selfp)->flush();
}
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit(void* selfp) {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit(void* selfp) {
// This calls 'close' on the derived class (which must then get any mutex)
reinterpret_cast<VL_SUB_T*>(selfp)->close();
}
@@ -289,25 +301,26 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit(void* selfp) {
//=============================================================================
// VerilatedTrace
template <> VerilatedTrace<VL_SUB_T, VL_BUF_T>::VerilatedTrace() {
template <>
VerilatedTrace<VL_SUB_T, VL_BUF_T>::VerilatedTrace() {
set_time_unit(Verilated::threadContextp()->timeunitString());
set_time_resolution(Verilated::threadContextp()->timeprecisionString());
}
template <> VerilatedTrace<VL_SUB_T, VL_BUF_T>::~VerilatedTrace() {
template <>
VerilatedTrace<VL_SUB_T, VL_BUF_T>::~VerilatedTrace() {
if (m_sigs_oldvalp) VL_DO_CLEAR(delete[] m_sigs_oldvalp, m_sigs_oldvalp = nullptr);
if (m_sigs_enabledp) VL_DO_CLEAR(delete[] m_sigs_enabledp, m_sigs_enabledp = nullptr);
Verilated::removeFlushCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush, this);
Verilated::removeExitCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit, this);
#ifdef VL_TRACE_OFFLOAD
closeBase();
#endif
if (offload()) closeBase();
}
//=========================================================================
// Internals available to format specific implementations
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::traceInit() VL_MT_UNSAFE {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::traceInit() VL_MT_UNSAFE {
// Note: It is possible to re-open a trace file (VCD in particular),
// so we must reset the next code here, but it must have the same number
// of codes on re-open
@@ -355,17 +368,19 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::traceInit() VL_MT_UNSAFE {
Verilated::addFlushCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush, this);
Verilated::addExitCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit, this);
#ifdef VL_TRACE_OFFLOAD
// Compute offload buffer size. we need to be able to store a new value for
// each signal, which is 'nextCode()' entries after the init callbacks
// above have been run, plus up to 2 more words of metadata per signal,
// plus fixed overhead of 1 for a termination flag and 3 for a time stamp
// update.
m_offloadBufferSize = nextCode() + numSignals() * 2 + 4;
#ifdef VL_THREADED
if (offload()) {
// Compute offload buffer size. we need to be able to store a new value for
// each signal, which is 'nextCode()' entries after the init callbacks
// above have been run, plus up to 2 more words of metadata per signal,
// plus fixed overhead of 1 for a termination flag and 3 for a time stamp
// update.
m_offloadBufferSize = nextCode() + numSignals() * 2 + 4;
// Start the worker thread
m_workerThread.reset(
new std::thread{&VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain, this});
// Start the worker thread
m_workerThread.reset(
new std::thread{&VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain, this});
}
#endif
}
@@ -415,14 +430,16 @@ bool VerilatedTrace<VL_SUB_T, VL_BUF_T>::declCode(uint32_t code, const char* nam
//=========================================================================
// Internals available to format specific implementations
template <> std::string VerilatedTrace<VL_SUB_T, VL_BUF_T>::timeResStr() const {
template <>
std::string VerilatedTrace<VL_SUB_T, VL_BUF_T>::timeResStr() const {
return doubleToTimescale(m_timeRes);
}
//=========================================================================
// External interface to client code
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::set_time_unit(const char* unitp) VL_MT_SAFE {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::set_time_unit(const char* unitp) VL_MT_SAFE {
m_timeUnit = timescaleToDouble(unitp);
}
template <>
@@ -451,8 +468,8 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dumpvars(int level, const std::string&
}
}
#ifdef VL_TRACE_PARALLEL
template <> //
#ifdef VL_THREADED
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::parallelWorkerTask(void* datap, bool) {
ParallelWorkerData* const wdp = reinterpret_cast<ParallelWorkerData*>(datap);
// Run the task
@@ -463,7 +480,8 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::parallelWorkerTask(void* datap, bool) {
if (wdp->m_waiting) wdp->m_cv.notify_one();
}
template <> VL_ATTR_NOINLINE void VerilatedTrace<VL_SUB_T, VL_BUF_T>::ParallelWorkerData::wait() {
template <>
VL_ATTR_NOINLINE void VerilatedTrace<VL_SUB_T, VL_BUF_T>::ParallelWorkerData::wait() {
// Spin for a while, waiting for the buffer to become ready
for (int i = 0; i < VL_LOCK_SPINS; ++i) {
if (VL_LIKELY(m_ready.load(std::memory_order_relaxed))) return;
@@ -478,56 +496,70 @@ template <> VL_ATTR_NOINLINE void VerilatedTrace<VL_SUB_T, VL_BUF_T>::ParallelWo
#endif
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runParallelCallbacks(const ParallelCallbackMap& cbMap) {
for (VlThreadPool* threadPoolp : m_threadPoolps) {
#ifdef VL_TRACE_PARALLEL
// If tracing in parallel, dispatch to the thread pool (if exists)
if (threadPoolp && threadPoolp->numThreads()) {
// List of work items for thread (std::list, as ParallelWorkerData is not movable)
std::list<ParallelWorkerData> workerData;
// We use the whole pool + the main thread
const unsigned threads = threadPoolp->numThreads() + 1;
// Main thread executes all jobs with index % threads == 0
std::vector<ParallelWorkerData*> mainThreadWorkerData;
// The tracing callbacks to execute on this thread-pool
const auto& cbVec = cbMap.at(threadPoolp);
// Enuque all the jobs
for (unsigned i = 0; i < cbVec.size(); ++i) {
const CallbackRecord& cbr = cbVec[i];
// Always get the trace buffer on the main thread
Buffer* const bufp = getTraceBuffer();
// Create new work item
workerData.emplace_back(cbr.m_dumpCb, cbr.m_userp, bufp);
// Grab the new work item
ParallelWorkerData* const itemp = &workerData.back();
// Enqueue task to thread pool, or main thread
if (unsigned rem = i % threads) {
threadPoolp->workerp(rem - 1)->addTask(parallelWorkerTask, itemp, false);
} else {
mainThreadWorkerData.push_back(itemp);
}
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runCallbacks(const std::vector<CallbackRecord>& cbVec) {
#ifdef VL_THREADED
if (parallel()) {
// If tracing in parallel, dispatch to the thread pool
VlThreadPool* threadPoolp = static_cast<VlThreadPool*>(m_contextp->threadPoolp());
// List of work items for thread (std::list, as ParallelWorkerData is not movable)
std::list<ParallelWorkerData> workerData;
// We use the whole pool + the main thread
const unsigned threads = threadPoolp->numThreads() + 1;
// Main thread executes all jobs with index % threads == 0
std::vector<ParallelWorkerData*> mainThreadWorkerData;
// Enuque all the jobs
for (unsigned i = 0; i < cbVec.size(); ++i) {
const CallbackRecord& cbr = cbVec[i];
// Always get the trace buffer on the main thread
Buffer* const bufp = getTraceBuffer();
// Create new work item
workerData.emplace_back(cbr.m_dumpCb, cbr.m_userp, bufp);
// Grab the new work item
ParallelWorkerData* const itemp = &workerData.back();
// Enqueue task to thread pool, or main thread
if (unsigned rem = i % threads) {
threadPoolp->workerp(rem - 1)->addTask(parallelWorkerTask, itemp);
} else {
mainThreadWorkerData.push_back(itemp);
}
// Execute main thead jobs
for (ParallelWorkerData* const itemp : mainThreadWorkerData) {
parallelWorkerTask(itemp, false);
}
// Commit all trace buffers in order
for (ParallelWorkerData& item : workerData) {
// Wait until ready
item.wait();
// Commit the buffer
commitTraceBuffer(item.m_bufp);
}
continue;
}
#endif
// Fall back on sequential execution
for (const CallbackRecord& cbr : cbMap.at(threadPoolp)) {
Buffer* const traceBufferp = getTraceBuffer();
cbr.m_dumpCb(cbr.m_userp, traceBufferp);
commitTraceBuffer(traceBufferp);
// Execute main thead jobs
for (ParallelWorkerData* const itemp : mainThreadWorkerData) {
parallelWorkerTask(itemp, false);
}
// Commit all trace buffers in order
for (ParallelWorkerData& item : workerData) {
// Wait until ready
item.wait();
// Commit the buffer
commitTraceBuffer(item.m_bufp);
}
// Done
return;
}
#endif
// Fall back on sequential execution
for (const CallbackRecord& cbr : cbVec) {
Buffer* const traceBufferp = getTraceBuffer();
cbr.m_dumpCb(cbr.m_userp, traceBufferp);
commitTraceBuffer(traceBufferp);
}
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runOffloadedCallbacks(
const std::vector<CallbackRecord>& cbVec) {
// Fall back on sequential execution
#ifdef VL_THREADED
for (const CallbackRecord& cbr : cbVec) {
Buffer* traceBufferp = getTraceBuffer();
cbr.m_dumpOffloadCb(cbr.m_userp, static_cast<OffloadBuffer*>(traceBufferp));
commitTraceBuffer(traceBufferp);
}
#else
if (!cbVec.empty()) VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
#endif
}
template <>
@@ -536,13 +568,14 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
// This does get the mutex, but if multiple threads are trying to dump
// chances are the data being dumped will have other problems
const VerilatedLockGuard lock{m_mutex};
if (VL_UNCOVERABLE(m_timeLastDump && timeui <= m_timeLastDump)) { // LCOV_EXCL_START
if (VL_UNCOVERABLE(m_didSomeDump && timeui <= m_timeLastDump)) { // LCOV_EXCL_START
VL_PRINTF_MT("%%Warning: previous dump at t=%" PRIu64 ", requesting t=%" PRIu64
", dump call ignored\n",
m_timeLastDump, timeui);
return;
} // LCOV_EXCL_STOP
m_timeLastDump = timeui;
m_didSomeDump = true;
Verilated::quiesce();
@@ -553,35 +586,49 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
if (!preChangeDump()) return;
}
#ifdef VL_TRACE_OFFLOAD
// Currently only incremental dumps run on the worker thread
#ifdef VL_THREADED
uint32_t* bufferp = nullptr;
if (VL_LIKELY(!m_fullDump)) {
// Get the offload buffer we are about to fill
bufferp = getOffloadBuffer();
m_offloadBufferWritep = bufferp;
m_offloadBufferEndp = bufferp + m_offloadBufferSize;
#endif
if (offload()) {
#ifdef VL_THREADED
// Currently only incremental dumps run on the worker thread
if (VL_LIKELY(!m_fullDump)) {
// Get the offload buffer we are about to fill
bufferp = getOffloadBuffer();
m_offloadBufferWritep = bufferp;
m_offloadBufferEndp = bufferp + m_offloadBufferSize;
// Tell worker to update time point
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::TIME_CHANGE;
*reinterpret_cast<uint64_t*>(m_offloadBufferWritep + 1) = timeui;
m_offloadBufferWritep += 3;
// Tell worker to update time point
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::TIME_CHANGE;
*reinterpret_cast<uint64_t*>(m_offloadBufferWritep + 1) = timeui;
m_offloadBufferWritep += 3;
} else {
// Update time point
flushBase();
emitTimeChange(timeui);
}
#else
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
#endif
} else {
// Update time point
flushBase();
emitTimeChange(timeui);
}
#else
// Update time point
emitTimeChange(timeui);
#endif
// Run the callbacks
if (VL_UNLIKELY(m_fullDump)) {
m_fullDump = false; // No more need for next dump to be full
runParallelCallbacks(m_fullCbs);
if (offload()) {
runOffloadedCallbacks(m_fullOffloadCbs);
} else {
runCallbacks(m_fullCbs);
}
} else {
runParallelCallbacks(m_chgCbs);
if (offload()) {
runOffloadedCallbacks(m_chgOffloadCbs);
} else {
runCallbacks(m_chgCbs);
}
}
for (uint32_t i = 0; i < m_cleanupCbs.size(); ++i) {
@@ -589,14 +636,18 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
cbr.m_cleanupCb(cbr.m_userp, self());
}
#ifdef VL_TRACE_OFFLOAD
if (VL_LIKELY(bufferp)) {
#ifdef VL_THREADED
if (offload() && VL_LIKELY(bufferp)) {
// Mark end of the offload buffer we just filled
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::END;
// Assert no buffer overflow
assert(m_offloadBufferWritep - bufferp <= m_offloadBufferSize);
// Reset our pointers as we are giving up the buffer
m_offloadBufferWritep = nullptr;
m_offloadBufferEndp = nullptr;
// Pass it to the worker thread
m_offloadBuffersToWorker.put(bufferp);
}
@@ -607,58 +658,98 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
// Non-hot path internal interface to Verilator generated code
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addThreadPool(VlThreadPool* threadPoolp)
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addModel(VerilatedModel* modelp)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
for (VlThreadPool* const poolp : m_threadPoolps) {
if (poolp == threadPoolp) return;
const bool firstModel = m_models.empty();
const bool newModel = m_models.insert(modelp).second;
VerilatedContext* const contextp = modelp->contextp();
// Validate
if (!newModel) { // LCOV_EXCL_START
VL_FATAL_MT(__FILE__, __LINE__, "",
"The same model has already been added to this trace file");
}
m_threadPoolps.push_back(threadPoolp);
if (VL_UNCOVERABLE(m_contextp && contextp != m_contextp)) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"A trace file instance can only handle models from the same context");
}
if (VL_UNCOVERABLE(m_didSomeDump)) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"Cannot add models to a trace file if 'dump' has already been called");
} // LCOV_EXCL_STOP
// Keep hold of the context
m_contextp = contextp;
// Get the desired trace config from the model
const std::unique_ptr<VerilatedTraceConfig> configp = modelp->traceConfig();
#ifndef VL_THREADED
if (configp->m_useOffloading) {
VL_FATAL_MT(__FILE__, __LINE__, "", "Cannot use trace offloading without VL_THREADED");
}
#endif
// Configure trace base class
if (!firstModel) {
if (m_offload != configp->m_useOffloading) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"Either all or no models using the same trace file must use offloading");
}
}
m_offload = configp->m_useOffloading;
// If at least one model requests parallel tracing, then use it
m_parallel |= configp->m_useParallel;
if (VL_UNCOVERABLE(m_parallel && m_offload)) { // LCOV_EXCL_START
VL_FATAL_MT(__FILE__, __LINE__, "", "Cannot use parallel tracing with offloading");
} // LCOV_EXCL_STOP
// Configure format specific sub class
configure(*(configp.get()));
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCallbackRecord(std::vector<CallbackRecord>& cbVec,
CallbackRecord& cbRec)
CallbackRecord&& cbRec)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
if (VL_UNCOVERABLE(timeLastDump() != 0)) { // LCOV_EXCL_START
const std::string msg = (std::string{"Internal: "} + __FILE__ + "::" + __FUNCTION__
+ " called with already open file");
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
} // LCOV_EXCL_STOP
cbVec.push_back(cbRec);
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addInitCb(initCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_initCbs, cbr);
addCallbackRecord(m_initCbs, CallbackRecord{cb, userp});
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpCb_t cb, void* userp,
VlThreadPool* threadPoolp) VL_MT_SAFE {
CallbackRecord cbr{cb, userp};
addThreadPool(threadPoolp);
addCallbackRecord(m_fullCbs[threadPoolp], cbr);
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
addCallbackRecord(m_fullCbs, CallbackRecord{cb, userp});
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpCb_t cb, void* userp,
VlThreadPool* threadPoolp) VL_MT_SAFE {
CallbackRecord cbr{cb, userp};
addThreadPool(threadPoolp);
addCallbackRecord(m_chgCbs[threadPoolp], cbr);
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE {
addCallbackRecord(m_fullOffloadCbs, CallbackRecord{cb, userp});
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
addCallbackRecord(m_chgCbs, CallbackRecord{cb, userp});
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE {
addCallbackRecord(m_chgOffloadCbs, CallbackRecord{cb, userp});
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_cleanupCbs, cbr);
addCallbackRecord(m_cleanupCbs, CallbackRecord{cb, userp});
}
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::pushNamePrefix(const std::string& prefix) {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::pushNamePrefix(const std::string& prefix) {
m_namePrefixStack.push_back(m_namePrefixStack.back() + prefix);
}
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::popNamePrefix(unsigned count) {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::popNamePrefix(unsigned count) {
while (count--) m_namePrefixStack.pop_back();
assert(!m_namePrefixStack.empty());
}
@@ -756,83 +847,93 @@ static inline void cvtQDataToStr(char* dstp, QData value) {
//=========================================================================
// VerilatedTraceBuffer
template <> //
VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::VerilatedTraceBuffer(VL_SUB_T& owner)
: m_owner{owner} {
#ifdef VL_TRACE_OFFLOAD
if (m_offloadBufferWritep) {
using This = VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>*;
// Tack on the buffer address
static_assert(2 * sizeof(uint32_t) >= sizeof(This),
"This should be enough on all plafrorms");
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::TRACE_BUFFER;
*reinterpret_cast<This*>(m_offloadBufferWritep) = this;
m_offloadBufferWritep += 2;
}
#endif
}
template <>
VerilatedTraceBuffer<VL_BUF_T>::VerilatedTraceBuffer(Trace& owner)
: VL_BUF_T{owner}
, m_sigs_oldvalp{owner.m_sigs_oldvalp}
, m_sigs_enabledp{owner.m_sigs_enabledp} {}
// These functions must write the new value back into the old value store,
// and subsequently call the format specific emit* implementations. Note
// that this file must be included in the format specific implementation, so
// the emit* functions can be inlined for performance.
template <> //
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullBit(uint32_t* oldp, CData newval) {
template <>
void VerilatedTraceBuffer<VL_BUF_T>::fullBit(uint32_t* oldp, CData newval) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitBit(code, newval);
emitBit(code, newval);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullCData(uint32_t* oldp, CData newval, int bits) {
void VerilatedTraceBuffer<VL_BUF_T>::fullCData(uint32_t* oldp, CData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitCData(code, newval, bits);
emitCData(code, newval, bits);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullSData(uint32_t* oldp, SData newval, int bits) {
void VerilatedTraceBuffer<VL_BUF_T>::fullSData(uint32_t* oldp, SData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitSData(code, newval, bits);
emitSData(code, newval, bits);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullIData(uint32_t* oldp, IData newval, int bits) {
void VerilatedTraceBuffer<VL_BUF_T>::fullIData(uint32_t* oldp, IData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitIData(code, newval, bits);
emitIData(code, newval, bits);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullQData(uint32_t* oldp, QData newval, int bits) {
void VerilatedTraceBuffer<VL_BUF_T>::fullQData(uint32_t* oldp, QData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*reinterpret_cast<QData*>(oldp) = newval;
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitQData(code, newval, bits);
emitQData(code, newval, bits);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullWData(uint32_t* oldp, const WData* newvalp,
int bits) {
void VerilatedTraceBuffer<VL_BUF_T>::fullWData(uint32_t* oldp, const WData* newvalp, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
for (int i = 0; i < VL_WORDS_I(bits); ++i) oldp[i] = newvalp[i];
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitWData(code, newvalp, bits);
emitWData(code, newvalp, bits);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullDouble(uint32_t* oldp, double newval) {
void VerilatedTraceBuffer<VL_BUF_T>::fullDouble(uint32_t* oldp, double newval) {
const uint32_t code = oldp - m_sigs_oldvalp;
*reinterpret_cast<double*>(oldp) = newval;
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
// cppcheck-suppress invalidPointerCast
self()->emitDouble(code, newval);
emitDouble(code, newval);
}
#ifdef VL_THREADED
//=========================================================================
// VerilatedTraceOffloadBuffer
template <>
VerilatedTraceOffloadBuffer<VL_BUF_T>::VerilatedTraceOffloadBuffer(VL_SUB_T& owner)
: VerilatedTraceBuffer<VL_BUF_T>{owner}
, m_offloadBufferWritep{owner.m_offloadBufferWritep}
, m_offloadBufferEndp{owner.m_offloadBufferEndp} {
if (m_offloadBufferWritep) {
using This = VerilatedTraceBuffer<VL_BUF_T>*;
// Tack on the buffer address
static_assert(2 * sizeof(uint32_t) >= sizeof(This),
"This should be enough on all plafrorms");
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::TRACE_BUFFER;
*reinterpret_cast<This*>(m_offloadBufferWritep) = static_cast<This>(this);
m_offloadBufferWritep += 2;
}
}
#endif
#endif // VL_CPPCHECK
+332 -38
View File
@@ -29,6 +29,8 @@
#endif
#include <algorithm>
#include <array>
#include <atomic>
#include <deque>
#include <map>
#include <set>
@@ -70,6 +72,72 @@ extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
#define VL_OUT(name, msb, lsb) IData name ///< Declare output signal, 17-32 bits
#define VL_OUTW(name, msb, lsb, words) VlWide<words> name ///< Declare output signal, 65+ bits
//===================================================================
// Activity trigger vector
template <std::size_t T_size> //
class VlTriggerVec final {
// TODO: static assert T_size > 0, and don't generate when empty
private:
// MEMBERS
std::array<bool, T_size> m_flags; // State of the assoc array
public:
// CONSTRUCTOR
VlTriggerVec() { clear(); }
~VlTriggerVec() = default;
// METHODS
// Set all elements to false
void clear() { m_flags.fill(false); }
// Reference to element at 'index'
bool& at(size_t index) { return m_flags.at(index); }
// Return true iff at least one element is set
bool any() const {
for (size_t i = 0; i < m_flags.size(); ++i)
if (m_flags[i]) return true;
return false;
}
// Set all elements true in 'this' that are set in 'other'
void set(const VlTriggerVec<T_size>& other) {
for (size_t i = 0; i < m_flags.size(); ++i) m_flags[i] |= other.m_flags[i];
}
// Set elements of 'this' to 'a & !b' element-wise
void andNot(const VlTriggerVec<T_size>& a, const VlTriggerVec<T_size>& b) {
for (size_t i = 0; i < m_flags.size(); ++i) m_flags[i] = a.m_flags[i] && !b.m_flags[i];
}
};
//===================================================================
// SystemVerilog event type
class VlEvent final {
// MEMBERS
bool m_fired = false; // Fired on this scheduling iteration
bool m_triggered = false; // Triggered state of event persisting until next time step
public:
// CONSTRUCTOR
VlEvent() = default;
~VlEvent() = default;
// METHODS
void fire() { m_fired = m_triggered = true; }
bool isFired() const { return m_fired; }
bool isTriggered() const { return m_triggered; }
void clearFired() { m_fired = false; }
void clearTriggered() { m_triggered = false; }
};
inline std::string VL_TO_STRING(const VlEvent& e) {
return std::string("triggered=") + (e.isTriggered() ? "true" : "false");
}
//===================================================================
// Shuffle RNG
@@ -91,9 +159,9 @@ class VlReadMem final {
const int m_bits; // Bit width of values
const std::string& m_filename; // Filename
const QData m_end; // End address (as specified by user)
FILE* m_fp; // File handle for filename
QData m_addr; // Next address to read
int m_linenum; // Line number last read from file
FILE* m_fp = nullptr; // File handle for filename
QData m_addr = 0; // Next address to read
int m_linenum = 0; // Line number last read from file
bool m_anyAddr = false; // Had address directive in the file
public:
VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end);
@@ -107,8 +175,8 @@ public:
class VlWriteMem final {
const bool m_hex; // Hex format
const int m_bits; // Bit width of values
FILE* m_fp; // File handle for filename
QData m_addr; // Next address to write
FILE* m_fp = nullptr; // File handle for filename
QData m_addr = 0; // Next address to write
public:
VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end);
~VlWriteMem();
@@ -131,7 +199,8 @@ public:
static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE;
template <std::size_t T_Words> struct VlWide final {
template <std::size_t T_Words>
struct VlWide final {
// MEMBERS
// This should be the only data member, otherwise generated static initializers need updating
EData m_storage[T_Words]; // Contents of the packed array
@@ -145,6 +214,12 @@ template <std::size_t T_Words> struct VlWide final {
// Default copy assignment operators are used.
operator WDataOutP() { return &m_storage[0]; } // This also allows []
operator WDataInP() const { return &m_storage[0]; } // This also allows []
bool operator!=(const VlWide<T_Words>& that) const {
for (size_t i = 0; i < T_Words; ++i) {
if (m_storage[i] != that.m_storage[i]) return true;
}
return false;
}
// METHODS
const EData& at(size_t index) const { return m_storage[index]; }
@@ -163,7 +238,8 @@ VlWide<T_Words>& VL_CVT_W_A(const WDataInP inp, const VlWide<T_Words>&) {
return *((VlWide<T_Words>*)inp);
}
template <std::size_t T_Words> std::string VL_TO_STRING(const VlWide<T_Words>& obj) {
template <std::size_t T_Words>
std::string VL_TO_STRING(const VlWide<T_Words>& obj) {
return VL_TO_STRING_W(T_Words, obj.data());
}
@@ -174,7 +250,8 @@ template <std::size_t T_Words> std::string VL_TO_STRING(const VlWide<T_Words>& o
//
// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound
// For dynamic arrays it is always zero
template <class T_Value, size_t T_MaxSize = 0> class VlQueue final {
template <class T_Value, size_t T_MaxSize = 0>
class VlQueue final {
private:
// TYPES
using Deque = std::deque<T_Value>;
@@ -199,7 +276,8 @@ public:
// Standard copy constructor works. Verilog: assoca = assocb
// Also must allow conversion from a different T_MaxSize queue
template <size_t U_MaxSize = 0> VlQueue operator=(const VlQueue<T_Value, U_MaxSize>& rhs) {
template <size_t U_MaxSize = 0>
VlQueue operator=(const VlQueue<T_Value, U_MaxSize>& rhs) {
m_deque = rhs.privateDeque();
if (VL_UNLIKELY(T_MaxSize && T_MaxSize < m_deque.size())) m_deque.resize(T_MaxSize - 1);
return *this;
@@ -330,7 +408,8 @@ public:
// Methods
void sort() { std::sort(m_deque.begin(), m_deque.end()); }
template <typename Func> void sort(Func with_func) {
template <typename Func>
void sort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
std::sort(m_deque.begin(), m_deque.end(), [=](const T_Value& a, const T_Value& b) {
// index number is meaninless with sort, as it changes
@@ -338,7 +417,8 @@ public:
});
}
void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); }
template <typename Func> void rsort(Func with_func) {
template <typename Func>
void rsort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
std::sort(m_deque.rbegin(), m_deque.rend(), [=](const T_Value& a, const T_Value& b) {
// index number is meaninless with sort, as it changes
@@ -373,7 +453,8 @@ public:
}
return out;
}
template <typename Func> VlQueue find(Func with_func) const {
template <typename Func>
VlQueue find(Func with_func) const {
VlQueue out;
IData index = 0;
for (const auto& i : m_deque) {
@@ -382,7 +463,8 @@ public:
}
return out;
}
template <typename Func> VlQueue<IData> find_index(Func with_func) const {
template <typename Func>
VlQueue<IData> find_index(Func with_func) const {
VlQueue<IData> out;
IData index = 0;
for (const auto& i : m_deque) {
@@ -391,7 +473,8 @@ public:
}
return out;
}
template <typename Func> VlQueue find_first(Func with_func) const {
template <typename Func>
VlQueue find_first(Func with_func) const {
// Can't use std::find_if as need index number
IData index = 0;
for (const auto& i : m_deque) {
@@ -400,7 +483,8 @@ public:
}
return VlQueue{};
}
template <typename Func> VlQueue<IData> find_first_index(Func with_func) const {
template <typename Func>
VlQueue<IData> find_first_index(Func with_func) const {
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) return VlQueue<IData>::cons(index);
@@ -408,7 +492,8 @@ public:
}
return VlQueue<IData>{};
}
template <typename Func> VlQueue find_last(Func with_func) const {
template <typename Func>
VlQueue find_last(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto& item : vlstd::reverse_view(m_deque)) {
if (with_func(index, item)) return VlQueue::cons(item);
@@ -416,7 +501,8 @@ public:
}
return VlQueue{};
}
template <typename Func> VlQueue<IData> find_last_index(Func with_func) const {
template <typename Func>
VlQueue<IData> find_last_index(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto& item : vlstd::reverse_view(m_deque)) {
if (with_func(index, item)) return VlQueue<IData>::cons(index);
@@ -442,7 +528,8 @@ public:
for (const auto& i : m_deque) out += i;
return out;
}
template <typename Func> T_Value r_sum(Func with_func) const {
template <typename Func>
T_Value r_sum(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out += with_func(index++, i);
@@ -456,7 +543,8 @@ public:
for (; it != m_deque.end(); ++it) out *= *it;
return out;
}
template <typename Func> T_Value r_product(Func with_func) const {
template <typename Func>
T_Value r_product(Func with_func) const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
IData index = 0;
@@ -474,7 +562,8 @@ public:
for (; it != m_deque.end(); ++it) out &= *it;
return out;
}
template <typename Func> T_Value r_and(Func with_func) const {
template <typename Func>
T_Value r_and(Func with_func) const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
IData index = 0;
@@ -489,7 +578,8 @@ public:
for (const auto& i : m_deque) out |= i;
return out;
}
template <typename Func> T_Value r_or(Func with_func) const {
template <typename Func>
T_Value r_or(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out |= with_func(index++, i);
@@ -500,7 +590,8 @@ public:
for (const auto& i : m_deque) out ^= i;
return out;
}
template <typename Func> T_Value r_xor(Func with_func) const {
template <typename Func>
T_Value r_xor(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out ^= with_func(index++, i);
@@ -520,7 +611,8 @@ public:
}
};
template <class T_Value> std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
template <class T_Value>
std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
return obj.to_string();
}
@@ -529,7 +621,8 @@ template <class T_Value> std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
// There are no multithreaded locks on this; the base variable must
// be protected by other means
//
template <class T_Key, class T_Value> class VlAssocArray final {
template <class T_Key, class T_Value>
class VlAssocArray final {
private:
// TYPES
using Map = std::map<T_Key, T_Value>;
@@ -654,19 +747,22 @@ public:
}
return out;
}
template <typename Func> VlQueue<T_Value> find(Func with_func) const {
template <typename Func>
VlQueue<T_Value> find(Func with_func) const {
VlQueue<T_Value> out;
for (const auto& i : m_map)
if (with_func(i.first, i.second)) out.push_back(i.second);
return out;
}
template <typename Func> VlQueue<T_Key> find_index(Func with_func) const {
template <typename Func>
VlQueue<T_Key> find_index(Func with_func) const {
VlQueue<T_Key> out;
for (const auto& i : m_map)
if (with_func(i.first, i.second)) out.push_back(i.first);
return out;
}
template <typename Func> VlQueue<T_Value> find_first(Func with_func) const {
template <typename Func>
VlQueue<T_Value> find_first(Func with_func) const {
const auto it
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
@@ -674,7 +770,8 @@ public:
if (it == m_map.end()) return VlQueue<T_Value>{};
return VlQueue<T_Value>::cons(it->second);
}
template <typename Func> VlQueue<T_Key> find_first_index(Func with_func) const {
template <typename Func>
VlQueue<T_Key> find_first_index(Func with_func) const {
const auto it
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
@@ -682,7 +779,8 @@ public:
if (it == m_map.end()) return VlQueue<T_Value>{};
return VlQueue<T_Key>::cons(it->first);
}
template <typename Func> VlQueue<T_Value> find_last(Func with_func) const {
template <typename Func>
VlQueue<T_Value> find_last(Func with_func) const {
const auto it
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
@@ -690,7 +788,8 @@ public:
if (it == m_map.rend()) return VlQueue<T_Value>{};
return VlQueue<T_Value>::cons(it->second);
}
template <typename Func> VlQueue<T_Key> find_last_index(Func with_func) const {
template <typename Func>
VlQueue<T_Key> find_last_index(Func with_func) const {
const auto it
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
@@ -724,7 +823,8 @@ public:
for (const auto& i : m_map) out += i.second;
return out;
}
template <typename Func> T_Value r_sum(Func with_func) const {
template <typename Func>
T_Value r_sum(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out += with_func(i.first, i.second);
return out;
@@ -737,7 +837,8 @@ public:
for (; it != m_map.end(); ++it) out *= it->second;
return out;
}
template <typename Func> T_Value r_product(Func with_func) const {
template <typename Func>
T_Value r_product(Func with_func) const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{with_func(it->first, it->second)};
@@ -753,7 +854,8 @@ public:
for (; it != m_map.end(); ++it) out &= it->second;
return out;
}
template <typename Func> T_Value r_and(Func with_func) const {
template <typename Func>
T_Value r_and(Func with_func) const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{with_func(it->first, it->second)};
@@ -766,7 +868,8 @@ public:
for (const auto& i : m_map) out |= i.second;
return out;
}
template <typename Func> T_Value r_or(Func with_func) const {
template <typename Func>
T_Value r_or(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out |= with_func(i.first, i.second);
return out;
@@ -776,7 +879,8 @@ public:
for (const auto& i : m_map) out ^= i.second;
return out;
}
template <typename Func> T_Value r_xor(Func with_func) const {
template <typename Func>
T_Value r_xor(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out ^= with_func(i.first, i.second);
return out;
@@ -839,7 +943,8 @@ void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
/// This class may get exposed to a Verilated Model's top I/O, if the top
/// IO has an unpacked array.
template <class T_Value, std::size_t T_Depth> struct VlUnpacked final {
template <class T_Value, std::size_t T_Depth>
struct VlUnpacked final {
// MEMBERS
// This should be the only data member, otherwise generated static initializers need updating
T_Value m_storage[T_Depth]; // Contents of the unpacked array
@@ -860,6 +965,12 @@ template <class T_Value, std::size_t T_Depth> struct VlUnpacked final {
T_Value& operator[](size_t index) { return m_storage[index]; }
const T_Value& operator[](size_t index) const { return m_storage[index]; }
// *this != that, which might be used for change detection/trigger computation, but avoid
// operator overloading in VlUnpacked for safety in other contexts.
bool neq(const VlUnpacked<T_Value, T_Depth>& that) const { return neq(*this, that); }
// Similar to 'neq' above, *this = that used for change detection
void assign(const VlUnpacked<T_Value, T_Depth>& that) { *this = that; }
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
std::string out = "'{";
@@ -870,6 +981,22 @@ template <class T_Value, std::size_t T_Depth> struct VlUnpacked final {
}
return out + "} ";
}
private:
template <typename T_Val, std::size_t T_Dep>
static bool neq(const VlUnpacked<T_Val, T_Dep>& a, const VlUnpacked<T_Val, T_Dep>& b) {
for (size_t i = 0; i < T_Dep; ++i) {
// Recursive 'neq', in case T_Val is also a VlUnpacked<_, _>
if (neq(a.m_storage[i], b.m_storage[i])) return true;
}
return false;
}
template <typename T_Other> //
static bool neq(const T_Other& a, const T_Other& b) {
// Base case (T_Other is not VlUnpacked<_, _>), fall back on !=
return a != b;
}
};
template <class T_Value, std::size_t T_Depth>
@@ -877,12 +1004,179 @@ std::string VL_TO_STRING(const VlUnpacked<T_Value, T_Depth>& obj) {
return obj.to_string();
}
class VlClass; // See below
//===================================================================
// Class providing delayed deletion of garbage objects. Objects get deleted only when 'deleteAll()'
// is called, or the deleter itself is destroyed.
class VlDeleter final {
// MEMBERS
// Queue of new objects that should be deleted
std::vector<VlClass*> m_newGarbage VL_GUARDED_BY(m_mutex);
// Queue of objects currently being deleted (only for deleteAll())
std::vector<VlClass*> m_toDelete VL_GUARDED_BY(m_deleteMutex);
mutable VerilatedMutex m_mutex; // Mutex protecting the 'new garbage' queue
mutable VerilatedMutex m_deleteMutex; // Mutex protecting the delete queue
public:
// CONSTRUCTOR
VlDeleter() = default;
~VlDeleter() { deleteAll(); }
private:
VL_UNCOPYABLE(VlDeleter);
public:
// METHODS
// Adds a new object to the 'new garbage' queue.
void put(VlClass* const objp) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_newGarbage.push_back(objp);
}
// Deletes all queued garbage objects.
void deleteAll() VL_MT_SAFE;
};
//===================================================================
// Base class for all verilated classes. Includes a reference counter, and a pointer to the deleter
// object that should destroy it after the counter reaches 0. This allows for easy construction of
// VlClassRefs from 'this'. Also declares a virtual constructor, so that the object can be deleted
// using a base pointer.
class VlClass VL_NOT_FINAL {
// TYPES
template <typename T_Class>
friend class VlClassRef; // Needed for access to the ref counter and deleter
// MEMBERS
std::atomic<size_t> m_counter{0}; // Reference count for this object
VlDeleter* m_deleter = nullptr; // The deleter that will delete this object
// METHODS
// Atomically increments the reference counter
void refCountInc() VL_MT_SAFE { ++m_counter; }
// Atomically decrements the reference counter. Assuming VlClassRef semantics are sound, it
// should never get called at m_counter == 0.
void refCountDec() VL_MT_SAFE {
if (!--m_counter) m_deleter->put(this);
}
public:
// CONSTRUCTORS
VlClass() = default;
VlClass(const VlClass& copied) {}
virtual ~VlClass() {}
};
//===================================================================
// Represents the null pointer. Used for setting VlClassRef to null instead of
// via nullptr_t, to prevent the implicit conversion of 0 to nullptr.
struct VlNull {
operator bool() const { return false; }
};
//===================================================================
// Verilog class reference container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
#define VlClassRef std::shared_ptr
template <typename T_Class>
class VlClassRef final {
private:
// TYPES
template <typename T_OtherClass>
friend class VlClassRef; // Needed for template copy/move assignments
// MEMBERS
T_Class* m_objp = nullptr; // Object pointed to
// METHODS
// Increase reference counter with null check
void refCountInc() const VL_MT_SAFE {
if (m_objp) m_objp->refCountInc();
}
// Decrease reference counter with null check
void refCountDec() const VL_MT_SAFE {
if (m_objp) m_objp->refCountDec();
}
public:
// CONSTRUCTORS
VlClassRef() = default;
// Init with nullptr
VlClassRef(VlNull){};
template <typename... T_Args>
VlClassRef(VlDeleter& deleter, T_Args&&... args)
: m_objp{new T_Class{std::forward<T_Args>(args)...}} {
m_objp->m_deleter = &deleter;
refCountInc();
}
// Explicit to avoid implicit conversion from 0
explicit VlClassRef(T_Class* objp)
: m_objp{objp} {
refCountInc();
}
// cppcheck-suppress noExplicitConstructor
VlClassRef(const VlClassRef& copied)
: m_objp{copied.m_objp} {
refCountInc();
}
// cppcheck-suppress noExplicitConstructor
VlClassRef(VlClassRef&& moved)
: m_objp{vlstd::exchange(moved.m_objp, nullptr)} {}
~VlClassRef() { refCountDec(); }
// METHODS
// Copy and move assignments
VlClassRef& operator=(const VlClassRef& copied) {
refCountDec();
m_objp = copied.m_objp;
refCountInc();
return *this;
}
VlClassRef& operator=(VlClassRef&& moved) {
refCountDec();
m_objp = vlstd::exchange(moved.m_objp, nullptr);
return *this;
}
template <typename T_OtherClass>
VlClassRef& operator=(const VlClassRef<T_OtherClass>& copied) {
refCountDec();
m_objp = copied.m_objp;
refCountInc();
return *this;
}
template <typename T_OtherClass>
VlClassRef& operator=(VlClassRef<T_OtherClass>&& moved) {
refCountDec();
m_objp = vlstd::exchange(moved.m_objp, nullptr);
return *this;
}
// Assign with nullptr
VlClassRef& operator=(VlNull) {
refCountDec();
m_objp = nullptr;
return *this;
}
// Dynamic caster
template <typename T_OtherClass>
VlClassRef<T_OtherClass> dynamicCast() const {
return VlClassRef<T_OtherClass>{dynamic_cast<T_OtherClass*>(m_objp)};
}
// Dereference operators
T_Class& operator*() const { return *m_objp; }
T_Class* operator->() const { return m_objp; }
// For 'if (ptr)...'
operator bool() const { return m_objp; }
};
#define VL_NEW(Class, ...) \
VlClassRef<Class> { vlSymsp->__Vm_deleter, __VA_ARGS__ }
#define VL_KEEP_THIS \
VlClassRef<std::remove_pointer<decltype(this)>::type> __Vthisref { this }
template <class T> // T typically of type VlClassRef<x>
inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
@@ -892,7 +1186,7 @@ inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
template <typename T, typename U>
static inline bool VL_CAST_DYNAMIC(VlClassRef<T> in, VlClassRef<U>& outr) {
VlClassRef<U> casted = std::dynamic_pointer_cast<U>(in);
VlClassRef<U> casted = in.template dynamicCast<U>();
if (VL_LIKELY(casted)) {
outr = casted;
return true;
+110 -121
View File
@@ -38,13 +38,13 @@
# include <unistd.h>
#endif
#ifndef O_LARGEFILE // For example on WIN32
#ifndef O_LARGEFILE // WIN32 headers omit this
# define O_LARGEFILE 0
#endif
#ifndef O_NONBLOCK
#ifndef O_NONBLOCK // WIN32 headers omit this
# define O_NONBLOCK 0
#endif
#ifndef O_CLOEXEC
#ifndef O_CLOEXEC // WIN32 headers omit this
# define O_CLOEXEC 0
#endif
@@ -65,7 +65,8 @@ constexpr unsigned VL_TRACE_SUFFIX_ENTRY_SIZE = 8; // Size of a suffix entry
//=============================================================================
// Utility functions: TODO: put these in a common place and share them.
template <size_t N> static size_t roundUpToMultipleOf(size_t value) {
template <size_t N>
static size_t roundUpToMultipleOf(size_t value) {
static_assert((N & (N - 1)) == 0, "'N' must be a power of 2");
size_t mask = N - 1;
return (value + mask) & ~mask;
@@ -119,13 +120,13 @@ void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
// Set member variables
m_filename = filename; // "" is ok, as someone may overload open
openNextImp(m_rolloverMB != 0);
openNextImp(m_rolloverSize != 0);
if (!isOpen()) return;
dumpHeader();
// When using rollover, the first chunk contains the header only.
if (m_rolloverMB) openNextImp(true);
if (m_rolloverSize) openNextImp(true);
}
void VerilatedVcd::openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex) {
@@ -179,7 +180,7 @@ void VerilatedVcd::openNextImp(bool incFilename) {
}
bool VerilatedVcd::preChangeDump() {
if (VL_UNLIKELY(m_rolloverMB && m_wroteBytes > m_rolloverMB)) openNextImp(true);
if (VL_UNLIKELY(m_rolloverSize && m_wroteBytes > m_rolloverSize)) openNextImp(true);
return isOpen();
}
@@ -229,9 +230,11 @@ VerilatedVcd::~VerilatedVcd() {
if (m_wrBufp) VL_DO_CLEAR(delete[] m_wrBufp, m_wrBufp = nullptr);
deleteNameMap();
if (m_filep && m_fileNewed) VL_DO_CLEAR(delete m_filep, m_filep = nullptr);
#ifdef VL_TRACE_PARALLEL
assert(m_numBuffers == m_freeBuffers.size());
for (auto& pair : m_freeBuffers) VL_DO_CLEAR(delete[] pair.first, pair.first = nullptr);
#ifdef VL_THREADED
if (parallel()) {
assert(m_numBuffers == m_freeBuffers.size());
for (auto& pair : m_freeBuffers) VL_DO_CLEAR(delete[] pair.first, pair.first = nullptr);
}
#endif
}
@@ -260,11 +263,6 @@ void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
// This function is on the flush() call path
const VerilatedLockGuard lock{m_mutex};
if (!isOpen()) return;
if (m_evcd) {
printStr("$vcdclose ");
printQuad(timeLastDump());
printStr(" $end\n");
}
closePrev();
// closePrev() called Super::flush(), so we just
// need to shut down the tracing thread here.
@@ -514,38 +512,29 @@ void VerilatedVcd::declare(uint32_t code, const char* name, const char* wirep, b
// Print reference
std::string decl = "$var ";
if (m_evcd) {
decl += "port";
} else {
decl += wirep; // usually "wire"
}
decl += wirep; // usually "wire"
constexpr size_t bufsize = 1000;
char buf[bufsize];
VL_SNPRINTF(buf, bufsize, " %2d ", bits);
decl += buf;
if (m_evcd) {
VL_SNPRINTF(buf, bufsize, "<%u", code);
decl += buf;
} else {
// Add string code to decl
char* const endp = writeCode(buf, code);
*endp = '\0';
decl += buf;
// Build suffix array entry
char* const entryp = &m_suffixes[code * VL_TRACE_SUFFIX_ENTRY_SIZE];
const size_t length = endp - buf;
assert(length <= VL_TRACE_MAX_VCD_CODE_SIZE);
// 1 bit values don't have a ' ' separator between value and string code
const bool isBit = bits == 1;
entryp[0] = ' '; // Separator
// Use memcpy as we checked size above, and strcpy is flagged unsafe
std::memcpy(entryp + !isBit, buf,
std::strlen(buf)); // Code (overwrite separator if isBit)
entryp[length + !isBit] = '\n'; // Replace '\0' with line termination '\n'
// Set length of suffix (used to increment write pointer)
entryp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1] = !isBit + length + 1;
}
// Add string code to decl
char* const endp = writeCode(buf, code);
*endp = '\0';
decl += buf;
// Build suffix array entry
char* const entryp = &m_suffixes[code * VL_TRACE_SUFFIX_ENTRY_SIZE];
const size_t length = endp - buf;
assert(length <= VL_TRACE_MAX_VCD_CODE_SIZE);
// 1 bit values don't have a ' ' separator between value and string code
const bool isBit = bits == 1;
entryp[0] = ' '; // Separator
// Use memcpy as we checked size above, and strcpy is flagged unsafe
std::memcpy(entryp + !isBit, buf,
std::strlen(buf)); // Code (overwrite separator if isBit)
entryp[length + !isBit] = '\n'; // Replace '\0' with line termination '\n'
// Set length of suffix (used to increment write pointer)
entryp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1] = !isBit + length + 1;
decl += " ";
decl += basename;
if (array) {
@@ -583,74 +572,70 @@ void VerilatedVcd::declDouble(uint32_t code, const char* name, bool array, int a
//=============================================================================
// Get/commit trace buffer
VerilatedVcdBuffer* VerilatedVcd::getTraceBuffer() {
#ifdef VL_TRACE_PARALLEL
// Note: This is called from VeriltedVcd::dump, which already holds the lock
// If no buffer available, allocate a new one
if (m_freeBuffers.empty()) {
constexpr size_t pageSize = 4096;
// 4 * m_maxSignalBytes, so we can reserve 2 * m_maxSignalBytes at the end for safety
size_t startingSize = roundUpToMultipleOf<pageSize>(4 * m_maxSignalBytes);
m_freeBuffers.emplace_back(new char[startingSize], startingSize);
++m_numBuffers;
VerilatedVcd::Buffer* VerilatedVcd::getTraceBuffer() {
VerilatedVcd::Buffer* const bufp = new Buffer{*this};
#ifdef VL_THREADED
if (parallel()) {
// Note: This is called from VeriltedVcd::dump, which already holds the lock
// If no buffer available, allocate a new one
if (m_freeBuffers.empty()) {
constexpr size_t pageSize = 4096;
// 4 * m_maxSignalBytes, so we can reserve 2 * m_maxSignalBytes at the end for safety
size_t startingSize = roundUpToMultipleOf<pageSize>(4 * m_maxSignalBytes);
m_freeBuffers.emplace_back(new char[startingSize], startingSize);
++m_numBuffers;
}
// Grab a buffer
const auto pair = m_freeBuffers.back();
m_freeBuffers.pop_back();
// Initialize
bufp->m_writep = bufp->m_bufp = pair.first;
bufp->m_size = pair.second;
bufp->adjustGrowp();
}
// Grab a buffer
const auto pair = m_freeBuffers.back();
m_freeBuffers.pop_back();
// Return the buffer
return new VerilatedVcdBuffer{*this, pair.first, pair.second};
#else
return new VerilatedVcdBuffer{*this};
#endif
// Return the buffer
return bufp;
}
void VerilatedVcd::commitTraceBuffer(VerilatedVcdBuffer* bufp) {
#ifdef VL_TRACE_PARALLEL
// Note: This is called from VeriltedVcd::dump, which already holds the lock
// Resize output buffer. Note, we use the full size of the trace buffer, as
// this is a lot more stable than the actual occupancy of the trace buffer.
// This helps us to avoid re-allocations due to small size changes.
bufferResize(bufp->m_size);
// Compute occupancy of buffer
const size_t usedSize = bufp->m_writep - bufp->m_bufp;
// Copy to output buffer
std::memcpy(m_writep, bufp->m_bufp, usedSize);
// Adjust write pointer
m_writep += usedSize;
// Flush if necessary
bufferCheck();
// Put buffer back on free list
m_freeBuffers.emplace_back(bufp->m_bufp, bufp->m_size);
void VerilatedVcd::commitTraceBuffer(VerilatedVcd::Buffer* bufp) {
if (parallel()) {
#if VL_THREADED
// Note: This is called from VeriltedVcd::dump, which already holds the lock
// Resize output buffer. Note, we use the full size of the trace buffer, as
// this is a lot more stable than the actual occupancy of the trace buffer.
// This helps us to avoid re-allocations due to small size changes.
bufferResize(bufp->m_size);
// Compute occupancy of buffer
const size_t usedSize = bufp->m_writep - bufp->m_bufp;
// Copy to output buffer
std::memcpy(m_writep, bufp->m_bufp, usedSize);
// Adjust write pointer
m_writep += usedSize;
// Flush if necessary
bufferCheck();
// Put buffer back on free list
m_freeBuffers.emplace_back(bufp->m_bufp, bufp->m_size);
#else
// Needs adjusting for emitTimeChange
m_writep = bufp->m_writep;
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
#endif
} else {
// Needs adjusting for emitTimeChange
m_writep = bufp->m_writep;
}
delete bufp;
}
//=============================================================================
// VerilatedVcdBuffer implementation
#ifdef VL_TRACE_PARALLEL
VerilatedVcdBuffer::VerilatedVcdBuffer(VerilatedVcd& owner, char* bufp, size_t size)
: VerilatedTraceBuffer<VerilatedVcd, VerilatedVcdBuffer>{owner}
, m_writep{bufp}
, m_bufp{bufp}
, m_size{size} {
adjustGrowp();
}
#else
VerilatedVcdBuffer::VerilatedVcdBuffer(VerilatedVcd& owner)
: VerilatedTraceBuffer<VerilatedVcd, VerilatedVcdBuffer>{owner} {}
#endif
//=============================================================================
// Trace rendering primitives
static inline void
VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) VL_ATTR_NO_SANITIZE_ALIGN;
static void VerilatedVcdCCopyAndAppendNewLine(char* writep,
const char* suffixp) VL_ATTR_NO_SANITIZE_ALIGN;
static inline void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) {
static void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) {
// Copy the whole suffix (this avoid having hard to predict branches which
// helps a lot). Note: The maximum length of the suffix is
// VL_TRACE_MAX_VCD_CODE_SIZE + 2 == 7, but we unroll this here for speed.
@@ -679,35 +664,39 @@ void VerilatedVcdBuffer::finishLine(uint32_t code, char* writep) {
// suffix, which was stored in the last byte of the suffix buffer entry.
m_writep = writep + suffixp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1];
#ifdef VL_TRACE_PARALLEL
// Double the size of the buffer if necessary
if (VL_UNLIKELY(m_writep >= m_growp)) {
// Compute occupied size of current buffer
const size_t usedSize = m_writep - m_bufp;
// We are always doubling the size
m_size *= 2;
// Allocate the new buffer
char* const newBufp = new char[m_size];
// Copy from current buffer to new buffer
std::memcpy(newBufp, m_bufp, usedSize);
// Delete current buffer
delete[] m_bufp;
// Make new buffer the current buffer
m_bufp = newBufp;
// Adjust write pointer
m_writep = m_bufp + usedSize;
// Adjust resize limit
adjustGrowp();
}
if (m_owner.parallel()) {
#ifdef VL_THREADED
// Double the size of the buffer if necessary
if (VL_UNLIKELY(m_writep >= m_growp)) {
// Compute occupied size of current buffer
const size_t usedSize = m_writep - m_bufp;
// We are always doubling the size
m_size *= 2;
// Allocate the new buffer
char* const newBufp = new char[m_size];
// Copy from current buffer to new buffer
std::memcpy(newBufp, m_bufp, usedSize);
// Delete current buffer
delete[] m_bufp;
// Make new buffer the current buffer
m_bufp = newBufp;
// Adjust write pointer
m_writep = m_bufp + usedSize;
// Adjust resize limit
adjustGrowp();
}
#else
// Flush the write buffer if there's not enough space left for new information
// We only call this once per vector, so we need enough slop for a very wide "b###" line
if (VL_UNLIKELY(m_writep > m_wrFlushp)) {
m_owner.m_writep = m_writep;
m_owner.bufferFlush();
m_writep = m_owner.m_writep;
}
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
#endif
} else {
// Flush the write buffer if there's not enough space left for new information
// We only call this once per vector, so we need enough slop for a very wide "b###" line
if (VL_UNLIKELY(m_writep > m_wrFlushp)) {
m_owner.m_writep = m_writep;
m_owner.bufferFlush();
m_writep = m_owner.m_writep;
}
}
}
//=============================================================================
+60 -50
View File
@@ -41,7 +41,7 @@ public:
using Super = VerilatedTrace<VerilatedVcd, VerilatedVcdBuffer>;
private:
friend Buffer; // Give the buffer access to the private bits
friend VerilatedVcdBuffer; // Give the buffer access to the private bits
//=========================================================================
// VCD specific internals
@@ -49,9 +49,8 @@ private:
VerilatedVcdFile* m_filep; // File we're writing to
bool m_fileNewed; // m_filep needs destruction
bool m_isOpen = false; // True indicates open file
bool m_evcd = false; // True for evcd format
std::string m_filename; // Filename we're writing to (if open)
uint64_t m_rolloverMB = 0; // MB of file size to rollover at
uint64_t m_rolloverSize = 0; // File size to rollover at
int m_modDepth = 0; // Depth of module hierarchy
char* m_wrBufp; // Output buffer
@@ -66,7 +65,7 @@ private:
using NameMap = std::map<const std::string, const std::string>;
NameMap* m_namemapp = nullptr; // List of names for the header
#ifdef VL_TRACE_PARALLEL
#ifdef VL_THREADED
// Vector of free trace buffers as (pointer, size) pairs.
std::vector<std::pair<char*, size_t>> m_freeBuffers;
size_t m_numBuffers = 0; // Number of trace buffers allocated
@@ -74,7 +73,7 @@ private:
void bufferResize(size_t minsize);
void bufferFlush() VL_MT_UNSAFE_ONE;
inline void bufferCheck() {
void bufferCheck() {
// Flush the write buffer if there's not enough space left for new information
// We only call this once per vector, so we need enough slop for a very wide "b###" line
if (VL_UNLIKELY(m_writep > m_wrFlushp)) bufferFlush();
@@ -103,15 +102,18 @@ protected:
// Implementation of VerilatedTrace interface
// Called when the trace moves forward to a new time point
virtual void emitTimeChange(uint64_t timeui) override;
void emitTimeChange(uint64_t timeui) override;
// Hooks called from VerilatedTrace
virtual bool preFullDump() override { return isOpen(); }
virtual bool preChangeDump() override;
bool preFullDump() override { return isOpen(); }
bool preChangeDump() override;
// Trace buffer management
virtual VerilatedVcdBuffer* getTraceBuffer() override;
virtual void commitTraceBuffer(VerilatedVcdBuffer*) override;
Buffer* getTraceBuffer() override;
void commitTraceBuffer(Buffer*) override;
// Configure sub-class
void configure(const VerilatedTraceConfig&) override{};
public:
//=========================================================================
@@ -122,8 +124,8 @@ public:
~VerilatedVcd();
// ACCESSORS
// Set size in megabytes after which new file should be created
void rolloverMB(uint64_t rolloverMB) { m_rolloverMB = rolloverMB; }
// Set size in bytes after which new file should be created.
void rolloverSize(uint64_t size) { m_rolloverSize = size; }
// METHODS - All must be thread safe
// Open the file; call isOpen() to see if errors
@@ -149,67 +151,72 @@ public:
#ifndef DOXYGEN
// Declare specialization here as it's used in VerilatedFstC just below
template <> void VerilatedVcd::Super::dump(uint64_t time);
template <> void VerilatedVcd::Super::set_time_unit(const char* unitp);
template <> void VerilatedVcd::Super::set_time_unit(const std::string& unit);
template <> void VerilatedVcd::Super::set_time_resolution(const char* unitp);
template <> void VerilatedVcd::Super::set_time_resolution(const std::string& unit);
template <> void VerilatedVcd::Super::dumpvars(int level, const std::string& hier);
template <>
void VerilatedVcd::Super::dump(uint64_t time);
template <>
void VerilatedVcd::Super::set_time_unit(const char* unitp);
template <>
void VerilatedVcd::Super::set_time_unit(const std::string& unit);
template <>
void VerilatedVcd::Super::set_time_resolution(const char* unitp);
template <>
void VerilatedVcd::Super::set_time_resolution(const std::string& unit);
template <>
void VerilatedVcd::Super::dumpvars(int level, const std::string& hier);
#endif // DOXYGEN
//=============================================================================
// VerilatedVcdBuffer
class VerilatedVcdBuffer final : public VerilatedTraceBuffer<VerilatedVcd, VerilatedVcdBuffer> {
// Give the trace file access to the private bits
class VerilatedVcdBuffer VL_NOT_FINAL {
// Give the trace file ans sub-classes access to the private bits
friend VerilatedVcd;
friend VerilatedVcd::Super;
friend VerilatedVcd::Buffer;
friend VerilatedVcd::OffloadBuffer;
#ifdef VL_TRACE_PARALLEL
char* m_writep; // Write pointer into m_bufp
char* m_bufp; // The beginning of the trace buffer
size_t m_size; // The size of the buffer at m_bufp
char* m_growp; // Resize limit pointer
#else
char* m_writep = m_owner.m_writep; // Write pointer into output buffer
char* const m_wrFlushp = m_owner.m_wrFlushp; // Output buffer flush trigger location
#endif
VerilatedVcd& m_owner; // Trace file owning this buffer. Required by subclasses.
// Write pointer into output buffer (in parallel mode, this is set up in 'getTraceBuffer')
char* m_writep = m_owner.parallel() ? nullptr : m_owner.m_writep;
// Output buffer flush trigger location (only used when not parallel)
char* const m_wrFlushp = m_owner.parallel() ? nullptr : m_owner.m_wrFlushp;
// VCD line end string codes + metadata
const char* const m_suffixes = m_owner.m_suffixes.data();
// The maximum number of bytes a single signal can emit
const size_t m_maxSignalBytes = m_owner.m_maxSignalBytes;
void finishLine(uint32_t code, char* writep);
#ifdef VL_THREADED
// Additional data for parallel tracing only
char* m_bufp = nullptr; // The beginning of the trace buffer
size_t m_size = 0; // The size of the buffer at m_bufp
char* m_growp = nullptr; // Resize limit pointer
#ifdef VL_TRACE_PARALLEL
void adjustGrowp() {
m_growp = (m_bufp + m_size) - (2 * m_maxSignalBytes);
assert(m_growp >= m_bufp + m_maxSignalBytes);
}
#endif
public:
void finishLine(uint32_t code, char* writep);
// CONSTRUCTOR
#ifdef VL_TRACE_PARALLEL
explicit VerilatedVcdBuffer(VerilatedVcd& owner, char* bufp, size_t size);
#else
explicit VerilatedVcdBuffer(VerilatedVcd& owner);
#endif
~VerilatedVcdBuffer() = default;
explicit VerilatedVcdBuffer(VerilatedVcd& owner)
: m_owner{owner} {}
virtual ~VerilatedVcdBuffer() = default;
//=========================================================================
// Implementation of VerilatedTraceBuffer interface
// Implementations of duck-typed methods for VerilatedTraceBuffer. These are
// called from only one place (the full* methods), so always inline them.
VL_ATTR_ALWINLINE inline void emitBit(uint32_t code, CData newval);
VL_ATTR_ALWINLINE inline void emitCData(uint32_t code, CData newval, int bits);
VL_ATTR_ALWINLINE inline void emitSData(uint32_t code, SData newval, int bits);
VL_ATTR_ALWINLINE inline void emitIData(uint32_t code, IData newval, int bits);
VL_ATTR_ALWINLINE inline void emitQData(uint32_t code, QData newval, int bits);
VL_ATTR_ALWINLINE inline void emitWData(uint32_t code, const WData* newvalp, int bits);
VL_ATTR_ALWINLINE inline void emitDouble(uint32_t code, double newval);
VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval);
VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits);
VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits);
VL_ATTR_ALWINLINE void emitIData(uint32_t code, IData newval, int bits);
VL_ATTR_ALWINLINE void emitQData(uint32_t code, QData newval, int bits);
VL_ATTR_ALWINLINE void emitWData(uint32_t code, const WData* newvalp, int bits);
VL_ATTR_ALWINLINE void emitDouble(uint32_t code, double newval);
};
//=============================================================================
@@ -252,7 +259,6 @@ public:
/// Destruct, flush, and close the dump
virtual ~VerilatedVcdC() { close(); }
public:
// METHODS - User called
/// Return if file is open
@@ -265,8 +271,12 @@ public:
/// The header is only in the first file created, this allows
/// "cat" to be used to combine the header plus any number of data files.
void openNext(bool incFilename = true) VL_MT_SAFE { m_sptrace.openNext(incFilename); }
/// Set size in megabytes after which new file should be created
void rolloverMB(size_t rolloverMB) VL_MT_SAFE { m_sptrace.rolloverMB(rolloverMB); }
/// Set size in bytes after which new file should be created
/// This will create a header file, followed by each separate file
/// which might be larger than the given size (due to chunking and
/// alignment to a start of a given time's dump). Any file but the
/// first may be removed. Cat files together to create viewable vcd.
void rolloverSize(size_t size) VL_MT_SAFE { m_sptrace.rolloverSize(size); }
/// Close dump
void close() VL_MT_SAFE { m_sptrace.close(); }
/// Flush dump
@@ -303,7 +313,7 @@ public:
}
// Internal class access
inline VerilatedVcd* spTrace() { return &m_sptrace; }
VerilatedVcd* spTrace() { return &m_sptrace; }
};
#endif // guard
+2 -4
View File
@@ -22,6 +22,7 @@
//=============================================================================
#include "verilatedos.h"
#include "verilated_vcd_sc.h"
//======================================================================
@@ -30,7 +31,7 @@
void VerilatedVcdSc::open(const char* filename) {
if (!sc_core::sc_get_curr_simcontext()->elaboration_done()) {
vl_fatal(__FILE__, __LINE__, "VerilatedVcdSc",
("%Error: VerilatedVcdSc::open(\"" + std::string(filename)
("%Error: VerilatedVcdSc::open(\"" + std::string{filename}
+ "\") is called before sc_core::sc_start(). "
"Run sc_core::sc_start(sc_core::SC_ZERO_TIME) before opening a wave file.")
.c_str());
@@ -63,9 +64,6 @@ void VerilatedVcdSc::trace(const unsigned int&, const std::string&, const char**
DECL_TRACE_METHOD_B( unsigned short )
DECL_TRACE_METHOD_B( unsigned int )
DECL_TRACE_METHOD_B( unsigned long )
#ifdef SYSTEMC_64BIT_PATCHES
DECL_TRACE_METHOD_B( unsigned long long)
#endif
DECL_TRACE_METHOD_B( char )
DECL_TRACE_METHOD_B( short )
DECL_TRACE_METHOD_B( int )
+12 -12
View File
@@ -23,6 +23,7 @@
#define VERILATOR_VERILATED_VCD_SC_H_
#include "verilatedos.h"
#include "verilated_sc.h"
#include "verilated_vcd_c.h"
@@ -54,37 +55,38 @@ public:
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
}
/// Destruct, flush, and close the dump
virtual ~VerilatedVcdSc() { close(); }
~VerilatedVcdSc() override { close(); }
// METHODS - for SC kernel
// Called by SystemC simulate()
virtual void cycle(bool delta_cycle) {
void cycle(bool delta_cycle) override {
if (!delta_cycle) this->dump(sc_time_stamp().to_double());
}
// Override VerilatedVcdC. Must be called after starting simulation.
virtual void open(const char* filename) /*override*/ VL_MT_SAFE;
void open(const char* filename) override VL_MT_SAFE;
private:
// METHODS - Fake outs for linker
#ifdef NC_SYSTEMC
// Cadence Incisive has these as abstract functions so we must create them
virtual void set_time_unit(int exponent10_seconds) {} // deprecated
void set_time_unit(int exponent10_seconds) override {} // deprecated
#endif
virtual void set_time_unit(double v, sc_time_unit tu) {} // LCOV_EXCL_LINE
void set_time_unit(double v, sc_time_unit tu) override {} // LCOV_EXCL_LINE
//--------------------------------------------------
// SystemC 2.1.v1
#define DECL_TRACE_METHOD_A(tp) virtual void trace(const tp& object, const std::string& name);
#define DECL_TRACE_METHOD_A(tp) void trace(const tp& object, const std::string& name) override;
#define DECL_TRACE_METHOD_B(tp) \
virtual void trace(const tp& object, const std::string& name, int width);
void trace(const tp& object, const std::string& name, int width) override;
virtual void write_comment(const std::string&);
virtual void trace(const unsigned int&, const std::string&, const char**);
void write_comment(const std::string&) override;
void trace(const unsigned int&, const std::string&, const char**) override;
// clang-format off
// Formatting matches that of sc_trace.h
// LCOV_EXCL_START
#if (SYSTEMC_VERSION >= 20171012)
DECL_TRACE_METHOD_A( sc_event )
DECL_TRACE_METHOD_A( sc_time )
@@ -98,9 +100,6 @@ private:
DECL_TRACE_METHOD_B( unsigned short )
DECL_TRACE_METHOD_B( unsigned int )
DECL_TRACE_METHOD_B( unsigned long )
#ifdef SYSTEMC_64BIT_PATCHES
DECL_TRACE_METHOD_B( unsigned long long)
#endif
DECL_TRACE_METHOD_B( char )
DECL_TRACE_METHOD_B( short )
DECL_TRACE_METHOD_B( int )
@@ -122,6 +121,7 @@ private:
DECL_TRACE_METHOD_A( sc_dt::sc_bv_base )
DECL_TRACE_METHOD_A( sc_dt::sc_lv_base )
// LCOV_EXCL_STOP
// clang-format on
#undef DECL_TRACE_METHOD_A
+48 -47
View File
@@ -26,8 +26,9 @@
#define VERILATOR_VERILATED_VPI_CPP_
#include "verilated.h"
#include "verilated_vpi.h"
#include "verilated.h"
#include "verilated_imp.h"
#include <list>
@@ -95,7 +96,7 @@ public:
*(reinterpret_cast<uint32_t*>(newp)) = activeMagic();
return newp + 8;
}
static void operator delete(void* obj, size_t /*size*/)VL_MT_SAFE {
static void operator delete(void* obj, size_t /*size*/) VL_MT_SAFE {
uint8_t* const oldp = (static_cast<uint8_t*>(obj)) - 8;
if (VL_UNLIKELY(*(reinterpret_cast<uint32_t*>(oldp)) != activeMagic())) {
VL_FATAL_MT(__FILE__, __LINE__, "",
@@ -113,7 +114,7 @@ public:
static VerilatedVpio* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpio*>(reinterpret_cast<VerilatedVpio*>(h));
}
inline vpiHandle castVpiHandle() { return reinterpret_cast<vpiHandle>(this); }
vpiHandle castVpiHandle() { return reinterpret_cast<vpiHandle>(this); }
// ACCESSORS
virtual const char* name() const { return "<null>"; }
virtual const char* fullname() const { return "<null>"; }
@@ -135,12 +136,12 @@ public:
VerilatedVpioTimedCb(uint64_t id, QData time)
: m_id{id}
, m_time{time} {}
virtual ~VerilatedVpioTimedCb() override = default;
~VerilatedVpioTimedCb() override = default;
static VerilatedVpioTimedCb* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioTimedCb*>(reinterpret_cast<VerilatedVpioTimedCb*>(h));
}
virtual uint32_t type() const override { return vpiCallback; }
virtual PLI_INT32 dovpi_remove_cb() override;
uint32_t type() const override { return vpiCallback; }
PLI_INT32 dovpi_remove_cb() override;
};
class VerilatedVpioReasonCb final : public VerilatedVpio {
@@ -154,12 +155,12 @@ public:
VerilatedVpioReasonCb(uint64_t id, PLI_INT32 reason)
: m_id{id}
, m_reason{reason} {}
virtual ~VerilatedVpioReasonCb() override = default;
~VerilatedVpioReasonCb() override = default;
static VerilatedVpioReasonCb* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioReasonCb*>(reinterpret_cast<VerilatedVpioReasonCb*>(h));
}
virtual uint32_t type() const override { return vpiCallback; }
virtual PLI_INT32 dovpi_remove_cb() override;
uint32_t type() const override { return vpiCallback; }
PLI_INT32 dovpi_remove_cb() override;
};
class VerilatedVpioConst final : public VerilatedVpio {
@@ -168,11 +169,11 @@ class VerilatedVpioConst final : public VerilatedVpio {
public:
explicit VerilatedVpioConst(int32_t num)
: m_num{num} {}
virtual ~VerilatedVpioConst() override = default;
~VerilatedVpioConst() override = default;
static VerilatedVpioConst* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioConst*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiConstant; }
uint32_t type() const override { return vpiConstant; }
int32_t num() const { return m_num; }
};
@@ -200,10 +201,10 @@ public:
}
const VerilatedVar* varp() const { return m_varp; }
const VerilatedScope* scopep() const { return m_scopep; }
virtual uint32_t size() const override { return get_range().elements(); }
virtual const VerilatedRange* rangep() const override { return &get_range(); }
virtual const char* name() const override { return m_varp->name(); }
virtual const char* fullname() const override {
uint32_t size() const override { return get_range().elements(); }
const VerilatedRange* rangep() const override { return &get_range(); }
const char* name() const override { return m_varp->name(); }
const char* fullname() const override {
static VL_THREAD_LOCAL std::string t_out;
t_out = std::string{m_scopep->name()} + "." + name();
return t_out.c_str();
@@ -214,12 +215,12 @@ class VerilatedVpioParam final : public VerilatedVpioVarBase {
public:
VerilatedVpioParam(const VerilatedVar* varp, const VerilatedScope* scopep)
: VerilatedVpioVarBase{varp, scopep} {}
virtual ~VerilatedVpioParam() override = default;
~VerilatedVpioParam() override = default;
static VerilatedVpioParam* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioParam*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiParameter; }
uint32_t type() const override { return vpiParameter; }
void* varDatap() const { return m_varp->datap(); }
};
@@ -229,13 +230,13 @@ class VerilatedVpioRange final : public VerilatedVpio {
public:
explicit VerilatedVpioRange(const VerilatedRange* range)
: m_range{range} {}
virtual ~VerilatedVpioRange() override = default;
~VerilatedVpioRange() override = default;
static VerilatedVpioRange* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioRange*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiRange; }
virtual uint32_t size() const override { return m_range->elements(); }
virtual const VerilatedRange* rangep() const override { return m_range; }
uint32_t type() const override { return vpiRange; }
uint32_t size() const override { return m_range->elements(); }
const VerilatedRange* rangep() const override { return m_range; }
};
class VerilatedVpioRangeIter final : public VerilatedVpio {
@@ -246,12 +247,12 @@ class VerilatedVpioRangeIter final : public VerilatedVpio {
public:
explicit VerilatedVpioRangeIter(const VerilatedRange* range)
: m_range{range} {}
virtual ~VerilatedVpioRangeIter() override = default;
~VerilatedVpioRangeIter() override = default;
static VerilatedVpioRangeIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioRangeIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiIterator; }
virtual vpiHandle dovpi_scan() override {
uint32_t type() const override { return vpiIterator; }
vpiHandle dovpi_scan() override {
if (VL_UNLIKELY(m_done)) {
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
@@ -268,14 +269,14 @@ protected:
public:
explicit VerilatedVpioScope(const VerilatedScope* scopep)
: m_scopep{scopep} {}
virtual ~VerilatedVpioScope() override = default;
~VerilatedVpioScope() override = default;
static VerilatedVpioScope* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioScope*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiScope; }
uint32_t type() const override { return vpiScope; }
const VerilatedScope* scopep() const { return m_scopep; }
virtual const char* name() const override { return m_scopep->name(); }
virtual const char* fullname() const override { return m_scopep->name(); }
const char* name() const override { return m_scopep->name(); }
const char* fullname() const override { return m_scopep->name(); }
};
class VerilatedVpioVar VL_NOT_FINAL : public VerilatedVpioVarBase {
@@ -308,7 +309,7 @@ public:
m_mask.u32 = 0;
}
}
virtual ~VerilatedVpioVar() override {
~VerilatedVpioVar() override {
if (m_prevDatap) VL_DO_CLEAR(delete[] m_prevDatap, m_prevDatap = nullptr);
}
static VerilatedVpioVar* castp(vpiHandle h) {
@@ -318,7 +319,7 @@ public:
uint8_t mask_byte(int idx) const { return m_mask.u8[idx & 3]; }
uint32_t entSize() const { return m_entSize; }
uint32_t index() const { return m_index; }
virtual uint32_t type() const override {
uint32_t type() const override {
return (varp()->dims() > 1) ? vpiMemory : vpiReg; // but might be wire, logic
}
void* prevDatap() const { return m_prevDatap; }
@@ -339,14 +340,14 @@ public:
m_index = index;
m_varDatap = (static_cast<uint8_t*>(varp->datap())) + entSize() * offset;
}
virtual ~VerilatedVpioMemoryWord() override = default;
~VerilatedVpioMemoryWord() override = default;
static VerilatedVpioMemoryWord* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioMemoryWord*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiMemoryWord; }
virtual uint32_t size() const override { return varp()->packed().elements(); }
virtual const VerilatedRange* rangep() const override { return &(varp()->packed()); }
virtual const char* fullname() const override {
uint32_t type() const override { return vpiMemoryWord; }
uint32_t size() const override { return varp()->packed().elements(); }
const VerilatedRange* rangep() const override { return &(varp()->packed()); }
const char* fullname() const override {
static VL_THREAD_LOCAL std::string t_out;
constexpr size_t LEN_MAX_INDEX = 25;
char num[LEN_MAX_INDEX];
@@ -364,12 +365,12 @@ class VerilatedVpioVarIter final : public VerilatedVpio {
public:
explicit VerilatedVpioVarIter(const VerilatedScope* scopep)
: m_scopep{scopep} {}
virtual ~VerilatedVpioVarIter() override = default;
~VerilatedVpioVarIter() override = default;
static VerilatedVpioVarIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioVarIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiIterator; }
virtual vpiHandle dovpi_scan() override {
uint32_t type() const override { return vpiIterator; }
vpiHandle dovpi_scan() override {
if (VL_LIKELY(m_scopep->varsp())) {
const VerilatedVarNameMap* const varsp = m_scopep->varsp();
if (VL_UNLIKELY(!m_started)) {
@@ -405,15 +406,15 @@ public:
, m_varp{varp}
, m_iteration{varp->unpacked().right()}
, m_direction{VL_LIKELY(varp->unpacked().left() > varp->unpacked().right()) ? 1 : -1} {}
virtual ~VerilatedVpioMemoryWordIter() override = default;
~VerilatedVpioMemoryWordIter() override = default;
static VerilatedVpioMemoryWordIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioMemoryWordIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiIterator; }
uint32_t type() const override { return vpiIterator; }
void iterationInc() {
if (!(m_done = (m_iteration == m_varp->unpacked().left()))) m_iteration += m_direction;
}
virtual vpiHandle dovpi_scan() override {
vpiHandle dovpi_scan() override {
if (VL_UNLIKELY(m_done)) {
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
@@ -438,9 +439,9 @@ public:
static VerilatedVpioModule* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioModule*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiModule; }
virtual const char* name() const override { return m_name; }
virtual const char* fullname() const override { return m_fullname; }
uint32_t type() const override { return vpiModule; }
const char* name() const override { return m_name; }
const char* fullname() const override { return m_fullname; }
};
class VerilatedVpioModuleIter final : public VerilatedVpio {
@@ -452,12 +453,12 @@ public:
: m_vec{&vec} {
m_it = m_vec->begin();
}
virtual ~VerilatedVpioModuleIter() override = default;
~VerilatedVpioModuleIter() override = default;
static VerilatedVpioModuleIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioModuleIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiIterator; }
virtual vpiHandle dovpi_scan() override {
uint32_t type() const override { return vpiIterator; }
vpiHandle dovpi_scan() override {
if (m_it == m_vec->end()) {
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
+2 -1
View File
@@ -25,6 +25,7 @@
#define VERILATOR_VERILATED_VPI_H_
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_syms.h"
@@ -47,7 +48,7 @@ public:
static bool callValueCbs() VL_MT_UNSAFE_ONE;
/// Call callbacks of arbitrary types.
/// User wrapper code should call this from their main loops.
static bool callCbs(const uint32_t reason) VL_MT_UNSAFE_ONE;
static bool callCbs(uint32_t reason) VL_MT_UNSAFE_ONE;
/// Returns time of the next registered VPI callback, or
/// ~(0ULL) if none are registered
static QData cbNextDeadline() VL_MT_UNSAFE_ONE;
+73 -31
View File
@@ -39,7 +39,7 @@
#ifdef __GNUC__
# define VL_ATTR_ALIGNED(alignment) __attribute__((aligned(alignment)))
# define VL_ATTR_ALWINLINE __attribute__((always_inline))
# define VL_ATTR_ALWINLINE __attribute__((always_inline)) inline
# define VL_ATTR_NOINLINE __attribute__((noinline))
# define VL_ATTR_COLD __attribute__((cold))
# define VL_ATTR_HOT __attribute__((hot))
@@ -69,9 +69,9 @@
# define VL_EXCLUDES(x) __attribute__((locks_excluded(x)))
# define VL_SCOPED_CAPABILITY __attribute__((scoped_lockable))
# endif
# define VL_LIKELY(x) __builtin_expect(!!(x), 1)
# define VL_UNLIKELY(x) __builtin_expect(!!(x), 0)
# define VL_UNREACHABLE __builtin_unreachable();
# define VL_LIKELY(x) __builtin_expect(!!(x), 1) // Prefer over C++20 [[likely]]
# define VL_UNLIKELY(x) __builtin_expect(!!(x), 0) // Prefer over C++20 [[unlikely]]
# define VL_UNREACHABLE __builtin_unreachable() // C++23 std::unreachable()
# define VL_PREFETCH_RD(p) __builtin_prefetch((p), 0)
# define VL_PREFETCH_RW(p) __builtin_prefetch((p), 1)
#endif
@@ -163,14 +163,22 @@
// Comment tag that Function is pure (and thus also VL_MT_SAFE)
#define VL_PURE
// Comment tag that function is threadsafe when VL_THREADED
#define VL_MT_SAFE
#if defined(__clang__)
# define VL_MT_SAFE __attribute__((annotate("MT_SAFE")))
#else
# define VL_MT_SAFE
#endif
// Comment tag that function is threadsafe when VL_THREADED, only
// during normal operation (post-init)
#define VL_MT_SAFE_POSTINIT
// Attribute that function is clang threadsafe and uses given mutex
#define VL_MT_SAFE_EXCLUDES(mutex) VL_EXCLUDES(mutex)
// Comment tag that function is not threadsafe when VL_THREADED
#define VL_MT_UNSAFE
#if defined(__clang__)
# define VL_MT_UNSAFE __attribute__((annotate("MT_UNSAFE")))
#else
# define VL_MT_UNSAFE
#endif
// Comment tag that function is not threadsafe when VL_THREADED,
// protected to make sure single-caller
#define VL_MT_UNSAFE_ONE
@@ -218,22 +226,33 @@
// C++-2011
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(VL_CPPCHECK)
# ifndef VL_NO_LEGACY
// These are deprecated historical defines. We leave them in case users referenced them.
# define VL_EQ_DELETE = delete
# define vl_unique_ptr std::unique_ptr
# define vl_unordered_map std::unordered_map
# define vl_unordered_set std::unordered_set
# define VL_INCLUDE_UNORDERED_MAP <unordered_map>
# define VL_INCLUDE_UNORDERED_SET <unordered_set>
# define VL_FINAL final
# define VL_MUTABLE mutable
# define VL_OVERRIDE override
# endif
#else
# error "Verilator requires a C++11 or newer compiler"
#endif
#ifndef VL_NO_LEGACY
// These are deprecated historical defines. We leave them in case users referenced them.
# define VL_EQ_DELETE = delete
# define vl_unique_ptr std::unique_ptr
# define vl_unordered_map std::unordered_map
# define vl_unordered_set std::unordered_set
# define VL_INCLUDE_UNORDERED_MAP <unordered_map>
# define VL_INCLUDE_UNORDERED_SET <unordered_set>
# define VL_FINAL final
# define VL_MUTABLE mutable
# define VL_OVERRIDE override
#endif
//=========================================================================
// C++-2017
#if __cplusplus >= 201703L
# define VL_CONSTEXPR_CXX17 constexpr
#else
# define VL_CONSTEXPR_CXX17
#endif
//=========================================================================
// Optimization
@@ -245,14 +264,11 @@
// Internal coverage
#ifdef VL_GCOV
extern "C" {
void __gcov_flush(); // gcc sources gcc/gcov-io.h has the prototype
}
// Flush internal code coverage data before e.g. std::abort()
# define VL_GCOV_FLUSH() \
__gcov_flush()
extern "C" void __gcov_dump();
// Dump internal code coverage data before e.g. std::abort()
# define VL_GCOV_DUMP() __gcov_dump()
#else
# define VL_GCOV_FLUSH()
# define VL_GCOV_DUMP()
#endif
//=========================================================================
@@ -446,7 +462,8 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
// or 0x0 if not implemented on this platform
#define VL_GET_CPU_TICK(val) \
{ \
uint32_t hi, lo; \
uint32_t hi; \
uint32_t lo; \
asm volatile("rdtsc" : "=a"(lo), "=d"(hi)); \
(val) = ((uint64_t)lo) | (((uint64_t)hi) << 32); \
}
@@ -521,25 +538,50 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
#define VL_STRINGIFY(x) VL_STRINGIFY2(x)
#define VL_STRINGIFY2(x) #x
//=========================================================================
// Offset of field in type
// Address zero can cause compiler problems
#define VL_OFFSETOF(type, field) \
(reinterpret_cast<size_t>(&(reinterpret_cast<type*>(0x10000000)->field)) - 0x10000000)
//=========================================================================
// Conversions
#include <utility>
namespace vlstd {
template <typename T> struct reverse_wrapper {
template <typename T>
struct reverse_wrapper {
const T& m_v;
explicit reverse_wrapper(const T& a_v)
: m_v(a_v) {}
inline auto begin() -> decltype(m_v.rbegin()) { return m_v.rbegin(); }
inline auto end() -> decltype(m_v.rend()) { return m_v.rend(); }
auto begin() -> decltype(m_v.rbegin()) { return m_v.rbegin(); }
auto end() -> decltype(m_v.rend()) { return m_v.rend(); }
};
// C++20's std::ranges::reverse_view
template <typename T> reverse_wrapper<T> reverse_view(const T& v) { return reverse_wrapper<T>(v); }
template <typename T>
reverse_wrapper<T> reverse_view(const T& v) {
return reverse_wrapper<T>(v);
}
// C++17's std::as_const
template <class T> T const& as_const(T& v) { return v; }
template <class T>
T const& as_const(T& v) {
return v;
}
// C++14's std::exchange
template <class T, class U = T>
T exchange(T& obj, U&& new_value) {
T old_value = std::move(obj);
obj = std::forward<U>(new_value);
return old_value;
}
}; // namespace vlstd
//=========================================================================
+1
View File
@@ -48,6 +48,7 @@ exclude_line_regexp(r'\bNUM_ASSERT')
exclude_line_regexp(r'\bERROR_RSVD_WORD')
exclude_line_regexp(r'\bV3ERROR_NA')
exclude_line_regexp(r'\bUINFO\b')
exclude_line_regexp(r'\bVL_DEFINE_DEBUG_FUNCTIONS\b')
# Exclude for branch coverage only
exclude_branch_regexp(r'\bdebug\(\)')
+38 -10
View File
@@ -79,7 +79,7 @@ ifeq ($(VL_NOOPT),1)
CPPFLAGS += -O0
else ifeq ($(VL_DEBUG),)
# Optimize
CPPFLAGS += -O2
CPPFLAGS += -O3
else
# Debug
CPPFLAGS += @CFG_CXXFLAGS_DEBUG@ -DVL_DEBUG -D_GLIBCXX_DEBUG
@@ -166,7 +166,6 @@ RAW_OBJS = \
V3Case.o \
V3Cast.o \
V3Cdc.o \
V3Changed.o \
V3Class.o \
V3Clean.o \
V3Clock.o \
@@ -181,7 +180,15 @@ RAW_OBJS = \
V3Depth.o \
V3DepthBlock.o \
V3Descope.o \
V3Dfg.o \
V3DfgAstToDfg.o \
V3DfgDecomposition.o \
V3DfgDfgToAst.o \
V3DfgOptimizer.o \
V3DfgPasses.o \
V3DfgPeephole.o \
V3DupFinder.o \
V3Timing.o \
V3EmitCBase.o \
V3EmitCConstPool.o \
V3EmitCFunc.o \
@@ -201,7 +208,6 @@ RAW_OBJS = \
V3FileLine.o \
V3Force.o \
V3Gate.o \
V3GenClk.o \
V3Global.o \
V3Graph.o \
V3GraphAlg.o \
@@ -239,6 +245,11 @@ RAW_OBJS = \
V3ProtectLib.o \
V3Randomize.o \
V3Reloop.o \
V3Sched.o \
V3SchedAcyclic.o \
V3SchedPartition.o \
V3SchedReplicate.o \
V3SchedTiming.o \
V3Scope.o \
V3Scoreboard.o \
V3Slice.o \
@@ -276,13 +287,30 @@ VLCOV_OBJS = \
NON_STANDALONE_HEADERS = \
V3AstInlines.h \
V3AstNodes.h \
V3AstNodeDType.h \
V3AstNodeMath.h \
V3AstNodeOther.h \
V3DfgVertices.h \
V3WidthCommit.h \
AST_DEFS := \
V3AstNodeDType.h \
V3AstNodeMath.h \
V3AstNodeOther.h \
DFG_DEFS := \
V3DfgVertices.h
#### astgen common flags
ASTGENFLAGS = -I $(srcdir)
ASTGENFLAGS += $(foreach f,$(AST_DEFS),--astdef $f)
ASTGENFLAGS += $(foreach f,$(DFG_DEFS),--dfgdef $f)
#### Linking
ifeq ($(VL_VLCOV),)
PREDEP_H = V3Ast__gen_classes.h
PREDEP_H = V3Ast__gen_forward_class_decls.h
OBJS += $(RAW_OBJS) $(NC_OBJS)
else
PREDEP_H =
@@ -301,8 +329,8 @@ V3Number_test: V3Number_test.o
#### Modules
%__gen.cpp: %.cpp $(ASTGEN) V3Ast.h V3AstNodes.h
$(PYTHON3) $(ASTGEN) -I $(srcdir) $*.cpp
%__gen.cpp: %.cpp $(ASTGEN) $(AST_DEFS) $(DFG_DEFS)
$(PYTHON3) $(ASTGEN) $(ASTGENFLAGS) $*.cpp
%.o: %.cpp
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSWALL} -c $< -o $@
@@ -324,7 +352,7 @@ V3PreProc.o: V3PreProc.cpp V3PreLex.yy.cpp
#### Generated files
# Target rule called before parallel build to make generated files
serial:: V3Ast__gen_classes.h V3ParseBison.c
serial:: V3Ast__gen_forward_class_decls.h V3ParseBison.c
serial_vlcov:: vlcovgen.d
@@ -332,8 +360,8 @@ vlcovgen.d: $(VLCOVGEN) $(srcdir)/../include/verilated_cov_key.h
$(PYTHON3) $(VLCOVGEN) --srcdir $(srcdir)
touch $@
V3Ast__gen_classes.h : $(ASTGEN) V3Ast.h V3AstNodes.h
$(PYTHON3) $(ASTGEN) -I $(srcdir) --classes
V3Ast__gen_forward_class_decls.h: $(ASTGEN) $(AST_DEFS) $(DFG_DEFS)
$(PYTHON3) $(ASTGEN) $(ASTGENFLAGS) --classes
V3ParseBison.h: V3ParseBison.c
+239 -215
View File
@@ -29,14 +29,17 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Active.h"
#include "V3Ast.h"
#include "V3Const.h"
#include "V3Global.h"
#include "V3Graph.h"
#include <unordered_map>
VL_DEFINE_DEBUG_FUNCTIONS;
//***** See below for main transformation engine
//######################################################################
@@ -65,8 +68,8 @@ public:
: V3GraphVertex{graphp}
, m_name{name}
, m_type{type} {}
virtual string name() const override { return m_name + " " + typestr(); }
virtual string dotColor() const override { return user() ? "green" : "black"; }
string name() const override { return m_name + " " + typestr(); }
string dotColor() const override { return user() ? "green" : "black"; }
virtual int type() const { return m_type; }
};
@@ -75,11 +78,9 @@ public:
class LatchDetectGraph final : public V3Graph {
protected:
LatchDetectGraphVertex* m_curVertexp; // Current latch detection graph vertex
LatchDetectGraphVertex* m_curVertexp = nullptr; // Current latch detection graph vertex
std::vector<AstVarRef*> m_outputs; // Vector of lvalues encountered on this pass
VL_DEBUG_FUNC; // Declare debug()
static LatchDetectGraphVertex* castVertexp(void* vertexp) {
return reinterpret_cast<LatchDetectGraphVertex*>(vertexp);
}
@@ -119,7 +120,7 @@ protected:
public:
LatchDetectGraph() { clear(); }
virtual ~LatchDetectGraph() override { clear(); }
~LatchDetectGraph() override { clear(); }
// ACCESSORS
LatchDetectGraphVertex* currentp() { return m_curVertexp; }
void currentp(LatchDetectGraphVertex* vertex) { m_curVertexp = vertex; }
@@ -128,7 +129,7 @@ public:
// Start a new if/else tracking graph
// See NODE STATE comment in ActiveLatchCheckVisitor
AstNode::user1ClearTree();
m_curVertexp = new LatchDetectGraphVertex(this, "ROOT");
m_curVertexp = new LatchDetectGraphVertex{this, "ROOT"};
}
// Clear out userp field of referenced outputs on destruction
// (occurs at the end of each combinational always block)
@@ -164,7 +165,7 @@ public:
} else {
outVertexp = castVertexp(nodep->varp()->user1p());
}
new V3GraphEdge(this, m_curVertexp, outVertexp, 1);
new V3GraphEdge{this, m_curVertexp, outVertexp, 1};
}
// Run latchCheckInternal on each variable assigned by the always block to see if all control
// paths make an assignment. Detected latches are flagged in the variables AstVar
@@ -182,7 +183,7 @@ public:
<< " (not all control paths of combinational always assign a value)\n"
<< nodep->warnMore()
<< "... Suggest use of always_latch for intentional latches");
if (debug() >= 9) dumpDotFilePrefixed("latch_" + vrp->name());
if (dumpGraph() >= 9) dumpDotFilePrefixed("latch_" + vrp->name());
}
vertp->user(false); // Clear again (see above)
vrp->varp()->isLatched(latch_detected);
@@ -197,17 +198,14 @@ public:
//######################################################################
// Collect existing active names
class ActiveBaseVisitor VL_NOT_FINAL : public VNVisitor {
protected:
VL_DEBUG_FUNC; // Declare debug()
};
class ActiveNamer final : public ActiveBaseVisitor {
class ActiveNamer final : public VNVisitor {
private:
// STATE
AstScope* m_scopep = nullptr; // Current scope to add statement to
AstActive* m_iActivep = nullptr; // For current scope, the IActive we're building
AstActive* m_cActivep = nullptr; // For current scope, the SActive(combo) we're building
AstActive* m_sActivep = nullptr; // For current scope, the Static active we're building
AstActive* m_iActivep = nullptr; // For current scope, the Initial active we're building
AstActive* m_fActivep = nullptr; // For current scope, the Final active we're building
AstActive* m_cActivep = nullptr; // For current scope, the Combo active we're building
// Map from AstSenTree (equivalence) to the corresponding AstActive created.
std::unordered_map<VNRef<AstSenTree>, AstActive*> m_activeMap;
@@ -215,49 +213,57 @@ private:
// METHODS
void addActive(AstActive* nodep) {
UASSERT_OBJ(m_scopep, nodep, "nullptr scope");
m_scopep->addActivep(nodep);
m_scopep->addBlocksp(nodep);
}
// VISITORS
virtual void visit(AstScope* nodep) override {
void visit(AstScope* nodep) override {
m_scopep = nodep;
m_sActivep = nullptr;
m_iActivep = nullptr;
m_fActivep = nullptr;
m_cActivep = nullptr;
m_activeMap.clear();
iterateChildren(nodep);
// Don't clear scopep, the namer persists beyond this visit
}
virtual void visit(AstSenTree* nodep) override {
void visit(AstSenTree* nodep) override {
// Simplify sensitivity list
VL_DO_DANGLING(V3Const::constifyExpensiveEdit(nodep), nodep);
}
//--------------------
virtual void visit(AstNodeStmt*) override {} // Accelerate
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNodeStmt*) override {} // Accelerate
void visit(AstNode* nodep) override { iterateChildren(nodep); }
// Specialized below for the special sensitivity classes
template <typename SenItemKind>
AstActive*& getSpecialActive();
public:
// METHODS
AstScope* scopep() { return m_scopep; }
AstActive* getCActive(FileLine* fl) {
if (!m_cActivep) {
m_cActivep = new AstActive(
fl, "combo", new AstSenTree(fl, new AstSenItem(fl, AstSenItem::Combo())));
m_cActivep->sensesStorep(m_cActivep->sensesp());
addActive(m_cActivep);
}
return m_cActivep;
// Make a new AstActive sensitive to the given special sensitivity class and return it
template <typename SenItemKind>
AstActive* makeSpecialActive(FileLine* const fl) {
AstSenTree* const senTreep = new AstSenTree{fl, new AstSenItem{fl, SenItemKind{}}};
auto* const activep = new AstActive{fl, "", senTreep};
activep->sensesStorep(activep->sensesp());
addActive(activep);
return activep;
}
AstActive* getIActive(FileLine* fl) {
if (!m_iActivep) {
m_iActivep = new AstActive(
fl, "initial", new AstSenTree(fl, new AstSenItem(fl, AstSenItem::Initial())));
m_iActivep->sensesStorep(m_iActivep->sensesp());
addActive(m_iActivep);
}
return m_iActivep;
// Return an AstActive sensitive to the given special sensitivity class (possibly pre-created)
template <typename SenItemKind>
AstActive* getSpecialActive(FileLine* fl) {
AstActive*& cachep = getSpecialActive<SenItemKind>();
if (!cachep) cachep = makeSpecialActive<SenItemKind>(fl);
return cachep;
}
// Return an AstActive that is sensitive to a SenTree equivalent to the given sentreep.
AstActive* getActive(FileLine* fl, AstSenTree* sensesp) {
UASSERT(sensesp, "Must be non-null");
auto it = m_activeMap.find(*sensesp);
// If found matching AstActive, return it
@@ -265,7 +271,7 @@ public:
// No such AstActive yet, creat it, and add to map.
AstSenTree* const newsenp = sensesp->cloneTree(false);
AstActive* const activep = new AstActive(fl, "sequent", newsenp);
AstActive* const activep = new AstActive{fl, "sequent", newsenp};
activep->sensesStorep(activep->sensesp());
addActive(activep);
m_activeMap.emplace(*newsenp, activep);
@@ -274,14 +280,31 @@ public:
// CONSTRUCTORS
ActiveNamer() = default;
virtual ~ActiveNamer() override = default;
~ActiveNamer() override = default;
void main(AstScope* nodep) { iterate(nodep); }
};
template <>
AstActive*& ActiveNamer::getSpecialActive<AstSenItem::Static>() {
return m_sActivep;
}
template <>
AstActive*& ActiveNamer::getSpecialActive<AstSenItem::Initial>() {
return m_iActivep;
}
template <>
AstActive*& ActiveNamer::getSpecialActive<AstSenItem::Final>() {
return m_fActivep;
}
template <>
AstActive*& ActiveNamer::getSpecialActive<AstSenItem::Combo>() {
return m_cActivep;
}
//######################################################################
// Latch checking visitor
class ActiveLatchCheckVisitor final : public ActiveBaseVisitor {
class ActiveLatchCheckVisitor final : public VNVisitor {
private:
// NODE STATE
// Input:
@@ -290,40 +313,40 @@ private:
// STATE
LatchDetectGraph m_graph; // Graph used to detect latches in combo always
// VISITORS
virtual void visit(AstVarRef* nodep) {
void visit(AstVarRef* nodep) override {
const AstVar* const varp = nodep->varp();
if (nodep->access().isWriteOrRW() && varp->isSignal() && !varp->isUsedLoopIdx()) {
m_graph.addAssignment(nodep);
}
}
virtual void visit(AstNodeIf* nodep) {
void visit(AstNodeIf* nodep) override {
if (!nodep->isBoundsCheck()) {
LatchDetectGraphVertex* const parentp = m_graph.currentp();
LatchDetectGraphVertex* const branchp = m_graph.addPathVertex(parentp, "BRANCH", true);
m_graph.addPathVertex(branchp, "IF");
iterateAndNextNull(nodep->ifsp());
iterateAndNextNull(nodep->thensp());
m_graph.addPathVertex(branchp, "ELSE");
iterateAndNextNull(nodep->elsesp());
m_graph.currentp(parentp);
}
}
//--------------------
virtual void visit(AstNode* nodep) { iterateChildren(nodep); }
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
ActiveLatchCheckVisitor(AstNode* nodep, VAlwaysKwd kwd) {
ActiveLatchCheckVisitor(AstNode* nodep, bool expectLatch) {
m_graph.begin();
iterate(nodep);
m_graph.latchCheck(nodep, kwd == VAlwaysKwd::ALWAYS_LATCH);
m_graph.latchCheck(nodep, expectLatch);
}
virtual ~ActiveLatchCheckVisitor() = default;
~ActiveLatchCheckVisitor() override = default;
};
//######################################################################
// Replace unsupported non-blocking assignments with blocking assignments
class ActiveDlyVisitor final : public ActiveBaseVisitor {
class ActiveDlyVisitor final : public VNVisitor {
public:
enum CheckType : uint8_t { CT_SEQ, CT_COMB, CT_INITIAL };
@@ -332,7 +355,7 @@ private:
const CheckType m_check; // Process type we are checking
// VISITORS
virtual void visit(AstAssignDly* nodep) override {
void visit(AstAssignDly* nodep) override {
// Non-blocking assignments are OK in sequential processes
if (m_check == CT_SEQ) return;
@@ -350,17 +373,19 @@ private:
}
// Convert to blocking assignment
nodep->replaceWith(new AstAssign{nodep->fileline(), //
nodep->lhsp()->unlinkFrBack(), //
nodep->rhsp()->unlinkFrBack()});
nodep->replaceWith(new AstAssign{
nodep->fileline(), //
nodep->lhsp()->unlinkFrBack(), //
nodep->rhsp()->unlinkFrBack(), //
nodep->timingControlp() ? nodep->timingControlp()->unlinkFrBack() : nullptr});
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
virtual void visit(AstAssign* nodep) override {
void visit(AstAssign* nodep) override {
// Blocking assignments are always OK in combinational (and initial/final) processes
if (m_check != CT_SEQ) return;
const bool ignore = nodep->lhsp()->forall<AstVarRef>([&](const AstVarRef* refp) {
const bool ignore = nodep->lhsp()->forall([&](const AstVarRef* refp) {
// Ignore reads (e.g.: index expressions)
if (refp->access().isReadOnly()) return true;
const AstVar* const varp = refp->varp();
@@ -379,7 +404,7 @@ private:
}
//--------------------
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
@@ -387,97 +412,35 @@ public:
: m_check{check} {
iterate(nodep);
}
virtual ~ActiveDlyVisitor() override = default;
~ActiveDlyVisitor() override = default;
};
//######################################################################
// Active class functions
class ActiveVisitor final : public ActiveBaseVisitor {
class ActiveVisitor final : public VNVisitor {
private:
// NODE STATE
// Each call to V3Const::constify
// AstVarScope::user1() bool: This VarScope is referenced in the sensitivity list
// AstVarScope::user2() bool: This VarScope is written in the current process
// AstNode::user4() Used by V3Const::constify, called below
// STATE
ActiveNamer m_namer; // Tracking of active names
AstCFunc* m_scopeFinalp = nullptr; // Final function for this scope
bool m_itemCombo = false; // Found a SenItem combo
bool m_itemSequent = false; // Found a SenItem sequential
// VISITORS
virtual void visit(AstScope* nodep) override {
// Create required actives and add to scope
UINFO(4, " SCOPE " << nodep << endl);
// Clear last scope's names, and collect this scope's existing names
m_namer.main(nodep);
m_scopeFinalp = nullptr;
iterateChildren(nodep);
}
virtual void visit(AstActive* nodep) override {
// Actives are being formed, so we can ignore any already made
}
virtual void visit(AstInitialStatic* nodep) override {
// Relink to IACTIVE, unless already under it
UINFO(4, " INITIAL " << nodep << endl);
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
AstActive* const wantactivep = m_namer.getIActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstInitial* nodep) override {
// Relink to IACTIVE, unless already under it
UINFO(4, " INITIAL " << nodep << endl);
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
AstActive* const wantactivep = m_namer.getIActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstAssignAlias* nodep) override {
// Relink to CACTIVE, unless already under it
UINFO(4, " ASSIGNW " << nodep << endl);
AstActive* const wantactivep = m_namer.getCActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstAssignW* nodep) override {
// Relink to CACTIVE, unless already under it
UINFO(4, " ASSIGNW " << nodep << endl);
AstActive* const wantactivep = m_namer.getCActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstCoverToggle* nodep) override {
// Relink to CACTIVE, unless already under it
UINFO(4, " COVERTOGGLE " << nodep << endl);
AstActive* const wantactivep = m_namer.getCActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstFinal* nodep) override {
// Relink to CFUNC for the final
UINFO(4, " FINAL " << nodep << endl);
if (!nodep->bodysp()) { // Empty, Kill it.
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
if (!m_scopeFinalp) {
m_scopeFinalp = new AstCFunc(
nodep->fileline(), "_final_" + m_namer.scopep()->nameDotless(), m_namer.scopep());
m_scopeFinalp->dontCombine(true);
m_scopeFinalp->isFinal(true);
m_scopeFinalp->isStatic(false);
m_scopeFinalp->isLoose(true);
m_scopeFinalp->slow(true);
m_namer.scopep()->addActivep(m_scopeFinalp);
}
nodep->unlinkFrBack();
m_scopeFinalp->addStmtsp(nodep->bodysp()->unlinkFrBackWithNext());
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
bool m_clockedProcess = false; // Whether current process is a clocked process
bool m_allChanged = false; // Whether all SenItem in the SenTree are ET_CHANGED
bool m_walkingBody = false; // Walking body of a process
bool m_canBeComb = false; // Whether current clocked process can be turned into a comb process
// METHODS
template <typename T>
void moveUnderSpecial(AstNode* nodep) {
AstActive* const wantactivep = m_namer.getSpecialActive<T>(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
void visitAlways(AstNode* nodep, AstSenTree* oldsensesp, VAlwaysKwd kwd) {
// Move always to appropriate ACTIVE based on its sense list
if (oldsensesp && oldsensesp->sensesp() && oldsensesp->sensesp()->isNever()) {
@@ -488,117 +451,178 @@ private:
return;
}
// Read sensitivities
m_itemCombo = false;
m_itemSequent = false;
iterateAndNextNull(oldsensesp);
bool combo = m_itemCombo;
bool sequent = m_itemSequent;
{
const VNUser1InUse user1InUse;
if (!combo && !sequent) combo = true; // If no list, Verilog 2000: always @ (*)
if (combo && sequent) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Mixed edge (pos/negedge) and activity "
"(no edge) sensitive activity list");
sequent = false;
}
AstActive* wantactivep = nullptr;
if (combo && !sequent) {
// Combo: Relink to ACTIVE(combo)
wantactivep = m_namer.getCActive(nodep->fileline());
} else {
// Sequential: Build a ACTIVE(name)
// OPTIMIZE: We could substitute a constant for things in the sense list, for example
// always (posedge RESET) { if (RESET).... } we know RESET is true.
// Summarize a long list of combo inputs as just "combo"
#ifndef __COVERITY__ // Else dead code on next line.
if (combo) {
oldsensesp->addSensesp(new AstSenItem(nodep->fileline(), AstSenItem::Combo()));
// Walk sensitivity list
m_clockedProcess = false;
m_allChanged = true;
if (oldsensesp) {
oldsensesp->unlinkFrBack();
iterateChildrenConst(oldsensesp);
}
// If all SenItems are ET_CHANGE, then walk the body to determine if this process
// could be turned into a combinational process instead.
if (m_allChanged) {
const VNUser2InUse user2InUse;
m_walkingBody = true;
m_canBeComb = true;
iterateChildrenConst(nodep);
m_walkingBody = false;
if (m_canBeComb) m_clockedProcess = false;
}
#endif
wantactivep = m_namer.getActive(nodep->fileline(), oldsensesp);
}
AstActive* const wantactivep
= !m_clockedProcess ? m_namer.getSpecialActive<AstSenItem::Combo>(nodep->fileline())
: oldsensesp ? m_namer.getActive(nodep->fileline(), oldsensesp)
: m_namer.getSpecialActive<AstSenItem::Initial>(nodep->fileline());
// Delete sensitivity list
if (oldsensesp) {
VL_DO_DANGLING(oldsensesp->unlinkFrBackWithNext()->deleteTree(), oldsensesp);
}
if (oldsensesp) VL_DO_DANGLING(oldsensesp->deleteTree(), oldsensesp);
// Move node to new active
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
// Warn and/or convert any delayed assignments
if (combo && !sequent) {
ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_COMB};
const ActiveLatchCheckVisitor latchvisitor{nodep, kwd};
} else if (!combo && sequent) {
ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_SEQ};
// Warn and convert any delayed assignments
{
ActiveDlyVisitor{nodep, m_clockedProcess ? ActiveDlyVisitor::CT_SEQ
: ActiveDlyVisitor::CT_COMB};
}
// check combinational processes for latches
if (!m_clockedProcess || kwd == VAlwaysKwd::ALWAYS_LATCH) {
const ActiveLatchCheckVisitor latchvisitor{nodep, kwd == VAlwaysKwd::ALWAYS_LATCH};
}
}
virtual void visit(AstAlways* nodep) override {
// Move always to appropriate ACTIVE based on its sense list
UINFO(4, " ALW " << nodep << endl);
// if (debug() >= 9) nodep->dumpTree(cout, " Alw: ");
if (!nodep->bodysp()) {
// Empty always. Kill it.
void visitSenItems(AstNode* nodep) {
if (v3Global.opt.timing().isSetTrue()) {
nodep->foreach([this](AstSenItem* senItemp) { visit(senItemp); });
}
}
// VISITORS
void visit(AstScope* nodep) override {
m_namer.main(nodep); // Clear last scope's names, and collect this scope's existing names
iterateChildren(nodep);
}
void visit(AstActive* nodep) override {
// Actives are being formed, so we can ignore any already made
}
void visit(AstInitialStatic* nodep) override { moveUnderSpecial<AstSenItem::Static>(nodep); }
void visit(AstInitial* nodep) override {
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
visitSenItems(nodep);
moveUnderSpecial<AstSenItem::Initial>(nodep);
}
void visit(AstFinal* nodep) override {
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
moveUnderSpecial<AstSenItem::Final>(nodep);
}
void visit(AstAssignAlias* nodep) override { moveUnderSpecial<AstSenItem::Combo>(nodep); }
void visit(AstCoverToggle* nodep) override { moveUnderSpecial<AstSenItem::Combo>(nodep); }
void visit(AstAssignW* nodep) override { moveUnderSpecial<AstSenItem::Combo>(nodep); }
void visit(AstAlways* nodep) override {
if (!nodep->stmtsp()) { // Empty always. Remove it now.
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
visitSenItems(nodep);
visitAlways(nodep, nodep->sensesp(), nodep->keyword());
}
virtual void visit(AstAlwaysPostponed* nodep) override {
UINFO(4, " ALW " << nodep << endl);
if (!nodep->bodysp()) {
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
visitAlways(nodep, nullptr, VAlwaysKwd::ALWAYS);
void visit(AstAlwaysPostponed* nodep) override {
// Might be empty with later optimizations, so this assertion can be removed,
// but for now it is guaranteed to be not empty.
UASSERT_OBJ(nodep->stmtsp(), nodep, "Should not be empty");
// Make a new active for it, needs to be the only item under the active for V3Sched
AstActive* const activep = m_namer.makeSpecialActive<AstSenItem::Combo>(nodep->fileline());
activep->addStmtsp(nodep->unlinkFrBack());
}
virtual void visit(AstAlwaysPublic* nodep) override {
// Move always to appropriate ACTIVE based on its sense list
UINFO(4, " ALWPub " << nodep << endl);
// if (debug() >= 9) nodep->dumpTree(cout, " Alw: ");
void visit(AstAlwaysPublic* nodep) override {
visitAlways(nodep, nodep->sensesp(), VAlwaysKwd::ALWAYS);
}
virtual void visit(AstSenItem* nodep) override {
if (nodep->varrefp()) {
if (const AstBasicDType* const basicp = nodep->varrefp()->dtypep()->basicp()) {
if (basicp->isEventValue()) {
// Events need to be treated as active high so we only activate on event being
// 1
UINFO(8, "Demote event to HIGHEDGE " << nodep << endl);
nodep->edgeType(VEdgeType::ET_HIGHEDGE);
}
void visit(AstCFunc* nodep) override { visitSenItems(nodep); }
void visit(AstSenItem* nodep) override {
UASSERT_OBJ(!m_walkingBody, nodep,
"Should not reach here when walking body without --timing");
if (!nodep->sensp()) return; // Ignore sequential items (e.g.: initial, comb, etc.)
m_clockedProcess = true;
if (nodep->edgeType() != VEdgeType::ET_CHANGED) m_allChanged = false;
if (const auto* const dtypep = nodep->sensp()->dtypep()) {
if (const auto* const basicp = dtypep->basicp()) {
if (basicp->isEvent()) nodep->edgeType(VEdgeType::ET_EVENT);
}
}
if (nodep->edgeType() == VEdgeType::ET_ANYEDGE) {
m_itemCombo = true;
// Delete the sensitivity
// We'll add it as a generic COMBO SenItem in a moment.
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
} else if (nodep->varrefp()) {
// V3LinkResolve should have cleaned most of these up
if (!nodep->varrefp()->width1()) {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: Non-single bit wide signal pos/negedge sensitivity: "
<< nodep->varrefp()->prettyNameQ());
}
m_itemSequent = true;
nodep->varrefp()->varp()->usedClock(true);
nodep->sensp()->foreach([](const AstVarRef* refp) {
refp->varp()->usedClock(true);
refp->varScopep()->user1(true);
});
}
void visit(AstVarRef* nodep) override {
AstVarScope* const vscp = nodep->varScopep();
if (nodep->access().isWriteOnly()) {
vscp->user2(true);
} else {
// If the variable is read before it is written (and is not a never-changing value),
// and is not in the sensitivity list, then this cannot be optimized into a
// combinational process
// TODO: live variable analysis would be more precise
if (!vscp->user2() && !vscp->varp()->valuep() && !vscp->user1()) m_canBeComb = false;
}
}
void visit(AstAssignDly* nodep) override {
m_canBeComb = false;
if (nodep->isTimingControl()) m_clockedProcess = true;
iterateChildrenConst(nodep);
}
void visit(AstFireEvent* nodep) override {
m_canBeComb = false;
iterateChildrenConst(nodep);
}
void visit(AstAssignForce* nodep) override {
m_canBeComb = false;
iterateChildrenConst(nodep);
}
void visit(AstRelease* nodep) override {
m_canBeComb = false;
iterateChildrenConst(nodep);
}
void visit(AstFork* nodep) override {
if (nodep->isTimingControl()) {
m_canBeComb = false;
m_clockedProcess = true;
}
// Do not iterate children, technically not part of this process
}
//--------------------
virtual void visit(AstNodeMath*) override {} // Accelerate
virtual void visit(AstVarScope*) override {} // Accelerate
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstVar*) override {} // Accelerate
void visit(AstVarScope*) override {} // Accelerate
void visit(AstNode* nodep) override {
if (!v3Global.opt.timing().isSetTrue() && m_walkingBody && !m_canBeComb) {
return; // Accelerate
}
if (!nodep->isPure()) m_canBeComb = false;
if (nodep->isTimingControl()) {
m_canBeComb = false;
m_clockedProcess = true;
return;
}
iterateChildren(nodep);
}
public:
// CONSTRUCTORS
explicit ActiveVisitor(AstNetlist* nodep) { iterate(nodep); }
virtual ~ActiveVisitor() override = default;
~ActiveVisitor() override = default;
};
//######################################################################
@@ -607,5 +631,5 @@ public:
void V3Active::activeAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ActiveVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("active", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("active", 0, dumpTree() >= 3);
}
+49 -33
View File
@@ -26,38 +26,48 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3ActiveTop.h"
#include "V3Ast.h"
#include "V3SenTree.h"
#include "V3Const.h"
#include "V3Global.h"
#include "V3SenTree.h"
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Active class functions
class ActiveTopVisitor final : public VNVisitor {
private:
// NODE STATE
// Entire netlist
// AstNode::user() bool. True if processed
// Each call to V3Const::constify
// AstNode::user4() Used by V3Const::constify, called below
const VNUser1InUse m_inuser1;
// STATE
SenTreeFinder m_finder; // Find global sentree's / add them under the AstTopScope
// METHODS
VL_DEBUG_FUNC; // Declare debug()
static bool isInitial(AstNode* nodep) {
const VNUser1InUse user1InUse;
// Return true if no variables that read.
return nodep->forall([&](const AstVarRef* refp) -> bool {
AstVarScope* const vscp = refp->varScopep();
// Note: Use same heuristic as ordering does to ignore written variables
// TODO: Use live variable analysis.
if (refp->access().isWriteOnly()) {
vscp->user1(true);
return true;
}
// Read or ReadWrite: OK if written before
return vscp->user1();
});
}
// VISITORS
virtual void visit(AstNodeModule* nodep) override {
void visit(AstNodeModule* nodep) override {
// Create required actives and add to module
// We can start ordering at a module, or a scope
UINFO(4, " MOD " << nodep << endl);
iterateChildren(nodep);
}
virtual void visit(AstActive* nodep) override {
void visit(AstActive* nodep) override {
UINFO(4, " ACTIVE " << nodep << endl);
// Remove duplicate clocks and such; sensesp() may change!
V3Const::constifyExpensiveEdit(nodep);
@@ -70,15 +80,6 @@ private:
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
// Copy combo tree to settlement tree with duplicated statements
if (sensesp->hasCombo()) {
AstSenTree* const newsentreep = new AstSenTree(
nodep->fileline(), new AstSenItem(nodep->fileline(), AstSenItem::Settle()));
AstActive* const newp = new AstActive(nodep->fileline(), "settle", newsentreep);
newp->sensesStorep(newsentreep);
if (nodep->stmtsp()) newp->addStmtsp(nodep->stmtsp()->cloneTree(true));
nodep->addNextHere(newp);
}
// Move the SENTREE for each active up to the global level.
// This way we'll easily see what clock domains are identical
AstSenTree* const wantp = m_finder.getSenTree(sensesp);
@@ -97,30 +98,45 @@ private:
}
nodep->sensesp(wantp);
}
// No need to do statements under it, they're already moved.
// iterateChildren(nodep);
// If this is combinational logic that does not read any variables, then it really is an
// initial block in disguise, so move such logic under an Initial AstActive, V3Order would
// prune these otherwise.
// TODO: we should warn for these if they were 'always @*' as some (including strictly
// compliant) simulators will never execute these.
if (nodep->sensesp()->hasCombo()) {
FileLine* const flp = nodep->fileline();
AstActive* initialp = nullptr;
for (AstNode *logicp = nodep->stmtsp(), *nextp; logicp; logicp = nextp) {
nextp = logicp->nextp();
if (!isInitial(logicp)) continue;
if (!initialp) initialp = new AstActive{flp, "", m_finder.getInitial()};
initialp->addStmtsp(logicp->unlinkFrBack());
}
if (initialp) nodep->addHereThisAsNext(initialp);
}
}
virtual void visit(AstNodeProcedure* nodep) override { // LCOV_EXCL_LINE
void visit(AstNodeProcedure* nodep) override { // LCOV_EXCL_LINE
nodep->v3fatalSrc("Node should have been under ACTIVE");
}
virtual void visit(AstAssignAlias* nodep) override { // LCOV_EXCL_LINE
void visit(AstAssignAlias* nodep) override { // LCOV_EXCL_LINE
nodep->v3fatalSrc("Node should have been under ACTIVE");
}
virtual void visit(AstAssignW* nodep) override { // LCOV_EXCL_LINE
void visit(AstAssignW* nodep) override { // LCOV_EXCL_LINE
nodep->v3fatalSrc("Node should have been under ACTIVE");
}
virtual void visit(AstAlwaysPublic* nodep) override { // LCOV_EXCL_LINE
void visit(AstAlwaysPublic* nodep) override { // LCOV_EXCL_LINE
nodep->v3fatalSrc("Node should have been under ACTIVE");
}
//--------------------
virtual void visit(AstNodeMath*) override {} // Accelerate
virtual void visit(AstVarScope*) override {} // Accelerate
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNodeMath*) override {} // Accelerate
void visit(AstVarScope*) override {} // Accelerate
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit ActiveTopVisitor(AstNetlist* nodep) { iterate(nodep); }
virtual ~ActiveTopVisitor() override = default;
~ActiveTopVisitor() override = default;
};
//######################################################################
@@ -129,5 +145,5 @@ public:
void V3ActiveTop::activeTopAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ActiveTopVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("activetop", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("activetop", 0, dumpTree() >= 3);
}
+112 -64
View File
@@ -17,11 +17,14 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Assert.h"
#include "V3Ast.h"
#include "V3Global.h"
#include "V3Stats.h"
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Assert class functions
@@ -44,6 +47,7 @@ private:
VDouble0 m_statAsNotImm; // Statistic tracking
VDouble0 m_statAsImm; // Statistic tracking
VDouble0 m_statAsFull; // Statistic tracking
bool m_inSampled = false; // True inside a sampled expression
// METHODS
string assertDisplayMessage(AstNode* nodep, const string& prefix, const string& message) {
@@ -55,7 +59,7 @@ private:
nodep->displayType(VDisplayType::DT_WRITE);
nodep->fmtp()->text(assertDisplayMessage(nodep, prefix, nodep->fmtp()->text()));
// cppcheck-suppress nullPointer
AstNode* const timenewp = new AstTime(nodep->fileline(), m_modp->timeunit());
AstNode* const timenewp = new AstTime{nodep->fileline(), m_modp->timeunit()};
if (AstNode* const timesp = nodep->fmtp()->exprsp()) {
timesp->unlinkFrBackWithNext();
timenewp->addNext(timesp);
@@ -65,13 +69,18 @@ private:
nodep->fmtp()->scopeNamep(new AstScopeName{nodep->fileline(), true});
}
}
AstSampled* newSampledExpr(AstNode* nodep) {
const auto sampledp = new AstSampled{nodep->fileline(), nodep};
sampledp->dtypeFrom(nodep);
return sampledp;
}
AstVarRef* newMonitorNumVarRefp(AstNode* nodep, VAccess access) {
if (!m_monitorNumVarp) {
m_monitorNumVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorNum",
nodep->findUInt64DType()};
v3Global.rootp()->dollarUnitPkgAddp()->addStmtp(m_monitorNumVarp);
v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorNumVarp);
}
const auto varrefp = new AstVarRef(nodep->fileline(), m_monitorNumVarp, access);
const auto varrefp = new AstVarRef{nodep->fileline(), m_monitorNumVarp, access};
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
return varrefp;
}
@@ -79,9 +88,9 @@ private:
if (!m_monitorOffVarp) {
m_monitorOffVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorOff",
nodep->findBitDType()};
v3Global.rootp()->dollarUnitPkgAddp()->addStmtp(m_monitorOffVarp);
v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorOffVarp);
}
const auto varrefp = new AstVarRef(nodep->fileline(), m_monitorOffVarp, access);
const auto varrefp = new AstVarRef{nodep->fileline(), m_monitorOffVarp, access};
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
return varrefp;
}
@@ -89,16 +98,17 @@ private:
// Add a internal if to check assertions are on.
// Don't make this a AND term, as it's unlikely to need to test this.
FileLine* const fl = nodep->fileline();
AstNode* const newp = new AstIf(
AstNodeIf* const newp = new AstIf{
fl,
(force ? new AstConst(fl, AstConst::BitTrue())
(force ? new AstConst{fl, AstConst::BitTrue{}}
: // If assertions are off, have constant propagation rip them out later
// This allows syntax errors and such to be detected normally.
(v3Global.opt.assertOn()
? static_cast<AstNode*>(
new AstCMath(fl, "vlSymsp->_vm_contextp__->assertOn()", 1))
: static_cast<AstNode*>(new AstConst(fl, AstConst::BitFalse())))),
nodep);
new AstCMath{fl, "vlSymsp->_vm_contextp__->assertOn()", 1})
: static_cast<AstNode*>(new AstConst{fl, AstConst::BitFalse{}}))),
nodep};
newp->isBoundsCheck(true); // To avoid LATCH warning
newp->user1(true); // Don't assert/cover this if
return newp;
}
@@ -106,11 +116,11 @@ private:
AstNode* newFireAssertUnchecked(AstNode* nodep, const string& message) {
// Like newFireAssert() but omits the asserts-on check
AstDisplay* const dispp
= new AstDisplay(nodep->fileline(), VDisplayType::DT_ERROR, message, nullptr, nullptr);
= new AstDisplay{nodep->fileline(), VDisplayType::DT_ERROR, message, nullptr, nullptr};
dispp->fmtp()->timeunit(m_modp->timeunit());
AstNode* const bodysp = dispp;
replaceDisplay(dispp, "%%Error"); // Convert to standard DISPLAY format
bodysp->addNext(new AstStop(nodep->fileline(), true));
bodysp->addNext(new AstStop{nodep->fileline(), true});
return bodysp;
}
@@ -146,7 +156,7 @@ private:
selfDestruct = true;
} else {
// V3Coverage assigned us a bucket to increment.
AstCoverInc* const covincp = VN_AS(snodep->coverincp(), CoverInc);
AstCoverInc* const covincp = VN_AS(snodep->coverincsp(), CoverInc);
UASSERT_OBJ(covincp, snodep, "Missing AstCoverInc under assertion");
covincp->unlinkFrBackWithNext(); // next() might have AstAssign for trace
if (message != "") covincp->declp()->comment(message);
@@ -154,7 +164,8 @@ private:
}
if (bodysp && passsp) bodysp = bodysp->addNext(passsp);
ifp = new AstIf(nodep->fileline(), propp, bodysp);
ifp = new AstIf{nodep->fileline(), propp, bodysp};
ifp->isBoundsCheck(true); // To avoid LATCH warning
bodysp = ifp;
} else if (VN_IS(nodep, Assert) || VN_IS(nodep, AssertIntrinsic)) {
if (nodep->immediate()) {
@@ -166,7 +177,8 @@ private:
if (passsp) passsp = newIfAssertOn(passsp, force);
if (failsp) failsp = newIfAssertOn(failsp, force);
if (!failsp) failsp = newFireAssertUnchecked(nodep, "'assert' failed.");
ifp = new AstIf(nodep->fileline(), propp, passsp, failsp);
ifp = new AstIf{nodep->fileline(), propp, passsp, failsp};
ifp->isBoundsCheck(true); // To avoid LATCH warning
// It's more LIKELY that we'll take the nullptr if clause
// than the sim-killing else clause:
ifp->branchPred(VBranchPred::BP_LIKELY);
@@ -177,7 +189,7 @@ private:
AstNode* newp;
if (sentreep) {
newp = new AstAlways(nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, bodysp);
newp = new AstAlways{nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, bodysp};
} else {
newp = bodysp;
}
@@ -195,7 +207,7 @@ private:
}
// VISITORS
virtual void visit(AstIf* nodep) override {
void visit(AstIf* nodep) override {
if (nodep->user1SetOnce()) return;
if (nodep->uniquePragma() || nodep->unique0Pragma()) {
const AstNodeIf* ifp = nodep;
@@ -208,7 +220,7 @@ private:
iterateAndNextNull(ifp->condp());
// Recurse into the true case.
iterateAndNextNull(ifp->ifsp());
iterateAndNextNull(ifp->thensp());
// If the last else is not an else if, recurse into that too.
if (ifp->elsesp() && !nextifp) { //
@@ -218,7 +230,7 @@ private:
// Build a bitmask of the true predicates
AstNode* const predp = ifp->condp()->cloneTree(false);
if (propp) {
propp = new AstConcat(nodep->fileline(), predp, propp);
propp = new AstConcat{nodep->fileline(), predp, propp};
} else {
propp = predp;
}
@@ -233,17 +245,18 @@ private:
const bool allow_none = nodep->unique0Pragma();
// Empty case means no property
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::BitFalse());
if (!propp) propp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
// Note: if this ends with an 'else', then we don't need to validate that one of the
// predicates evaluates to true.
AstNode* const ohot
= ((allow_none || hasDefaultElse)
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
: static_cast<AstNode*>(new AstOneHot(nodep->fileline(), propp)));
? static_cast<AstNode*>(new AstOneHot0{nodep->fileline(), propp})
: static_cast<AstNode*>(new AstOneHot{nodep->fileline(), propp}));
AstIf* const checkifp
= new AstIf(nodep->fileline(), new AstLogNot(nodep->fileline(), ohot),
newFireAssert(nodep, "'unique if' statement violated"), newifp);
= new AstIf{nodep->fileline(), new AstLogNot{nodep->fileline(), ohot},
newFireAssert(nodep, "'unique if' statement violated"), newifp};
checkifp->isBoundsCheck(true); // To avoid LATCH warning
checkifp->branchPred(VBranchPred::BP_UNLIKELY);
nodep->replaceWith(checkifp);
pushDeletep(nodep);
@@ -253,7 +266,7 @@ private:
}
//========== Case assertions
virtual void visit(AstCase* nodep) override {
void visit(AstCase* nodep) override {
iterateChildren(nodep);
if (!nodep->user1SetOnce()) {
bool has_default = false;
@@ -265,9 +278,9 @@ private:
// Need to add a default if there isn't one already
++m_statAsFull;
if (!has_default) {
nodep->addItemsp(new AstCaseItem(
nodep->addItemsp(new AstCaseItem{
nodep->fileline(), nullptr /*DEFAULT*/,
newFireAssert(nodep, "synthesis full_case, but non-match found")));
newFireAssert(nodep, "synthesis full_case, but non-match found")});
}
}
if (nodep->parallelPragma() || nodep->uniquePragma() || nodep->unique0Pragma()) {
@@ -296,24 +309,25 @@ private:
icondp->cloneTree(false));
}
if (propp) {
propp = new AstConcat(icondp->fileline(), onep, propp);
propp = new AstConcat{icondp->fileline(), onep, propp};
} else {
propp = onep;
}
}
}
// Empty case means no property
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::BitFalse());
if (!propp) propp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
const bool allow_none = has_default || nodep->unique0Pragma();
AstNode* const ohot
= (allow_none
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
: static_cast<AstNode*>(new AstOneHot(nodep->fileline(), propp)));
AstIf* const ifp = new AstIf(
nodep->fileline(), new AstLogNot(nodep->fileline(), ohot),
? static_cast<AstNode*>(new AstOneHot0{nodep->fileline(), propp})
: static_cast<AstNode*>(new AstOneHot{nodep->fileline(), propp}));
AstIf* const ifp = new AstIf{
nodep->fileline(), new AstLogNot{nodep->fileline(), ohot},
newFireAssert(nodep,
"synthesis parallel_case, but multiple matches found"));
"synthesis parallel_case, but multiple matches found")};
ifp->isBoundsCheck(true); // To avoid LATCH warning
ifp->branchPred(VBranchPred::BP_UNLIKELY);
nodep->addNotParallelp(ifp);
}
@@ -322,7 +336,7 @@ private:
}
//========== Past
virtual void visit(AstPast* nodep) override {
void visit(AstPast* nodep) override {
iterateChildren(nodep);
uint32_t ticks = 1;
if (nodep->ticksp()) {
@@ -331,34 +345,66 @@ private:
ticks = VN_AS(nodep->ticksp(), Const)->toUInt();
}
UASSERT_OBJ(ticks >= 1, nodep, "0 tick should have been checked in V3Width");
AstNode* inp = nodep->exprp()->unlinkFrBack();
AstNode* const exprp = nodep->exprp()->unlinkFrBack();
AstNode* inp = newSampledExpr(exprp);
AstVar* invarp = nullptr;
AstSenTree* const sentreep = nodep->sentreep();
sentreep->unlinkFrBack();
AstAlways* const alwaysp
= new AstAlways(nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, nullptr);
m_modp->addStmtp(alwaysp);
= new AstAlways{nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, nullptr};
m_modp->addStmtsp(alwaysp);
for (uint32_t i = 0; i < ticks; ++i) {
AstVar* const outvarp = new AstVar(
AstVar* const outvarp = new AstVar{
nodep->fileline(), VVarType::MODULETEMP,
"_Vpast_" + cvtToStr(m_modPastNum++) + "_" + cvtToStr(i), inp->dtypep());
m_modp->addStmtp(outvarp);
AstNode* const assp = new AstAssignDly(
nodep->fileline(), new AstVarRef(nodep->fileline(), outvarp, VAccess::WRITE), inp);
alwaysp->addStmtp(assp);
"_Vpast_" + cvtToStr(m_modPastNum++) + "_" + cvtToStr(i), inp->dtypep()};
m_modp->addStmtsp(outvarp);
AstNode* const assp = new AstAssignDly{
nodep->fileline(), new AstVarRef{nodep->fileline(), outvarp, VAccess::WRITE}, inp};
alwaysp->addStmtsp(assp);
// if (debug() >= 9) assp->dumpTree(cout, "-ass: ");
invarp = outvarp;
inp = new AstVarRef(nodep->fileline(), invarp, VAccess::READ);
inp = new AstVarRef{nodep->fileline(), invarp, VAccess::READ};
}
nodep->replaceWith(inp);
}
virtual void visit(AstSampled* nodep) override {
//========== Move $sampled down to read-only variables
void visit(AstSampled* nodep) override {
if (nodep->user1()) return;
VL_RESTORER(m_inSampled);
{
m_inSampled = true;
iterateChildren(nodep);
}
nodep->replaceWith(nodep->exprp()->unlinkFrBack());
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstVarRef* nodep) override {
iterateChildren(nodep);
if (m_inSampled) {
if (!nodep->access().isReadOnly()) {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: Write to variable in sampled expression");
} else {
VNRelinker relinkHandle;
nodep->unlinkFrBack(&relinkHandle);
AstSampled* const newp = newSampledExpr(nodep);
relinkHandle.relink(newp);
newp->user1(1);
}
}
}
// Don't sample sensitivities
void visit(AstSenItem* nodep) override {
VL_RESTORER(m_inSampled);
{
m_inSampled = false;
iterateChildren(nodep);
}
}
//========== Statements
virtual void visit(AstDisplay* nodep) override {
void visit(AstDisplay* nodep) override {
iterateChildren(nodep);
// Replace the special types with standard text
if (nodep->displayType() == VDisplayType::DT_INFO) {
@@ -384,9 +430,10 @@ private:
new AstEq{fl, new AstConst{fl, monNum},
newMonitorNumVarRefp(nodep, VAccess::READ)}},
stmtsp};
ifp->isBoundsCheck(true); // To avoid LATCH warning
ifp->branchPred(VBranchPred::BP_UNLIKELY);
AstNode* const newp = new AstAlwaysPostponed{fl, ifp};
m_modp->addStmtp(newp);
AstNode* const newp = new AstAlways{fl, VAlwaysKwd::ALWAYS, nullptr, ifp};
m_modp->addStmtsp(newp);
} else if (nodep->displayType() == VDisplayType::DT_STROBE) {
nodep->displayType(VDisplayType::DT_DISPLAY);
// Need one-shot
@@ -394,7 +441,7 @@ private:
const auto varp
= new AstVar{fl, VVarType::MODULETEMP, "__Vstrobe" + cvtToStr(m_modStrobeNum++),
nodep->findBitDType()};
m_modp->addStmtp(varp);
m_modp->addStmtsp(varp);
// Where $strobe was we do "__Vstrobe = '1;"
const auto newsetp = new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
new AstConst{fl, AstConst::BitTrue{}}};
@@ -402,39 +449,40 @@ private:
// Add "always_comb if (__Vstrobe) begin $display(...); __Vstrobe = '0; end"
AstNode* const stmtsp = nodep;
AstIf* const ifp = new AstIf{fl, new AstVarRef{fl, varp, VAccess::READ}, stmtsp};
ifp->isBoundsCheck(true); // To avoid LATCH warning
ifp->branchPred(VBranchPred::BP_UNLIKELY);
AstNode* const newp = new AstAlwaysPostponed{fl, ifp};
stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
new AstConst{fl, AstConst::BitFalse{}}});
m_modp->addStmtp(newp);
m_modp->addStmtsp(newp);
}
}
virtual void visit(AstMonitorOff* nodep) override {
void visit(AstMonitorOff* nodep) override {
const auto newp
= new AstAssign(nodep->fileline(), newMonitorOffVarRefp(nodep, VAccess::WRITE),
new AstConst(nodep->fileline(), AstConst::BitTrue{}, nodep->off()));
= new AstAssign{nodep->fileline(), newMonitorOffVarRefp(nodep, VAccess::WRITE),
new AstConst{nodep->fileline(), AstConst::BitTrue{}, nodep->off()}};
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
virtual void visit(AstAssert* nodep) override {
void visit(AstAssert* nodep) override {
iterateChildren(nodep);
newPslAssertion(nodep, nodep->failsp());
}
virtual void visit(AstAssertIntrinsic* nodep) override {
void visit(AstAssertIntrinsic* nodep) override {
iterateChildren(nodep);
newPslAssertion(nodep, nodep->failsp());
}
virtual void visit(AstCover* nodep) override {
void visit(AstCover* nodep) override {
iterateChildren(nodep);
newPslAssertion(nodep, nullptr);
}
virtual void visit(AstRestrict* nodep) override {
void visit(AstRestrict* nodep) override {
iterateChildren(nodep);
// IEEE says simulator ignores these
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
}
virtual void visit(AstNodeModule* nodep) override {
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
VL_RESTORER(m_modPastNum);
VL_RESTORER(m_modStrobeNum);
@@ -445,7 +493,7 @@ private:
iterateChildren(nodep);
}
}
virtual void visit(AstBegin* nodep) override {
void visit(AstBegin* nodep) override {
// This code is needed rather than a visitor in V3Begin,
// because V3Assert is called before V3Begin
VL_RESTORER(m_beginp);
@@ -455,12 +503,12 @@ private:
}
}
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit AssertVisitor(AstNetlist* nodep) { iterate(nodep); }
virtual ~AssertVisitor() override {
~AssertVisitor() override {
V3Stats::addStat("Assertions, assert non-immediate statements", m_statAsNotImm);
V3Stats::addStat("Assertions, assert immediate statements", m_statAsImm);
V3Stats::addStat("Assertions, cover statements", m_statCover);
@@ -474,5 +522,5 @@ public:
void V3Assert::assertAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ AssertVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("assert", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("assert", 0, dumpTree() >= 3);
}
+1 -1
View File
@@ -20,8 +20,8 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Error.h"
#include "V3Ast.h"
#include "V3Error.h"
//============================================================================
+18 -16
View File
@@ -20,9 +20,12 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3AssertPre.h"
#include "V3Ast.h"
#include "V3Global.h"
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Assert class functions
@@ -44,7 +47,6 @@ private:
AstNode* m_disablep = nullptr; // Last disable
// METHODS
VL_DEBUG_FUNC; // Declare debug()
AstSenTree* newSenTree(AstNode* nodep) {
// Create sentree based on clocked or default clock
@@ -68,7 +70,7 @@ private:
// VISITORS
//========== Statements
virtual void visit(AstClocking* nodep) override {
void visit(AstClocking* nodep) override {
UINFO(8, " CLOCKING" << nodep << endl);
// Store the new default clock, reset on new module
m_seniDefaultp = nodep->sensesp();
@@ -80,14 +82,14 @@ private:
}
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
virtual void visit(AstAlways* nodep) override {
void visit(AstAlways* nodep) override {
iterateAndNextNull(nodep->sensesp());
if (nodep->sensesp()) m_seniAlwaysp = nodep->sensesp()->sensesp();
iterateAndNextNull(nodep->bodysp());
iterateAndNextNull(nodep->stmtsp());
m_seniAlwaysp = nullptr;
}
virtual void visit(AstNodeCoverOrAssert* nodep) override {
void visit(AstNodeCoverOrAssert* nodep) override {
if (nodep->sentreep()) return; // Already processed
clearAssertInfo();
// Find Clocking's buried under nodep->exprsp
@@ -95,7 +97,7 @@ private:
if (!nodep->immediate()) nodep->sentreep(newSenTree(nodep));
clearAssertInfo();
}
virtual void visit(AstFell* nodep) override {
void visit(AstFell* nodep) override {
if (nodep->sentreep()) return; // Already processed
iterateChildren(nodep);
FileLine* const fl = nodep->fileline();
@@ -109,12 +111,12 @@ private:
nodep->sentreep(newSenTree(nodep));
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
virtual void visit(AstPast* nodep) override {
void visit(AstPast* nodep) override {
if (nodep->sentreep()) return; // Already processed
iterateChildren(nodep);
nodep->sentreep(newSenTree(nodep));
}
virtual void visit(AstRose* nodep) override {
void visit(AstRose* nodep) override {
if (nodep->sentreep()) return; // Already processed
iterateChildren(nodep);
FileLine* const fl = nodep->fileline();
@@ -128,7 +130,7 @@ private:
nodep->sentreep(newSenTree(nodep));
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
virtual void visit(AstStable* nodep) override {
void visit(AstStable* nodep) override {
if (nodep->sentreep()) return; // Already processed
iterateChildren(nodep);
FileLine* const fl = nodep->fileline();
@@ -142,7 +144,7 @@ private:
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
virtual void visit(AstImplication* nodep) override {
void visit(AstImplication* nodep) override {
if (nodep->sentreep()) return; // Already processed
FileLine* const fl = nodep->fileline();
@@ -160,7 +162,7 @@ private:
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
virtual void visit(AstPropClocked* nodep) override {
void visit(AstPropClocked* nodep) override {
// No need to iterate the body, once replace will get iterated
iterateAndNextNull(nodep->sensesp());
if (m_senip)
@@ -182,12 +184,12 @@ private:
nodep->replaceWith(blockp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
virtual void visit(AstNodeModule* nodep) override {
void visit(AstNodeModule* nodep) override {
iterateChildren(nodep);
// Reset defaults
m_seniDefaultp = nullptr;
}
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
@@ -196,7 +198,7 @@ public:
// Process
iterate(nodep);
}
virtual ~AssertPreVisitor() override = default;
~AssertPreVisitor() override = default;
};
//######################################################################
@@ -205,5 +207,5 @@ public:
void V3AssertPre::assertPreAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ AssertPreVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("assertpre", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("assertpre", 0, dumpTree() >= 3);
}
+133 -68
View File
@@ -18,15 +18,18 @@
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3File.h"
#include "V3Global.h"
#include "V3Broken.h"
#include "V3EmitV.h"
#include "V3File.h"
#include "V3Global.h"
#include "V3String.h"
#include <iomanip>
#include <memory>
VL_DEFINE_DEBUG_FUNCTIONS;
//======================================================================
// Statics
@@ -164,22 +167,22 @@ string AstNode::prettyName(const string& namein) {
continue;
}
if (pos[0] == '_' && pos[1] == '_') { // Short-circuit
if (0 == strncmp(pos, "__BRA__", 7)) {
if (0 == std::strncmp(pos, "__BRA__", 7)) {
pretty += "[";
pos += 7;
continue;
}
if (0 == strncmp(pos, "__KET__", 7)) {
if (0 == std::strncmp(pos, "__KET__", 7)) {
pretty += "]";
pos += 7;
continue;
}
if (0 == strncmp(pos, "__DOT__", 7)) {
if (0 == std::strncmp(pos, "__DOT__", 7)) {
pretty += ".";
pos += 7;
continue;
}
if (0 == strncmp(pos, "__PVT__", 7)) {
if (0 == std::strncmp(pos, "__PVT__", 7)) {
pretty += "";
pos += 7;
continue;
@@ -205,14 +208,13 @@ string AstNode::prettyName(const string& namein) {
string AstNode::prettyTypeName() const {
if (name() == "") return typeName();
return string(typeName()) + " '" + prettyName() + "'";
return std::string{typeName()} + " '" + prettyName() + "'";
}
//######################################################################
// Insertion
inline void AstNode::debugTreeChange(const AstNode* nodep, const char* prefix, int lineno,
bool next){
void AstNode::debugTreeChange(const AstNode* nodep, const char* prefix, int lineno, bool next) {
#ifdef VL_DEBUG
// Called on all major tree changers.
// Only for use for those really nasty bugs relating to internals
@@ -233,7 +235,8 @@ inline void AstNode::debugTreeChange(const AstNode* nodep, const char* prefix, i
#endif
}
AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) {
template <>
AstNode* AstNode::addNext<AstNode, AstNode>(AstNode* nodep, AstNode* newp) {
// Add to m_nextp, returns this
UDEBUGONLY(UASSERT_OBJ(newp, nodep, "Null item passed to addNext"););
debugTreeChange(nodep, "-addNextThs: ", __LINE__, false);
@@ -271,23 +274,18 @@ AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) {
return nodep;
}
AstNode* AstNode::addNextNull(AstNode* nodep, AstNode* newp) {
if (!newp) return nodep;
return addNext(nodep, newp);
}
void AstNode::addNextHere(AstNode* newp) {
// Add to m_nextp on exact node passed, not at the end.
// This could be at head, tail, or both (single)
// New could be head of single node, or list
UASSERT(newp, "Null item passed to addNext");
UASSERT(!newp->backp(), "New node (back) already assigned?");
UASSERT_OBJ(!newp->backp(), newp, "New node (back) already assigned?");
debugTreeChange(this, "-addHereThs: ", __LINE__, false);
debugTreeChange(newp, "-addHereNew: ", __LINE__, true);
newp->editCountInc();
AstNode* const addlastp = newp->m_headtailp; // Last node in list to be added
UASSERT(!addlastp->m_nextp, "Headtailp tail isn't at the tail");
UASSERT_OBJ(!addlastp->m_nextp, addlastp, "Headtailp tail isn't at the tail");
// Forward links
AstNode* const oldnextp = this->m_nextp;
@@ -437,7 +435,7 @@ void VNRelinker::dump(std::ostream& str) const {
AstNode* AstNode::unlinkFrBackWithNext(VNRelinker* linkerp) {
debugTreeChange(this, "-unlinkWNextThs: ", __LINE__, true);
AstNode* const oldp = this;
UASSERT(oldp->m_backp, "Node has no back, already unlinked?");
UASSERT_OBJ(oldp->m_backp, oldp, "Node has no back, already unlinked?");
oldp->editCountInc();
AstNode* const backp = oldp->m_backp;
if (linkerp) {
@@ -497,7 +495,7 @@ AstNode* AstNode::unlinkFrBackWithNext(VNRelinker* linkerp) {
AstNode* AstNode::unlinkFrBack(VNRelinker* linkerp) {
debugTreeChange(this, "-unlinkFrBkThs: ", __LINE__, true);
AstNode* const oldp = this;
UASSERT(oldp->m_backp, "Node has no back, already unlinked?");
UASSERT_OBJ(oldp->m_backp, oldp, "Node has no back, already unlinked?");
oldp->editCountInc();
AstNode* const backp = oldp->m_backp;
if (linkerp) {
@@ -565,7 +563,7 @@ void AstNode::relink(VNRelinker* linkerp) {
}
AstNode* const newp = this;
UASSERT(linkerp && linkerp->m_backp, "Need non-empty linker");
UASSERT(!newp->backp(), "New node already linked?");
UASSERT_OBJ(!newp->m_backp, newp, "New node already linked?");
newp->editCountInc();
if (debug() > 8) {
@@ -631,11 +629,56 @@ void AstNode::relinkOneLink(AstNode*& pointpr, // Ref to pointer that gets set
}
void AstNode::addHereThisAsNext(AstNode* newp) {
// {old}->this->{next} becomes {old}->new->this->{next}
VNRelinker handle;
this->unlinkFrBackWithNext(&handle);
newp->addNext(this);
handle.relink(newp);
// {back}->this->{next} becomes {back}->new->this->{next}
UASSERT_OBJ(!newp->backp(), newp, "New node already linked?");
UASSERT_OBJ(this->m_backp, this, "'this' node has no back, already unlinked?");
UASSERT_OBJ(newp->m_headtailp, newp, "m_headtailp not set on new node");
//
AstNode* const backp = this->m_backp;
AstNode* const newLastp = newp->m_headtailp;
//
this->editCountInc();
// Common linkage
newLastp->m_nextp = this;
this->m_backp = newLastp;
newp->m_backp = backp;
// newLastp will not be the last node in the list as 'this' will follow it.
// If newLastp == newp, then below handles newp becoming head
newLastp->m_headtailp = nullptr;
// Linkage dependent on position
if (backp && backp->m_nextp == this) {
// If 'this' is not at the head of a list, then the new node will also not be at the head
// of a list, so we can just link in the new node in the middle.
backp->m_nextp = newp;
newp->m_headtailp = nullptr;
} else {
// If 'this' is at the head of a list, then the new node becomes the head of that list.
if (backp) {
if (backp->m_op1p == this) {
backp->m_op1p = newp;
} else if (backp->m_op2p == this) {
backp->m_op2p = newp;
} else if (backp->m_op3p == this) {
backp->m_op3p = newp;
} else {
UASSERT_OBJ(backp->m_op4p == this, this, "Don't know where newp should go");
backp->m_op4p = newp;
}
}
// We also need to update m_headtailp.
AstNode* const tailp = this->m_headtailp;
this->m_headtailp = nullptr;
newp->m_headtailp = tailp;
tailp->m_headtailp = newp;
}
// Iterator fixup
if (newLastp->m_iterpp) *(newLastp->m_iterpp) = this;
if (this->m_iterpp) {
*(this->m_iterpp) = newp;
this->m_iterpp = nullptr;
}
//
debugTreeChange(this, "-addHereThisAsNext: ", __LINE__, true);
}
void AstNode::swapWith(AstNode* bp) {
@@ -879,7 +922,7 @@ AstNode* AstNode::iterateSubtreeReturnEdits(VNVisitor& v) {
} else if (!nodep->backp()) {
// Calling on standalone tree; insert a shim node so we can keep
// track, then delete it on completion
AstBegin* const tempp = new AstBegin(nodep->fileline(), "[EditWrapper]", nodep);
AstBegin* const tempp = new AstBegin{nodep->fileline(), "[EditWrapper]", nodep};
{
VL_DO_DANGLING(tempp->stmtsp()->accept(v),
nodep); // nodep to null as may be replaced
@@ -958,43 +1001,12 @@ bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ig
//======================================================================
// Debugging
void AstNode::checkTreeIter(AstNode* backp) {
void AstNode::checkTreeIter(const AstNode* backp) const {
// private: Check a tree and children
UASSERT_OBJ(backp == this->backp(), this, "Back node inconsistent");
if (VN_IS(this, NodeTermop) || VN_IS(this, NodeVarRef)) {
// Termops have a short-circuited iterateChildren, so check usage
UASSERT_OBJ(!(op1p() || op2p() || op3p() || op4p()), this,
"Terminal operation with non-terminals");
}
if (m_op1p) m_op1p->checkTreeIterList(this);
if (m_op2p) m_op2p->checkTreeIterList(this);
if (m_op3p) m_op3p->checkTreeIterList(this);
if (m_op4p) m_op4p->checkTreeIterList(this);
}
void AstNode::checkTreeIterList(AstNode* backp) {
// private: Check a (possible) list of nodes, this is always the head of the list
// Audited to make sure this is never nullptr
AstNode* const headp = this;
const AstNode* tailp = this;
for (AstNode* nodep = headp; nodep; nodep = nodep->nextp()) {
nodep->checkTreeIter(backp);
UASSERT_OBJ(headp == this || !nextp(), this,
"Headtailp should be null in middle of lists");
tailp = nodep;
backp = nodep;
}
UASSERT_OBJ(headp->m_headtailp == tailp, headp, "Tail in headtailp is inconsistent");
UASSERT_OBJ(tailp->m_headtailp == headp, tailp, "Head in headtailp is inconsistent");
}
void AstNode::checkTree() {
if (!debug()) return;
if (this->backp()) {
// Linked tree- check only the passed node
this->checkTreeIter(this->backp());
} else {
this->checkTreeIterList(this->backp());
switch (this->type()) {
#include "V3Ast__gen_op_checks.h"
default: VL_UNREACHABLE; // LCOV_EXCL_LINE
}
}
@@ -1063,9 +1075,10 @@ void AstNode::dumpPtrs(std::ostream& os) const {
if (user5p()) os << " user5p=" << cvtToHex(user5p());
if (m_iterpp) {
os << " iterpp=" << cvtToHex(m_iterpp);
os << "*=" << cvtToHex(*m_iterpp);
// This may cause address sanitizer failures as iterpp can be stale
// os << "*=" << cvtToHex(*m_iterpp);
}
os << endl;
os << std::endl;
}
void AstNode::dumpTree(std::ostream& os, const string& indent, int maxDepth) const {
@@ -1110,7 +1123,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo
if (logsp->fail()) v3fatal("Can't write " << filename);
*logsp << "Verilator Tree Dump (format 0x3900) from <e" << std::dec << editCountLast();
*logsp << "> to <e" << std::dec << editCountGbl() << ">\n";
if (editCountGbl() == editCountLast() && !(v3Global.opt.dumpTree() >= 9)) {
if (editCountGbl() == editCountLast() && ::dumpTree() < 9) {
*logsp << '\n';
*logsp << "No changes since last dump!\n";
} else {
@@ -1120,7 +1133,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo
}
}
if (doDump && v3Global.opt.debugEmitV()) V3EmitV::debugEmitV(filename + ".v");
if (doCheck && (v3Global.opt.debugCheck() || v3Global.opt.dumpTree())) {
if (doCheck && (v3Global.opt.debugCheck() || ::dumpTree())) {
// Error check
checkTree();
// Broken isn't part of check tree because it can munge iterp's
@@ -1129,10 +1142,53 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo
}
}
void AstNode::v3errorEndFatal(std::ostringstream& str) const {
static void drawChildren(std::ostream& os, const AstNode* thisp, const AstNode* childp,
const std::string& childName) {
if (childp) {
os << "\tn" << cvtToHex(thisp) << " -> n" << cvtToHex(childp) << " ["
<< "label=\"" << childName << "\" color=red];\n";
for (const AstNode* nodep = childp; nodep; nodep = nodep->nextp()) {
nodep->dumpTreeDot(os);
if (nodep->nextp()) {
os << "\tn" << cvtToHex(nodep) << " -> n" << cvtToHex(nodep->nextp()) << " ["
<< "label=\"next\" color=red];\n";
os << "\t{rank=same; n" << cvtToHex(nodep) << ", n" << cvtToHex(nodep->nextp())
<< "}\n";
}
}
}
}
void AstNode::dumpTreeDot(std::ostream& os) const {
os << "\tn" << cvtToHex(this) << "\t["
<< "label=\"" << typeName() << "\\n"
<< name() << "\"];\n";
drawChildren(os, this, m_op1p, "op1");
drawChildren(os, this, m_op2p, "op2");
drawChildren(os, this, m_op3p, "op3");
drawChildren(os, this, m_op4p, "op4");
}
void AstNode::dumpTreeDotFile(const string& filename, bool append, bool doDump) {
if (doDump) {
UINFO(2, "Dumping " << filename << endl);
const std::unique_ptr<std::ofstream> treedotp{V3File::new_ofstream(filename, append)};
if (treedotp->fail()) v3fatal("Can't write " << filename);
*treedotp << "digraph vTree{\n";
*treedotp << "\tgraph\t[label=\"" << filename + ".dot"
<< "\",\n";
*treedotp << "\t\t labelloc=t, labeljust=l,\n";
*treedotp << "\t\t //size=\"7.5,10\",\n"
<< "];\n";
dumpTreeDot(*treedotp);
*treedotp << "}\n";
}
}
void AstNode::v3errorEndFatal(std::ostringstream& str) const VL_MT_SAFE {
v3errorEnd(str);
assert(0); // LCOV_EXCL_LINE
VL_UNREACHABLE
VL_UNREACHABLE;
}
string AstNode::instanceStr() const {
@@ -1171,7 +1227,11 @@ void AstNode::v3errorEnd(std::ostringstream& str) const {
const_cast<AstNode*>(this)->dump(nsstr);
nsstr << endl;
}
m_fileline->v3errorEnd(nsstr, instanceStr());
// Don't look for instance name when warning is disabled.
// In case of large number of warnings, this can
// take significant amount of time
m_fileline->v3errorEnd(nsstr,
m_fileline->warnIsOff(V3Error::errorCode()) ? "" : instanceStr());
}
}
@@ -1248,3 +1308,8 @@ void VNDeleter::doDeletes() {
for (AstNode* const nodep : m_deleteps) nodep->deleteTree();
m_deleteps.clear();
}
//######################################################################
// VNVisitor
#include "V3Ast__gen_visitor_defns.h" // From ./astgen
+850 -1605
View File
File diff suppressed because it is too large Load Diff
+133 -37
View File
@@ -17,71 +17,167 @@
#ifndef VERILATOR_V3ASTINLINES_H_
#define VERILATOR_V3ASTINLINES_H_
#ifndef VERILATOR_V3ASTNODES_H_
#ifndef VERILATOR_V3AST_H_
#error "Use V3Ast.h as the include"
#include "V3AstNodes.h" // This helps code analysis tools pick up symbols in V3Ast.h and V3AstNodes.h
#include "V3Ast.h" // This helps code analysis tools pick up symbols in V3Ast.h and relaed
#endif
//######################################################################
// Inline ACCESSORS
// Inline METHODS
inline int AstNode::width() const { return dtypep() ? dtypep()->width() : 0; }
inline int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() : 0; }
inline bool AstNode::width1() const { // V3Const uses to know it can optimize
int AstNode::width() const { return dtypep() ? dtypep()->width() : 0; }
int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() : 0; }
bool AstNode::width1() const { // V3Const uses to know it can optimize
return dtypep() && dtypep()->width() == 1;
}
inline int AstNode::widthInstrs() const {
int AstNode::widthInstrs() const {
return (!dtypep() ? 1 : (dtypep()->isWide() ? dtypep()->widthWords() : 1));
}
inline bool AstNode::isDouble() const {
bool AstNode::isDouble() const VL_MT_SAFE {
return dtypep() && VN_IS(dtypep(), BasicDType) && VN_AS(dtypep(), BasicDType)->isDouble();
}
inline bool AstNode::isString() const {
bool AstNode::isString() const VL_MT_SAFE {
return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isString();
}
inline bool AstNode::isSigned() const { return dtypep() && dtypep()->isSigned(); }
bool AstNode::isSigned() const { return dtypep() && dtypep()->isSigned(); }
inline bool AstNode::isZero() const {
bool AstNode::isZero() const {
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqZero());
}
inline bool AstNode::isNeqZero() const {
bool AstNode::isNeqZero() const {
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isNeqZero());
}
inline bool AstNode::isOne() const {
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqOne());
}
inline bool AstNode::isAllOnes() const {
bool AstNode::isOne() const { return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqOne()); }
bool AstNode::isAllOnes() const {
return (VN_IS(this, Const) && VN_AS(this, Const)->isEqAllOnes());
}
inline bool AstNode::isAllOnesV() const {
bool AstNode::isAllOnesV() const {
return (VN_IS(this, Const) && VN_AS(this, Const)->isEqAllOnesV());
}
inline bool AstNode::sameTree(const AstNode* node2p) const {
bool AstNode::sameTree(const AstNode* node2p) const {
return sameTreeIter(this, node2p, true, false);
}
inline bool AstNode::sameGateTree(const AstNode* node2p) const {
bool AstNode::sameGateTree(const AstNode* node2p) const {
return sameTreeIter(this, node2p, true, true);
}
inline void AstNodeVarRef::varp(AstVar* varp) {
m_varp = varp;
int AstNodeArrayDType::left() const VL_MT_SAFE { return rangep()->leftConst(); }
int AstNodeArrayDType::right() const VL_MT_SAFE { return rangep()->rightConst(); }
int AstNodeArrayDType::hi() const VL_MT_SAFE { return rangep()->hiConst(); }
int AstNodeArrayDType::lo() const VL_MT_SAFE { return rangep()->loConst(); }
int AstNodeArrayDType::elementsConst() const VL_MT_SAFE { return rangep()->elementsConst(); }
VNumRange AstNodeArrayDType::declRange() const VL_MT_SAFE { return VNumRange{left(), right()}; }
AstRange::AstRange(FileLine* fl, int left, int right)
: ASTGEN_SUPER_Range(fl) {
leftp(new AstConst{fl, static_cast<uint32_t>(left)});
rightp(new AstConst{fl, static_cast<uint32_t>(right)});
}
AstRange::AstRange(FileLine* fl, const VNumRange& range)
: ASTGEN_SUPER_Range(fl) {
leftp(new AstConst{fl, static_cast<uint32_t>(range.left())});
rightp(new AstConst{fl, static_cast<uint32_t>(range.right())});
}
int AstRange::leftConst() const {
AstConst* const constp = VN_CAST(leftp(), Const);
return (constp ? constp->toSInt() : 0);
}
int AstRange::rightConst() const {
AstConst* const constp = VN_CAST(rightp(), Const);
return (constp ? constp->toSInt() : 0);
}
int AstQueueDType::boundConst() const VL_MT_SAFE {
AstConst* const constp = VN_CAST(boundp(), Const);
return (constp ? constp->toSInt() : 0);
}
AstPin::AstPin(FileLine* fl, int pinNum, AstVarRef* varname, AstNode* exprp)
: ASTGEN_SUPER_Pin(fl)
, m_pinNum{pinNum}
, m_name{varname->name()} {
this->exprp(exprp);
}
AstPackArrayDType::AstPackArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp,
AstRange* rangep)
: ASTGEN_SUPER_PackArrayDType(fl) {
childDTypep(dtp); // Only for parser
refDTypep(nullptr);
this->rangep(rangep);
dtypep(nullptr); // V3Width will resolve
const int width = subDTypep()->width() * rangep->elementsConst();
widthForce(width, width);
}
AstPackArrayDType::AstPackArrayDType(FileLine* fl, AstNodeDType* dtp, AstRange* rangep)
: ASTGEN_SUPER_PackArrayDType(fl) {
refDTypep(dtp);
this->rangep(rangep);
dtypep(this);
const int width = subDTypep()->width() * rangep->elementsConst();
widthForce(width, width);
}
int AstBasicDType::hi() const { return (rangep() ? rangep()->hiConst() : m.m_nrange.hi()); }
int AstBasicDType::lo() const { return (rangep() ? rangep()->loConst() : m.m_nrange.lo()); }
int AstBasicDType::elements() const {
return (rangep() ? rangep()->elementsConst() : m.m_nrange.elements());
}
bool AstBasicDType::littleEndian() const {
return (rangep() ? rangep()->littleEndian() : m.m_nrange.littleEndian());
}
bool AstActive::hasClocked() const { return m_sensesp->hasClocked(); }
bool AstActive::hasCombo() const { return m_sensesp->hasCombo(); }
AstElabDisplay::AstElabDisplay(FileLine* fl, VDisplayType dispType, AstNode* exprsp)
: ASTGEN_SUPER_ElabDisplay(fl) {
addFmtp(new AstSFormatF{fl, AstSFormatF::NoFormat(), exprsp});
m_displayType = dispType;
}
AstCStmt::AstCStmt(FileLine* fl, const string& textStmt)
: ASTGEN_SUPER_CStmt(fl) {
addExprsp(new AstText{fl, textStmt, true});
}
AstCMath::AstCMath(FileLine* fl, const string& textStmt, int setwidth, bool cleanOut)
: ASTGEN_SUPER_CMath(fl)
, m_cleanOut{cleanOut}
, m_pure{true} {
addExprsp(new AstText{fl, textStmt, true});
if (setwidth) dtypeSetLogicSized(setwidth, VSigning::UNSIGNED);
}
AstVarRef::AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access)
: ASTGEN_SUPER_VarRef(fl, varp->name(), varp, access) {}
// This form only allowed post-link (see above)
AstVarRef::AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access)
: ASTGEN_SUPER_VarRef(fl, varscp->varp()->name(), varscp->varp(), access) {
varScopep(varscp);
}
bool AstVarRef::same(const AstVarRef* samep) const {
if (varScopep()) {
return (varScopep() == samep->varScopep() && access() == samep->access());
} else {
return (selfPointer() == samep->selfPointer() && varp()->name() == samep->varp()->name()
&& access() == samep->access());
}
}
bool AstVarRef::sameNoLvalue(AstVarRef* samep) const {
if (varScopep()) {
return (varScopep() == samep->varScopep());
} else {
return (selfPointer() == samep->selfPointer()
&& (!selfPointer().empty() || !samep->selfPointer().empty())
&& varp()->name() == samep->varp()->name());
}
}
AstVarXRef::AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access)
: ASTGEN_SUPER_VarXRef(fl, varp->name(), varp, access)
, m_dotted{dotted} {
dtypeFrom(varp);
}
inline bool AstNodeDType::isFourstate() const { return basicp()->isFourstate(); }
inline void AstNodeArrayDType::rangep(AstRange* nodep) { setOp2p(nodep); }
inline int AstNodeArrayDType::left() const { return rangep()->leftConst(); }
inline int AstNodeArrayDType::right() const { return rangep()->rightConst(); }
inline int AstNodeArrayDType::hi() const { return rangep()->hiConst(); }
inline int AstNodeArrayDType::lo() const { return rangep()->loConst(); }
inline int AstNodeArrayDType::elementsConst() const { return rangep()->elementsConst(); }
inline VNumRange AstNodeArrayDType::declRange() const { return VNumRange{left(), right()}; }
inline void AstIfaceRefDType::cloneRelink() {
if (m_cellp && m_cellp->clonep()) m_cellp = m_cellp->clonep();
if (m_ifacep && m_ifacep->clonep()) m_ifacep = m_ifacep->clonep();
if (m_modportp && m_modportp->clonep()) m_modportp = m_modportp->clonep();
}
#endif // Guard
+1290
View File
File diff suppressed because it is too large Load Diff
+4301
View File
File diff suppressed because it is too large Load Diff
+4167
View File
File diff suppressed because it is too large Load Diff

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