Compare commits

...
149 Commits
Author SHA1 Message Date
Wilson Snyder 8f090176a6 Version bump 2021-10-17 14:36:23 -04:00
Wilson Snyder 43e8263d1f Commentary 2021-10-17 14:35:34 -04:00
Geza Lore ac622f9db8 Introduce VN_AS for downcasting to known AstNode subtype.
VN_AS should be used over VN_CAST in code where the author knows up
front (i.e.: statically) what the true type of the node is. This has
multiple benefits over VN_CAST:
- In the debug build: Asserts node type is as expected
- In the optimized build: It is faster as no superfluous type test
- And (I would argue most importantly) it documents intent in the code

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

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

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

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

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

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

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

Fixes #3060
2021-07-08 13:42:25 +01:00
Geza Lore 686baaf2cf Internals: Streamline trace function generation
Remove magic code fragments form EmitCTrace, so Emit need not be aware
that a function is tracing related or not (apart from the purpose of
file name generation). All necessary code is now generated via text
nodes in V3TraceDecl and V3Trace. No functional change intended.
2021-07-08 02:08:09 +01:00
Geza Lore 76b3776fa3 Change generated tracing routines to use snake_case
For consistency with the rest of the generated code, generated methods
related to tracing now use snake_case instead of camelCase. No
functional change intended.
2021-07-08 02:08:09 +01:00
Wilson Snyder 3ed2af638b devel release 2021-07-07 20:50:11 -04:00
472 changed files with 18490 additions and 11473 deletions
-1
View File
@@ -108,4 +108,3 @@ Standard: Cpp11
TabWidth: 8
UseTab: Never
...
+2 -6
View File
@@ -29,7 +29,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04]
os: [ubuntu-20.04, ubuntu-18.04]
compiler:
- { cc: clang, cxx: clang++ }
- { cc: gcc, cxx: g++ }
@@ -37,11 +37,9 @@ jobs:
exclude:
# Build pull requests only with ubuntu-20.04 and without m32
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-16.04' || 'do-not-exclude' }}
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
# Build -m32 only on ubuntu-20.04
- {os: ubuntu-18.04, m32: 1}
- {os: ubuntu-16.04, m32: 1}
runs-on: ${{ matrix.os }}
name: Build | ${{ matrix.os }} | ${{ matrix.compiler.cc }} ${{ matrix.m32 && '| -m32' || '' }}
env:
@@ -92,7 +90,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04]
os: [ubuntu-20.04, ubuntu-18.04]
compiler:
- { cc: clang, cxx: clang++ }
- { cc: gcc, cxx: g++ }
@@ -101,11 +99,9 @@ jobs:
exclude:
# Build pull requests only with ubuntu-20.04 and without m32
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-16.04' || 'do-not-exclude' }}
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
# Build -m32 only on ubuntu-20.04
- {os: ubuntu-18.04, m32: 1}
- {os: ubuntu-16.04, m32: 1}
runs-on: ${{ matrix.os }}
name: Test | ${{ matrix.os }} | ${{ matrix.compiler.cc }} | ${{ matrix.suite }} ${{ matrix.m32 && '| -m32' || '' }}
env:
+48
View File
@@ -8,6 +8,54 @@ The changes in each Verilator version are described below. The
contributors that suggested a given feature are shown in []. Thanks!
Verilator 4.214 2021-10-17
==========================
* Add profile-guided optmization of mtasks (#3150).
* Verilator_gantt has removed the ASCII graphics, use the VCD output instead.
* Verilator_gantt now shows the predicted mtask times, eval times, and additional statistics.
* Verilator_gantt data files now include processor information, to allow later processing.
* Support displaying x and z in $display task (#3107) (#3109). [Iru Cai]
* Fix verilator_profcfunc profile accounting (#3115).
* Fix display has no time units on class function (#3116). [Damien Pretet]
* Fix removing if statement with side effect in condition (#3131). [Alexander Grobman]
* Fix --waiver-output for multiline warnings (#2429) (#3141). [Keith Colbert]
* Fix internal error on bad widths (#3140) (#3145). [Zhanglei Wang]
* Fix crash on clang 12/13 (#3148). [Kouping Hsu]
* Fix cygwin compile error due to missing -std=gnu++14 (#3149). [Sun Kim]
* Fix $urandom_range when the range is 0 ... UINT_MAX (#3161). [Iru Cai]
* Fix constructor-parameter argument comma-separation in C++ (#3162). [Matthew Ballance]
* Fix missing install of vl_file_copy/vl_hier_graph (#3165). [Popolon]
* Fix calling new with arguments in same class (#3166). [Matthew Ballance]
* Fix false EOFNEWLINE warning when DOS carriage returns present (#3171).
Verilator 4.212 2021-09-01
==========================
**Minor:**
* Fix re-evaluation of logic dependent on state set in DPI exports (#3091). [Geza Lore]
* Support unpacked array localparams in tasks/functions (#3078). [Geza Lore]
* Support timeunit/timeprecision in $unit.
* Support assignment patterns as children of pins (#3041). [Krzysztof Bieganski]
* Add --instr-count-dpi to tune assumed DPI import cost for multithreaded
model scheduling. Default value changed to 200 (#3068). [Yinan Xu]
* Output files are split based on the set of headers required
in order to aid incremental compilation via ccache (#3071). [Geza Lore]
* Parameter values are now emitted as 'static constexpr' instead of enum.
C++ direct references to parameters might require updating (#3077). [Geza Lore]
* Refactored Verilated include files; include verilated.h not verilated_heavy.h.
* Add header guards on Dpi.h generated files (#2979). [Tood Strader]
* Add XML ccall, constpool, initarray, and if/while begins (#3080). [Steven Hugg]
* Add error when constant function under a generate (#3103). [Don Owen]
* Fix -G to treat simple integer literals as signed (#3060). [Anikin1610]
* Fix emitted string array initializers (#2895). [Iztok Jeras]
* Fix bitop tree optimization dropping necessary & operator (#3096). [Flavien Solt]
* Fix internal error on wide -x-initial unique (#3106). [Alexandre Joannou]
* Fix traces to show array instances with brackets (#3092) (#3095). [Pieter Kapsenberg]
Verilator 4.210 2021-07-07
==========================
-76
View File
@@ -1,76 +0,0 @@
\.ccache/
\.clang-format
\.clang-tidy
\.git/
\.git$
\.github/
\.svn/
\.(bak|old)/
\.(bak|old)$
\.tar\.
.*\.tgz
.*\.log
\..*\.swp
.*\.tmp
.*\.tidy
.*\.tex
.*\.key
.*\.vcd
.*\.1
\.codacy\.yml
\.travis\.yml
/_build/
/build/
/obj_dbg/
/obj_dir/
/obj_dist/
/obj_iv/
/obj_ms/
/obj_nc/
/obj_opt/
/obj_vcs/
/obj_vlt/
/obj_vltmt/
INCA_libs/
/cov_work/
/logs/
^Makefile$
README.html
bin/verilator_bin.*
bin/verilator_coverage_bin.*
docs/.*\.html$
docs/_build/
docs/clang-format.txt$
docs/doxygen-doc/.*
docs/spelling.txt
examples/xml_py/copied/
examples/xml_py/graph.*
sonar-project.properties
src/Makefile$
src/Makefile_obj$
include/verilated.mk$
test_regress/.gdbinit$
test_regress/transcript
codecov.yml
config.cache$
config.status$
verilator\.log
verilator\.tex
verilator-config.cmake$
verilator-config-version.cmake$
verilator.pc$
verilator_bin.*
verilator_coverage_bin.*
.vcsmx_rebuild$
ncverilog.history
autom4te\.cache/
nodist/
ci/
/simv$
/simv.daidir/
/vc_hdrs.h$
/csrc/
obj_dir.*
TAGS
gmon.out
.*~
+11 -167
View File
@@ -118,98 +118,6 @@ INFOS = verilator.html verilator.pdf
INFOS_OLD = README README.html README.pdf
# Files that can be generated, but should be up to date for a distribution.
DISTDEP = info Makefile
DISTFILES1 = $(INFOS) .gitignore \
*.in *.ac \
Artistic \
Changes \
LICENSE \
MANIFEST.SKIP \
README.rst \
verilator-config.cmake.in \
verilator-config-version.cmake.in \
bin/verilator \
bin/verilator_ccache_report \
bin/verilator_coverage \
bin/verilator_difftree \
bin/verilator_gantt \
bin/verilator_includer \
bin/verilator_profcfunc \
docs/.gitignore \
docs/CONTRIBUTING.rst \
docs/CONTRIBUTORS \
docs/Makefile \
docs/_static/*.png \
docs/_static/css/* \
docs/bin/* \
docs/gen/* \
docs/guide/*.py \
docs/guide/*.rst \
docs/guide/figures/* \
docs/install.rst \
docs/internals.rst \
docs/internals.rst \
docs/verilated.dox \
docs/xml.rst \
install-sh configure *.pod \
include/*.[chv]* \
include/*.in \
include/.*ignore \
include/gtkwave/*.[chv]* \
include/vltstd/*.[chv]* \
.*attributes */.*attributes */*/.*attributes \
src/.*ignore src/*.in src/*.cpp src/*.[chly] \
src/astgen src/bisonpre src/*fix src/cppcheck_filtered \
src/config_rev \
src/vlcovgen src/mkinstalldirs \
src/.gdbinit \
src/*.pl src/*.pod \
examples/*/.*ignore examples/*/Makefile* \
examples/*/*.[chv]* examples/*/vl_* \
examples/*/CMakeLists.txt \
DISTFILES2 = \
test_*/.*ignore test_*/Makefile* test_*/*.cpp \
test_*/*.pl test_*/*.v test_*/*.vc test_*/*.vh \
test_regress/*.pl \
test_regress/Makefile \
test_regress/Makefile_obj \
test_regress/input.vc \
test_regress/CMakeLists.txt \
test_regress/t/t*/*.sv* \
test_regress/t/t*/*.v* \
test_regress/t/t*/*/*.sv* \
test_regress/t/t*/*/*.v* \
test_regress/t/t*/*.cpp \
test_regress/t/t*/CMakeLists.txt \
test_regress/t/*.cpp \
test_regress/t/*.h \
test_regress/t/*.dat \
test_regress/t/*.mem \
test_regress/t/*.out \
test_regress/t/*.pl \
test_regress/t/*.pf \
test_regress/t/*.v* \
INST_PROJ_FILES = \
bin/verilator \
bin/verilator_ccache_report \
bin/verilator_coverage \
bin/verilator_gantt \
bin/verilator_includer \
bin/verilator_profcfunc \
include/verilated.mk \
include/*.[chv]* \
include/gtkwave/*.[chv]* \
include/vltstd/*.[chv]* \
INST_PROJ_BIN_FILES = \
bin/verilator_bin$(EXEEXT) \
bin/verilator_bin_dbg$(EXEEXT) \
bin/verilator_coverage_bin_dbg$(EXEEXT) \
EXAMPLES_FIRST = \
examples/make_hello_c \
examples/make_hello_sc \
@@ -217,7 +125,7 @@ EXAMPLES_FIRST = \
EXAMPLES = $(EXAMPLES_FIRST) $(filter-out $(EXAMPLES_FIRST), $(sort $(wildcard examples/*)))
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
VL_INST_MAN_FILES = verilator.1 verilator_coverage.1 verilator_gantt.1 verilator_profcfunc.1
VL_INST_MAN_FILES = verilator.1 verilator_coverage.1
default: all
all: all_nomsg msg_test
@@ -299,8 +207,10 @@ VL_INST_INC_SRCDIR_FILES = \
include/vltstd/*.[chv]* \
VL_INST_DATA_SRCDIR_FILES = \
examples/*/*.[chv]* examples/*/Makefile* \
examples/*/CMakeLists.txt
examples/*/*.[chv]* \
examples/*/CMakeLists.txt \
examples/*/Makefile* \
examples/*/vl_* \
installbin:
$(MKINSTALLDIRS) $(DESTDIR)$(bindir)
@@ -386,44 +296,7 @@ uninstall:
install: all_nomsg install-all
install-all: installbin installman installdata install-msg
install-here: installman ftp
ifeq ($(VERILATOR_AUTHOR_SITE),1) # Local... Else don't burden users
DISTNAMEREV = $(shell sed -e '/DTVERSION/!d' -e 's/.*verilator_\([^"]*\).*/\1/' -e q ${srcdir}/src/config_rev.h)
VERILATOR_CAD_DIR = $(CAD_DIR)/verilator/$(DISTNAMEREV)/$(DIRPROJECT_ARCH)
INST_PROJ_CVS = cp_if_cvs_diff
install-cadtools: dist
@echo "Install-project to $(CAD_DIR)"
strip bin/verilator_bin*
strip bin/verilator_coverage_bin*
$(MAKE) install-cadtools-quick
$(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/man/man1
for p in $(VL_INST_MAN_FILES) ; do \
$(INSTALL_DATA) $$p $(VERILATOR_CAD_DIR)/man/man1/$$p; \
done
$(INST_PROJ_CVS) $(DISTNAME).tgz $(VERILATOR_CAD_DIR)/verilator.tgz
rm $(DISTNAME).tgz
install-cadtools-quick:
ifeq ($(CFG_WITH_DEFENV),yes)
@echo "%Error: Reconfigure with './configure --disable-defenv' to avoid hardcoded paths."
false
endif
@echo "Install-cadtools-quick (no strip) to $(VERILATOR_CAD_DIR)"
$(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/include/gtkwave
$(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/include/vltstd
$(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/bin
for p in $(INST_PROJ_FILES) ; do \
$(INST_PROJ_CVS) $$p $(VERILATOR_CAD_DIR)/$$p; \
done
for p in $(INST_PROJ_BIN_FILES) ; do \
$(INST_PROJ_CVS) $$p $(VERILATOR_CAD_DIR)/$$p; \
done
# VERILATOR_AUTHOR_SITE
endif
install-here: installman info
# Use --xml flag to see the cppcheck code to use for suppression
CPPCHECK_CPP = $(wildcard \
@@ -481,6 +354,9 @@ clang-format:
PY_PROGRAMS = \
bin/verilator_ccache_report \
bin/verilator_difftree \
bin/verilator_gantt \
bin/verilator_profcfunc \
examples/xml_py/vl_file_copy \
examples/xml_py/vl_hier_graph \
docs/guide/conf.py \
@@ -524,9 +400,6 @@ lint-py:
format-pl-exec:
-chmod a+x test_regress/t/*.pl
ftp: info
install-msg:
@echo
@echo "Installed binaries to $(DESTDIR)$(bindir)/verilator"
@@ -597,15 +470,6 @@ TAGS: $(TAGFILES)
doxygen:
$(MAKE) -C docs doxygen
######################################################################
# Test targets
dist-file-list:
@echo "begin-dist-file-list:"; # Scripts look for this
@echo $(wildcard $(DISTFILES1))
@echo $(wildcard $(DISTFILES2))
@echo "end-dist-file-list:"; # Scripts look for this
######################################################################
# Distributions
@@ -613,36 +477,16 @@ DISTTITLE := Verilator $(word 1,$(PACKAGE_VERSION))
DISTNAME := verilator-$(word 1,$(PACKAGE_VERSION))
DISTDATEPRE := $(word 2,$(PACKAGE_VERSION))
DISTDATE := $(subst /,-,$(DISTDATEPRE))
DISTTAGNAME := $(subst .,_,$(subst -,_,$(DISTNAME)))
tag:
svnorcvs tag $(DISTTAGNAME)
# Don't depend on DISTFILES because there's no rule for "standards.info*".
dist: $(DISTDEP) maintainer-copy
-rm -fr $(DISTNAME)
for file in $(DISTFILES1); do \
mkdir -p `dirname $(DISTNAME)/$$file` >/dev/null ; \
ln $$file $(DISTNAME)/$$file \
|| { echo copying $$file instead; cp -p $$file $(DISTNAME)/$$file;}; \
done; true;
for file in $(DISTFILES2); do \
mkdir -p `dirname $(DISTNAME)/$$file` >/dev/null ; \
ln $$file $(DISTNAME)/$$file \
|| { echo copying $$file instead; cp -p $$file $(DISTNAME)/$$file;}; \
done; true;
chmod -R a+r $(DISTNAME)
tar chf $(DISTNAME).tar $(DISTNAME)
gzip --force --best $(DISTNAME).tar
mv $(DISTNAME).tar.gz $(DISTNAME).tgz
rm -fr $(DISTNAME)
maintainer-diff:
svnorcvs diff $(DISTTAGNAME)
preexist:
svnorcvs nexists $(DISTTAGNAME)
maintainer-dist: preexist dist tag
svnorcvs release $(DISTNAME).tgz
maintainer-dist: preexist tag
svnorcvs release $(DISTTAGNAME)
+3
View File
@@ -5,6 +5,8 @@
:target: https://www.gnu.org/licenses/lgpl-3.0]
.. image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
:target: https://opensource.org/licenses/Artistic-2.0
.. image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
:target: https://repology.org/project/verilator/versions
.. image:: https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f
:target: https://www.codacy.com/gh/verilator/verilator
.. image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg
@@ -12,6 +14,7 @@
.. image:: https://github.com/verilator/verilator/workflows/build/badge.svg
:target: https://github.com/verilator/verilator/actions?query=workflow%3Abuild
Welcome to Verilator
====================
+35 -34
View File
@@ -34,11 +34,11 @@ my $opt_quiet_exit;
# No arguments can't do anything useful. Give help
if ($#ARGV < 0) {
pod2usage(-exitstatus=>2, -verbose=>0);
pod2usage(-exitstatus => 2, -verbose => 0);
}
# Insert debugging options up front
push @ARGV, (split ' ',$ENV{VERILATOR_TEST_FLAGS}||"");
push @ARGV, (split ' ', $ENV{VERILATOR_TEST_FLAGS} || "");
# We sneak a look at the flags so we can do some pre-environment checks
# All flags will hit verilator...
@@ -46,7 +46,7 @@ foreach my $sw (@ARGV) {
push @Opt_Verilator_Sw, $sw;
}
Getopt::Long::config("no_auto_abbrev","pass_through");
Getopt::Long::config("no_auto_abbrev", "pass_through");
if (! GetOptions(
# Major operating modes
"help" => \&usage,
@@ -60,7 +60,7 @@ if (! GetOptions(
# Additional parameters
"<>" => sub {}, # Ignored
)) {
pod2usage(-exitstatus=>2, -verbose=>0);
pod2usage(-exitstatus => 2, -verbose => 0);
}
if ($opt_gdbbt && !gdb_works()) {
@@ -73,12 +73,12 @@ if ($opt_gdbbt && !gdb_works()) {
# Starting with that, escape all special chars for the shell;
# The shell will undo the escapes and the verilator binary should
# then see exactly the contents of @Opt_Verilator_Sw.
my @quoted_sw = map {sh_escape($_)} @Opt_Verilator_Sw;
my @quoted_sw = map { sh_escape($_) } @Opt_Verilator_Sw;
if ($opt_gdb) {
# Generic GDB interactive
run (aslr_off()
.($ENV{VERILATOR_GDB}||"gdb")
." ".verilator_bin()
. ($ENV{VERILATOR_GDB} || "gdb")
. " " . verilator_bin()
# Note, uncomment to set breakpoints before running:
# ." -ex 'break main'"
@@ -87,42 +87,41 @@ if ($opt_gdb) {
# escapes as you would expect in a double-quoted string.
# That's not true for a single-quoted string, where \'
# actually terminates the string -- not what we want!
." -ex \"run ".join(' ', @quoted_sw)."\""
." -ex 'set width 0'"
." -ex 'bt'");
. " -ex \"run " . join(' ', @quoted_sw) . "\""
. " -ex 'set width 0'"
. " -ex 'bt'");
} elsif ($opt_rr) {
# Record with rr
run (aslr_off()
."rr record ".verilator_bin()
." ".join(' ', @quoted_sw));
. "rr record " . verilator_bin()
. " " . join(' ', @quoted_sw));
} elsif ($opt_gdbbt && $Debug) {
# Run under GDB to get gdbbt
run (aslr_off()
."gdb"
." ".verilator_bin()
." --batch --quiet --return-child-result"
." -ex \"run ".join(' ', @quoted_sw)."\""
." -ex 'set width 0'"
." -ex 'bt' -ex 'quit'");
. "gdb"
. " " . verilator_bin()
. " --batch --quiet --return-child-result"
. " -ex \"run " . join(' ', @quoted_sw)."\""
. " -ex 'set width 0'"
. " -ex 'bt' -ex 'quit'");
} elsif ($Debug) {
# Debug
run(aslr_off()
.verilator_bin()." ".join(' ',@quoted_sw));
run(aslr_off() . verilator_bin() . " " . join(' ', @quoted_sw));
} else {
# Normal, non gdb
run(verilator_bin()." ".join(' ',@quoted_sw));
run(verilator_bin() . " " . join(' ', @quoted_sw));
}
#----------------------------------------------------------------------
sub usage {
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
pod2usage(-verbose => 2, -exitval => 0, -output => \*STDOUT);
}
sub debug {
shift;
my $level = shift;
$Debug = $level||3;
$Debug = $level || 3;
}
#######################################################################
@@ -163,13 +162,13 @@ sub verilator_bin {
sub gdb_works {
$! = undef; # Cleanup -x
system("gdb /bin/echo"
." --batch-silent --quiet --return-child-result"
." -ex 'run -n'" # `echo -n`
." -ex 'set width 0'"
." -ex 'bt'"
." -ex 'quit'");
. " --batch-silent --quiet --return-child-result"
. " -ex 'run -n'" # `echo -n`
. " -ex 'set width 0'"
. " -ex 'bt'"
. " -ex 'quit'");
my $status = $?;
return $status==0;
return $status == 0;
}
sub aslr_off {
@@ -185,7 +184,7 @@ sub run {
# Run command, check errors
my $command = shift;
$! = undef; # Cleanup -x
print "\t$command\n" if $Debug>=3;
print "\t$command\n" if $Debug >= 3;
system($command);
my $status = $?;
if ($status) {
@@ -193,7 +192,7 @@ sub run {
warn "%Error: verilator: Misinstalled, or VERILATOR_ROOT might need to be in environment\n";
}
if ($Debug) { # For easy rerunning
warn "%Error: export VERILATOR_ROOT=".($ENV{VERILATOR_ROOT}||"")."\n";
warn "%Error: export VERILATOR_ROOT=" . ($ENV{VERILATOR_ROOT} || "") . "\n";
warn "%Error: $command\n";
}
if ($status & 127) {
@@ -201,13 +200,13 @@ sub run {
|| ($status & 127) == 8 # SIGFPA
|| ($status & 127) == 11) { # SIGSEGV
warn "%Error: Verilator internal fault, sorry. "
."Suggest trying --debug --gdbbt\n" if !$Debug;
. "Suggest trying --debug --gdbbt\n" if !$Debug;
} elsif (($status & 127) == 6) { # SIGABRT
warn "%Error: Verilator aborted. "
."Suggest trying --debug --gdbbt\n" if !$Debug;
. "Suggest trying --debug --gdbbt\n" if !$Debug;
} else {
warn "%Error: Verilator threw signal $status. "
."Suggest trying --debug --gdbbt\n" if !$Debug;
. "Suggest trying --debug --gdbbt\n" if !$Debug;
}
}
if (!$opt_quiet_exit && ($status != 256 || $Debug)) { # i.e. not normal exit(1)
@@ -333,6 +332,7 @@ detailed descriptions of these arguments.
--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
--l2-name <value> Verilog scope name of the top module
--language <lang> Default language standard to parse
@@ -447,6 +447,7 @@ description of these arguments.
+verilator+prof+threads+file+<filename> Set profile filename
+verilator+prof+threads+start+<value> Set profile starting point
+verilator+prof+threads+window+<value> Set profile duration
+verilator+prof+vlt+file+<filename> Set profile guided filename
+verilator+rand+reset+<value> Set random reset technique
+verilator+seed+<value> Set random seed
+verilator+V Verbose version and config
+9 -9
View File
@@ -30,7 +30,7 @@ $Debug = 0;
# No arguments can't do anything useful. Give help
if ($#ARGV < 0) {
pod2usage(-exitstatus=>2, -verbose=>0);
pod2usage(-exitstatus => 2, -verbose => 0);
}
# We sneak a look at the flags so we can do some pre-environment checks
@@ -40,7 +40,7 @@ foreach my $sw (@ARGV) {
push @Opt_Verilator_Sw, $sw;
}
Getopt::Long::config("no_auto_abbrev","pass_through");
Getopt::Long::config("no_auto_abbrev", "pass_through");
if (! GetOptions (
# Major operating modes
"help" => \&usage,
@@ -49,23 +49,23 @@ if (! GetOptions (
# Additional parameters
"<>" => sub {}, # Ignored
)) {
pod2usage(-exitstatus=>2, -verbose=>0);
pod2usage(-exitstatus => 2, -verbose => 0);
}
# Normal, non gdb
run(verilator_coverage_bin()
." ".join(' ',@Opt_Verilator_Sw));
. " " . join(' ', @Opt_Verilator_Sw));
#----------------------------------------------------------------------
sub usage {
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
pod2usage(-verbose => 2, -exitval => 0, -output => \*STDOUT);
}
sub debug {
shift;
my $level = shift;
$Debug = $level||3;
$Debug = $level || 3;
}
#######################################################################
@@ -107,7 +107,7 @@ sub run {
# Run command, check errors
my $command = shift;
$! = undef; # Cleanup -x
print "\t$command\n" if $Debug>=3;
print "\t$command\n" if $Debug >= 3;
system($command);
my $status = $?;
if ($status) {
@@ -115,11 +115,11 @@ sub run {
warn "%Error: verilator_coverage: Misinstalled, or VERILATOR_ROOT might need to be in environment\n";
}
if ($Debug) { # For easy rerunning
warn "%Error: export VERILATOR_ROOT=".($ENV{VERILATOR_ROOT}||"")."\n";
warn "%Error: export VERILATOR_ROOT=" . ($ENV{VERILATOR_ROOT} || "") . "\n";
warn "%Error: $command\n";
}
if ($status & 127) {
if (($status & 127) == 8 || ($status & 127) == 11) { # SIGFPA or SIGSEGV
if (($status & 127) == 8 || ($status & 127) == 11) { # SIGFPA or SIGSEGV
warn "%Error: Verilator_coverage internal fault, sorry.\n" if !$Debug;
} elsif (($status & 127) == 6) { # SIGABRT
warn "%Error: Verilator_coverage aborted.\n" if !$Debug;
+106 -200
View File
@@ -1,233 +1,139 @@
#!/usr/bin/env perl
# See copyright, etc in below POD section.
#!/usr/bin/env python3
# pylint: disable=C0103,C0114,C0116
######################################################################
use warnings;
use Getopt::Long;
use IO::File;
use Pod::Usage;
use strict;
use vars qw($Debug);
import argparse
import collections
import glob
import os.path
import re
import sys
#======================================================================
# main
$Debug = 0;
my $Opt_A;
my $Opt_B;
my $Opt_Lineno = 1;
autoflush STDOUT 1;
autoflush STDERR 1;
Getopt::Long::config("no_auto_abbrev");
if (! GetOptions(
"help" => \&usage,
"debug" => \&debug,
"<>" => \&parameter,
"lineno!" => \$Opt_Lineno,
)) {
die "%Error: Bad usage, try 'verilator_difftree --help'\n";
}
def diff(a, b):
defined $Opt_A or die "%Error: No old diff filename\n";
defined $Opt_B or die "%Error: No new diff filename\n";
if not os.path.exists(a):
sys.exit("%Error: No old diff filename found: " + a)
if not os.path.exists(b):
sys.exit("%Error: No new diff filename found: " + b)
-e $Opt_A or die "%Error: No old diff filename found: $Opt_A\n";
-e $Opt_B or die "%Error: No new diff filename found: $Opt_B\n";
if os.path.isdir(a) and os.path.isdir(b):
diff_dir(a, b)
elif os.path.isfile(a) and os.path.isfile(b):
diff_file(a, b)
else:
sys.exit("%Error: Mix of files and dirs")
if (-d $Opt_A && -d $Opt_B) {
diff_dir($Opt_A, $Opt_B);
} elsif (-f $Opt_A && -f $Opt_B) {
diff_file($Opt_A, $Opt_B);
} else {
die "%Error: Mix of files and dirs\n";
}
sub diff_dir {
my $a = shift;
my $b = shift;
def diff_dir(a, b):
# Diff all files under two directories
my %files;
files = collections.defaultdict(lambda: {})
foreach my $fn (glob("$a/*.tree")) {
(my $base = $fn) =~ s!.*/!!;
$files{$base}{a} = $fn;
}
foreach my $fn (glob("$b/*.tree")) {
(my $base = $fn) =~ s!.*/!!;
$files{$base}{b} = $fn;
}
my $any;
foreach my $base (sort (keys %files)) {
my $a = $files{$base}{a};
my $b = $files{$base}{b};
next if !$a || !$b;
print "="x70,"\n";
print "= $a <-> $b\n";
diff_file($a,$b);
$any = 1;
}
$any or warn("%Warning: No .tree files found that have similar base names:\n "
.join("\n ", sort keys %files),"\n");
}
for fn in glob.glob(a + "/*.tree"):
base = re.sub(r'.*/', '', fn)
files[base]['a'] = fn
for fn in glob.glob(b + "/*.tree"):
base = re.sub(r'.*/', '', fn)
files[base]['b'] = fn
sub diff_file {
my $a = shift;
my $b = shift;
anyfile = False
for base in sorted(files.keys()):
a = files[base]['a']
b = files[base]['b']
if not a or not b:
continue
print("=" * 70)
print("= %s <-> %s" % (a, b))
diff_file(a, b)
anyfile = True
if not anyfile:
sys.stderr.write(
"%Warning: No .tree files found that have similar base names\n")
def diff_file(a, b):
# Compare the two tree files
(my $short_a = $a) =~ s/[^a-zA-Z0-9.]+/_/g;
(my $short_b = $b) =~ s/[^a-zA-Z0-9.]+/_/g;
my $tmp_a = "/tmp/${$}_${short_a}.a";
my $tmp_b = "/tmp/${$}_${short_b}.b";
short_a = re.sub(r'[^a-zA-Z0-9.]+', '_', a)
short_b = re.sub(r'[^a-zA-Z0-9.]+', '_', b)
tmp_a = "/tmp/%s_%s.a" % (os.getpid(), short_a)
tmp_b = "/tmp/%s_%s.b" % (os.getpid(), short_b)
my $vera = version_from($a);
my $verb = version_from($b);
my $verCvt = (($vera < 0x3900 && $verb >= 0x3900)
|| ($vera >= 0x3900 && $verb < 0x3900));
# Version conversion deprecated, but for future...
# vera = version_from(a)
# verb = version_from(b)
# verCvt = ((vera < 0x3900 and verb >= 0x3900)
# or (vera >= 0x3900 and verb < 0x3900))
filter($a, $tmp_a, $verCvt);
filter($b, $tmp_b, $verCvt);
system("diff -u $tmp_a $tmp_b");
unlink $tmp_a;
unlink $tmp_b;
}
filterf(a, tmp_a)
filterf(b, tmp_b)
os.system("diff -u " + tmp_a + " " + tmp_b)
os.unlink(tmp_a)
os.unlink(tmp_b)
sub version_from {
my $fn = shift;
def version_from(filename):
# Return dump format
my $f1 = IO::File->new ($fn) or die "%Error: $! $fn,";
while (defined (my $line=$f1->getline())) {
last if $. > 10;
return hex $1 if $line =~ /\(format (0x[0-9.]+)\)/;
}
return 1.0;
}
with open(filename) as fh:
lineno = 0
for line in fh:
if lineno > 10:
break
match = re.search(r'format (0x[0-9.]+)', line)
if match:
return hex(match.group(1))
return 1.0
sub filter {
my $fn1 = shift;
my $fn2 = shift;
my $verCvt = shift;
def filterf(fn1, fn2):
# Remove hex numbers before diffing
my $f1 = IO::File->new ($fn1) or die "%Error: $! $fn1,";
my $f2 = IO::File->new ($fn2,"w") or die "%Error: $! $fn2,";
while (defined (my $line=$f1->getline())) {
same_line:
next if $line =~ / This=/;
$line =~ s/0x[a-f0-9]+/0x/g;
$line =~ s/<e[0-9]+\#?>/<e>/g;
$line =~ s/{[a-z]*\d+}/{}/g if !$Opt_Lineno;
if ($verCvt) {
next if $line =~ /^ NETLIST/;
if ($line =~ /: ([A-Z]+) /) {
my $type = $1;
next if $type =~ 'DTYPE';
if ($type eq 'TYPETABLE' || $type eq 'RANGE') {
$line =~ /^(\s+\S+:) /; my $prefix = $1;
while (defined ($line=$f1->getline())) {
next if $line =~ /^\s+[a-z]/; # Table body
next if $line =~ /^${prefix}[0-9]:/;
goto same_line;
}
next;
}
}
}
print $f2 $line;
}
$f1->close;
$f2->close;
}
with open(fn1) as fh1:
with open(fn2, "w") as fh2:
for line in fh1:
if re.search(r' This=', line):
continue
line = re.sub(r'0x[a-f0-9]+', '0x', line)
line = re.sub(r'<e[0-9]+\#?>', '<e>', line)
if not Args.no_lineno:
line = re.sub(r'{[a-z]*\d+}', '{}', line)
fh2.write(line)
#----------------------------------------------------------------------
sub usage {
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
exit(1); # Unreachable
}
######################################################################
######################################################################
sub debug {
$Debug = 1;
}
sub parameter {
my $param = shift;
if (!defined $Opt_A) {
$Opt_A = $param;
} elsif (!defined $Opt_B) {
$Opt_B = $param;
} else {
die "%Error: Unknown parameter: $param\n";
}
}
#######################################################################
sub run {
# Run a system command, check errors
my $command = shift;
print "\t$command\n";
system "$command";
my $status = $?;
($status == 0) or die "%Error: Command Failed $command, $status, stopped";
}
#######################################################################
__END__
=pod
=head1 NAME
verilator_difftree - Compare two Verilator debugging trees
=head1 SYNOPSIS
verilator_difftree .../a/a.tree .../b/a.tree
verilator_difftree .../a .../b
=head1 DESCRIPTION
Verilator_difftree is used for debugging Verilator tree output files. It
performs a diff between two files, or all files common between two
parser = argparse.ArgumentParser(
allow_abbrev=False,
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""Compare two Verilator debugging trees""",
epilog=
"""Verilator_difftree is used for debugging Verilator tree output files.
It performs a diff between two files, or all files common between two
directories, ignoring irrelevant pointer differences.
=head1 ARGUMENTS
=over 4
=item --help
Displays this message and program version and exits.
=item --nolineno
Do not show differences in line numbering.
=back
=head1 DISTRIBUTION
The latest version is available from L<https://verilator.org>.
For documentation see
https://verilator.org/guide/latest/exe_verilator_difftree.html
Copyright 2005-2021 by Wilson Snyder. This program is free software; you
can redistribute it and/or modify it under the terms of either the GNU
Lesser General Public License Version 3 or the Perl Artistic License
Version 2.0.
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
=head1 AUTHORS
parser.add_argument('--debug',
action='store_const',
const=9,
help='enable debug')
parser.add_argument('--no-lineno',
action='store_false',
help='do not show differences in line numbering')
parser.add_argument('filea', help='input file a to diff')
parser.add_argument('fileb', help='input file b to diff')
Wilson Snyder <[email protected]>
=head1 SEE ALSO
C<verilator>
and L<https://verilator.org/verilator_doc.html> for detailed documentation.
=cut
Args = parser.parse_args()
diff(Args.filea, Args.fileb)
######################################################################
### Local Variables:
### compile-command: "$V4/bin/verilator_difftree {$V4D,$V4}/test_regress/obj_dir/t_EXAMPLE/V*_03_*.tree"
### End:
# Local Variables:
# compile-command: "./verilator_difftree ../test_regress/t/t_difftree.{a,b}.tree"
# End:
+428 -511
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -10,11 +10,11 @@
require 5.005;
use warnings;
print "// DESCR"."IPTION: Generated by verilator_includer via makefile\n";
print "// DESCR" . "IPTION: Generated by verilator_includer via makefile\n";
foreach my $param (@ARGV) {
if ($param =~ /^-D([^=]+)=(.*)/) {
print "#define $1 $2\n"
print "#define $1 $2\n";
} else {
print "#include \"$param\"\n"
print "#include \"$param\"\n";
}
}
+173 -217
View File
@@ -1,248 +1,204 @@
#!/usr/bin/env perl
# See copyright, etc in below POD section.
#!/usr/bin/env python3
# pylint: disable=C0103,C0114,C0116,R0914,R0912,R0915,eval-used
######################################################################
require 5.006_001;
use warnings;
use Getopt::Long;
use IO::File;
use Pod::Usage;
eval { use Data::Dumper; $Data::Dumper::Indent = 1; }; # Debug, ok if missing
use strict;
use vars qw($Debug);
import argparse
import collections
import re
# from pprint import pprint
#======================================================================
######################################################################
#======================================================================
# main
def profcfunc(filename):
funcs = {}
$Debug = 0;
my $Opt_File;
autoflush STDOUT 1;
autoflush STDERR 1;
Getopt::Long::config("no_auto_abbrev");
if (! GetOptions(
"help" => \&usage,
"debug" => \&debug,
"<>" => \&parameter,
)) {
die "%Error: Bad usage, try 'verilator_profcfunc --help'\n";
}
with open(filename) as fh:
defined $Opt_File or die "%Error: No filename given\n";
for line in fh:
# %time cumesec selfsec calls {stuff} name
match = re.match(
r'^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$',
line)
if match:
pct = float(match.group(1))
sec = float(match.group(2))
calls = float(match.group(3))
func = match.group(4)
if func not in funcs:
funcs[func] = {'pct': 0, 'sec': 0, 'calls': 0}
funcs[func]['pct'] += pct
funcs[func]['sec'] += sec
funcs[func]['calls'] += calls
continue
profcfunc($Opt_File);
#----------------------------------------------------------------------
sub usage {
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
exit(1); # Unreachable
}
sub debug {
$Debug = 1;
}
sub parameter {
my $param = shift;
if (!defined $Opt_File) {
$Opt_File = $param;
} else {
die "%Error: Unknown parameter: $param\n";
}
}
#######################################################################
sub profcfunc {
my $filename = shift;
# Remove hex numbers before diffing
my $fh = IO::File->new ($filename) or die "%Error: $! $filename,";
my %funcs;
while (defined (my $line=$fh->getline())) {
# %time cumesec selfsec calls {stuff} name
if ($line =~ /^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$/) {
my $pct=$1; my $sec=$2; my $calls=$3; my $func=$4;
$funcs{$func}{pct} += $pct;
$funcs{$func}{sec} += $sec;
$funcs{$func}{calls} += $calls;
}
# Older gprofs have no call column for single-call functions
# %time cumesec selfsec {stuff} name
elsif ($line =~ /^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$/) {
my $pct=$1; my $sec=$2; my $calls=1; my $func=$3;
$funcs{$func}{pct} += $pct;
$funcs{$func}{sec} += $sec;
$funcs{$func}{calls} += $calls;
}
}
$fh->close;
# Older gprofs have no call column for single-call functions
# %time cumesec selfsec {stuff} name
match = re.match(
r'^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$',
line)
if match:
pct = float(match.group(1))
sec = float(match.group(2))
calls = 1
func = match.group(3)
if func not in funcs:
funcs[func] = {'pct': 0, 'sec': 0, 'calls': 0}
funcs[func]['pct'] += pct
funcs[func]['sec'] += sec
funcs[func]['calls'] += calls
continue
# Find modules
my %pointer_mods;
my %verilated_mods;
foreach my $func (keys %funcs) {
if ($func =~ /(.*)::_eval\(([a-zA-Z_0-9]+__Syms).*\)$/) {
$verilated_mods{$1} = qr/^$1/;
$pointer_mods{$2} = $1;
}
}
#print Dumper(\%pointer_mods, \%verilated_mods);
verilated_mods = {}
for func in funcs:
match = re.search(r'(.*)::eval(_step)?\(', func)
if match:
prefix = match.group(1)
if Args.debug:
print("-got _eval %s prefix=%s" % (func, prefix))
verilated_mods[prefix] = re.compile(r'^' + prefix)
# pprint(verilated_mods)
# Resort by Verilog name
my %vfuncs;
my %groups;
foreach my $func (keys %funcs) {
my $pct = $funcs{$func}{pct};
my $vfunc = $func;
my $design;
# Sort by Verilog name
vfuncs = {}
groups = {}
groups['type'] = collections.defaultdict(lambda: 0)
groups['design'] = collections.defaultdict(lambda: 0)
groups['module'] = collections.defaultdict(lambda: 0)
if ($func =~ /\(([a-zA-Z_0-9]+__Syms)/) {
$design = $pointer_mods{$1};
}
for func in funcs:
pct = funcs[func]['pct']
vfunc = func
foreach my $vde (keys %verilated_mods) {
last if $design;
if ($func =~ /$verilated_mods{$vde}/) {
$design=$vde;
last;
}
}
funcarg = re.sub(r'^.*\(', '', func)
if ($vfunc =~ /__PROF__([a-zA-Z_0-9]+)__l?([0-9]+)\(/) {
$vfunc = sprintf("VBlock %s:%d", $1, $2);
$groups{type}{"Verilog Blocks under $design"} += $pct;
$groups{design}{$design} += $pct;
$groups{module}{$1} += $pct;
} else {
if ($design) {
$vfunc = sprintf("VCommon %s", $func);
$groups{type}{"Common code under $design"} += $pct;
$groups{design}{$design} += $pct;
$groups{module}{$design." common code"} += $pct;
} elsif ($func =~ /^VL_[A-Z0-9_]+/
|| $func =~ /^_?vl_[a-zA-Z0-9_]+/
|| $func =~ /^verilated/i) {
$vfunc = sprintf("VLib %s", $func);
$groups{type}{'VLib'} += $pct;
$groups{design}{'VLib'} += $pct;
$groups{module}{'VLib'} += $pct;
} elsif ($func =~ /^_mcount_private/) {
$vfunc = sprintf("Prof %s", $func);
$groups{type}{'Prof'} += $pct;
$groups{design}{'Prof'} += $pct;
$groups{module}{'Prof'} += $pct;
} else {
$vfunc = sprintf("C++ %s", $func);
$groups{type}{'C++'} += $pct;
$groups{design}{'C++'} += $pct;
$groups{module}{'C++'} += $pct;
}
}
$vfuncs{$vfunc} = $funcs{$func};
}
design = None
for vde in verilated_mods:
if verilated_mods[vde].match(func) or verilated_mods[vde].match(
funcarg):
design = vde
break
vdesign = "-"
prof_match = re.search(r'__PROF__([a-zA-Z_0-9]+)__l?([0-9]+)\(', vfunc)
if design and prof_match:
linefunc = prof_match.group(1)
lineno = int(prof_match.group(2))
vfunc = "VBlock %s:%d" % (linefunc, lineno)
vdesign = design
groups['type']["Verilog Blocks under " + design] += pct
groups['design'][design] += pct
groups['module'][linefunc] += pct
elif design:
vfunc = "VCommon " + func
vdesign = design
groups['type']["Common code under " + design] += pct
groups['design'][design] += pct
groups['module'][design + " common code"] += pct
elif re.match(r'(VL_[A-Z0-9_]+|_?vl_[a-zA-Z0-9_]+|Verilated)', vfunc):
vfunc = "VLib " + func
groups['type']['VLib'] += pct
groups['design']['VLib'] += pct
groups['module']['VLib'] += pct
elif re.match(r'^_mcount_private', vfunc):
vfunc = "Prof " + func
groups['type']['Prof'] += pct
groups['design']['Prof'] += pct
groups['module']['Prof'] += pct
else:
vfunc = "C++ " + func
groups['type']['C++'] += pct
groups['design']['C++'] += pct
groups['module']['C++'] += pct
if vfunc not in vfuncs:
vfuncs[vfunc] = funcs[func]
vfuncs[vfunc]['design'] = vdesign
else:
vfuncs[vfunc]['pct'] += funcs[func]['pct']
vfuncs[vfunc]['calls'] += funcs[func]['calls']
vfuncs[vfunc]['sec'] += funcs[func]['sec']
for ftype in ['type', 'design', 'module']:
missing = 100
for item in groups[ftype].keys():
missing -= groups[ftype][item]
groups[ftype]["\377Unaccounted for/rounding error"] = missing
print("Overall summary by %s:" % ftype)
print(" %-6s %s" % ("% time", ftype))
for what in sorted(groups[ftype].keys()):
# \377 used to establish sort order
pwhat = re.sub(r'^\377', '', what)
print(" %6.2f %s" % (groups[ftype][what], pwhat))
print()
design_width = 1
for func in vfuncs:
if design_width < len(vfuncs[func]['design']):
design_width = len(vfuncs[func]['design'])
print("Verilog code profile:")
print(" These are split into three categories:")
print(" C++: Time in non-Verilated C++ code")
print(" Prof: Time in profile overhead")
print(" VBlock: Time attributable to a block in a" +
" Verilog file and line")
print(" VCommon: Time in a Verilated module," +
" due to all parts of the design")
print(" VLib: Time in Verilated common libraries," +
" called by the Verilated code")
print()
print(" % cumulative self ")
print((" time seconds seconds calls %-" + str(design_width) +
"s type filename and line number") % "design")
cume = 0
for func in sorted(vfuncs.keys(),
key=lambda f: vfuncs[f]['sec'],
reverse=True):
cume += vfuncs[func]['sec']
print(("%6.2f %9.2f %8.2f %10d %-" + str(design_width) + "s %s") %
(vfuncs[func]['pct'], cume, vfuncs[func]['sec'],
vfuncs[func]['calls'], vfuncs[func]['design'], func))
foreach my $type (qw(type design module)) {
my $missing = 100;
foreach (sort (keys %{$groups{$type}})) {
$missing -= $groups{$type}{$_};
}
if ($missing) {
$groups{$type}{"\377Unaccounted for/rounding error"} = $missing;
}
######################################################################
######################################################################
print("Overall summary by $type:\n");
printf(" %-6s %s\n","% time",$type);
foreach my $what (sort (keys %{$groups{$type}})) {
(my $pwhat = $what) =~ s/^\377//; # Just used to establish sort order
printf(" %6.2f %s\n", $groups{$type}{$what}, $pwhat);
}
print("\n");
}
print("Verilog code profile:\n");
print(" These are split into three categories:\n");
print(" C++: Time in non-Verilated C++ code\n");
print(" Prof: Time in profile overhead\n");
print(" VBlock: Time attributable to a block in a Verilog file and line\n");
print(" VCommon: Time in a Verilated module, due to all parts of the design\n");
print(" VLib: Time in Verilated common libraries, called by the Verilated code\n");
print("\n");
print(" % cumulative self \n");
print(" time seconds seconds calls type filename and line number\n");
my $cume = 0;
foreach my $func (sort {$vfuncs{$b}{sec} <=> $vfuncs{$a}{sec}
|| $a cmp $b}
(keys %vfuncs)) {
$cume += $vfuncs{$func}{sec};
printf +("%6.2f %9.2f %8.2f %8d %s\n",
$vfuncs{$func}{pct},
$cume, $vfuncs{$func}{sec},
$vfuncs{$func}{calls},
$func);
}
}
#######################################################################
__END__
=pod
=head1 NAME
verilator_profcfunc - Read gprof report created with --prof-cfuncs
=head1 SYNOPSIS
verilator --prof-cfuncs ....
gcc ....
{run executable}
gprof
verilator_profcfuncs gprof.out
=head1 DESCRIPTION
Verilator_profcfunc reads a profile report created by gprof. The names of
parser = argparse.ArgumentParser(
allow_abbrev=False,
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""Read gprof report created with --prof-cfuncs""",
epilog=
"""Verilator_profcfunc reads a profile report created by gprof. The names of
the functions are then transformed, assuming the user used Verilator's
--prof-cfuncs, and a report printed showing the percentage of time, etc,
in each Verilog block.
For documentation see
L<https://verilator.org/guide/latest/exe_verilator_profcfuncs.html>.
https://verilator.org/guide/latest/exe_verilator_profcfunc.html
=head1 ARGUMENT SUMMARY
<filename> Input file (gprof.out)
--help Display this help
=head1 DISTRIBUTION
The latest version is available from L<https://verilator.org>.
Copyright 2007-2021 by Wilson Snyder. This program is free software; you
Copyright 2002-2021 by Wilson Snyder. This program is free software; you
can redistribute it and/or modify it under the terms of either the GNU
Lesser General Public License Version 3 or the Perl Artistic License
Version 2.0.
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
=head1 SEE ALSO
parser.add_argument('--debug',
action='store_const',
const=9,
help='enable debug')
parser.add_argument('filename', help='input gprof output to process')
C<verilator>
and L<https://verilator.org/guide/latest/exe_verilator_profcfuncs.html> for
detailed documentation.
=cut
Args = parser.parse_args()
profcfunc(Args.filename)
######################################################################
### Local Variables:
### compile-command: "$V4/bin/verilator_profcfunc $V4/test_c/obj_dir/V*_03_*.tree $V4N/test_c/obj_dir/V*_03_*.tree"
### End:
# Local Variables:
# compile-command: "./verilator_profcfunc ../test_regress/t/t_profcfunc.gprof"
# End:
+8 -22
View File
@@ -7,7 +7,7 @@
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[4.210 2021-07-07],
AC_INIT([Verilator],[4.214 2021-10-17],
[https://verilator.org],
[verilator],[https://verilator.org])
# When releasing, also update header of Changes file
@@ -346,33 +346,19 @@ AC_SUBST(CFG_CXXFLAGS_PROFILE)
# Flag to select newest language standard supported
# Macros work such that first option that passes is the one we take
# Currently enabled gnu++14/c++14 due to packaged SystemC dependency
# gnu++17 is the newest that Verilator supports
# std++03 is the oldest that Verilator supports
# Currently enabled c++14 due to packaged SystemC dependency
# c++14 is the newest that Verilator is regressed to support
# c++11 is the oldest that Verilator supports
# gnu is requried for Cygwin to compile verilated.h successfully
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++20)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++14)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++11)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++03)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++20)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++17)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++14)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++14)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++11)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++11)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++03)
AC_SUBST(CFG_CXXFLAGS_STD_NEWEST)
# And likewise oldest standard (same list above, backwards)
# This is used for internal testing
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++03)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++11)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++14)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++17)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++20)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++03)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++11)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++14)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++17)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++20)
AC_SUBST(CFG_CXXFLAGS_STD_OLDEST)
# Flags for compiling Verilator internals including parser, and Verilated files
# These turn on extra warnings and are only used with 'configure --enable-ccwarn'
+6
View File
@@ -11,6 +11,7 @@ Andreas Kuster
Chris Randall
Conor McCullough
Dan Petrisko
Daniel Bates
David Horton
David Metz
David Stanford
@@ -30,6 +31,8 @@ Harald Heckmann
Howard Su
Huang Rui
HyungKi Jeong
Iru Cai
Ivan Vnučec
Iztok Jeras
James Hanlon
James Hutchinson
@@ -45,6 +48,7 @@ Josh Redford
Julien Margetts
Kaleb Barrett
Kanad Kanhere
Keith Colbert
Kevin Kiningham
Krzysztof Bieganski
Kuba Ober
@@ -83,6 +87,7 @@ Sebastien Van Cauwenberghe
Sergi Granell
Stefan Wallentowitz
Stephen Henry
Steven Hugg
Tim Snyder
Tobias Rosenkranz
Tobias Wölfel
@@ -99,3 +104,4 @@ Yossi Nivin
Yuri Victorovich
Yutetsu TAKATSUKASA
Yves Mathieu
Zhanglei Wang
+6
View File
@@ -0,0 +1,6 @@
.. comment: generated by t_lint_didnotconverge_bad
.. code-block:: sv
:linenos:
always_comb b = ~a;
always_comb a = b;
+7
View File
@@ -0,0 +1,7 @@
.. comment: generated by t_lint_didnotconverge_bad
.. code-block::
-V{t#,#}+ Vt_lint_didnotconverge_bad___024root___change_request
-V{t#,#}+ Vt_lint_didnotconverge_bad___024root___change_request_1
-V{t#,#} CHANGE: t/t_lint_didnotconverge_bad.v:14: a
%Error: t/t_lint_didnotconverge_bad.v:7: Verilated model didn't converge
+4
View File
@@ -0,0 +1,4 @@
.. comment: generated by t_lint_didnotconverge_nodbg_bad
.. code-block::
%Error: t/t_lint_didnotconverge_bad.v:7: Verilated model didn't converge
+5
View File
@@ -0,0 +1,5 @@
.. comment: generated by t_lint_stmtdly_bad
.. code-block:: sv
:emphasize-lines: 1
#100 $finish; //<--- Warning
+4
View File
@@ -0,0 +1,4 @@
.. comment: generated by t_lint_stmtdly_bad
.. code-block::
%Warning-STMTDLY: example.v:1:8 Unsupported: Ignoring delay on this delayed statement.
+1 -1
View File
@@ -1,4 +1,4 @@
# pylint: disable=C0103,C0114,C0116,E0402,W0622
# pylint: disable=C0103,C0114,C0116,C0301,E0402,W0622
#
# Configuration file for Verilator's Sphinx documentation builder.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
+2 -1
View File
@@ -13,7 +13,8 @@ When possible, please instead report bugs at `Verilator Issues
Primary author is Wilson Snyder <[email protected]>.
Major concepts by Paul Wasson, Duane Galbi, John Coiner and Jie Xu.
Major concepts by Paul Wasson, Duane Galbi, John Coiner, Geza Lore, Yutetsu
Takatsukasa, and Jie Xu.
Contributors
+5
View File
@@ -11,6 +11,11 @@ C++11 compiler support
require C++14 or newer compilers for both compiling Verilator and
compiling Verilated models no sooner than January 2022.
Verilated_heavy.h
The legacy "verilated_heavy.h" include was replaced with just including
"verilated.h". Verilated_heavy.h is planned for removal no sooner than
July 2022.
Configuration File -msg
The :vlopt:`lint_off` "-msg" option has been replaced with the "-rule"
option. "-msg" is planned for removal no sooner than January 2021.
+6
View File
@@ -62,6 +62,12 @@ Summary:
makes sense for a single-clock-domain module where it's typical to want
to capture one posedge eval() and one negedge eval().
.. option:: +verilator+prof+vlt+file+<filename>
When a model was Verilated using :vlopt:`--prof-threads`, sets the
profile-guided optimization data runtime filename to dump to. Defaults
to :file:`profile.vlt`.
.. option:: +verilator+rand+reset+<value>
When a model was Verilated using :vlopt:`--x-initial unique
+19 -3
View File
@@ -536,6 +536,14 @@ Summary:
times, but potentially faster simulation speed. This setting is ignored
for very small modules; they will always be inlined, if allowed.
.. option:: --instr-count-dpi <value>
Assumed dynamic instruction count of the average DPI import. This is used
by the partitioning algorithm when creating a multithread model. The
default value is 200. Adjusting this to an appropriate value can yield
performance improvements in multithreaded models. Ignored when creating a
single threaded model.
.. option:: -j [<value>]
Specify the level of parallelism for :vlopt:`--build`. The <value> must
@@ -808,7 +816,7 @@ Summary:
When compiling the C++ code, enable the compiler's profiling flag
(e.g. :code:`g++ -pg`). See :ref:`Profiling`.
Using :vlopt:`--prof-cfuncs` also enables :vlopt:`prof-c`.
Using :vlopt:`--prof-cfuncs` also enables :vlopt:`--prof-c`.
.. option:: --prof-cfuncs
@@ -820,12 +828,12 @@ Summary:
came from. This allows gprof or oprofile reports to be correlated with
the original Verilog source statements. See :ref:`Profiling`.
Using :vlopt:`--prof-cfuncs` also enables :vlopt:`prof-c`.
Using :vlopt:`--prof-cfuncs` also enables :vlopt:`--prof-c`.
.. option:: --prof-threads
Enable gantt chart data collection for threaded builds. See :ref:`Thread
Profiling`.
Profiling` and :ref:`Thread PGO`.
.. option:: --protect-key <key>
@@ -1044,6 +1052,8 @@ Summary:
Verilator assumes DPI pure imports are threadsafe, but non-pure DPI
imports are not.
See also :vlopt:`--instr-count-dpi` option.
.. option:: --threads-max-mtasks <value>
Rarely needed. When using :vlopt:`--threads`, specify the number of
@@ -1602,6 +1612,12 @@ The grammar of configuration commands is as follows:
:option:`/*verilator&32;public_flat*/`, etc, metacomments. See
e.g. :ref:`VPI Example`.
.. option:: profile_data -mtask "<mtask_hash>" -cost <cost_value>
Feeds profile-guided optimization data into the Verilator algorithms in
order to improve model runtime performance. This option is not expected
to be used by users directly. See :ref:`Thread PGO`.
.. option:: sc_bv -module "<modulename>" [-task "<taskname>"] -var "<signame>"
.. option:: sc_bv -module "<modulename>" [-function "<funcname>"] -var "<signame>"
+38 -27
View File
@@ -10,42 +10,60 @@ starts and ends, and showing when each thread is busy or idle.
For an overview of use of verilator_gantt, see :ref:`Profiling`.
Gantt Chart Contents
--------------------
Gantt Chart VCD
---------------
The generated Gantt chart has time on the X-axis. Times shown are to the
scale printed, i.e. a certain about of time for each character width. The
Y-axis shows threads, each thread's execution is shown on one line. That
line shows "[" at the position in time when it executes.
Verilated_gnatt creates a value change dump (VCD) format dump file which
may be viewed in a waveform viewer (e.g. C<GTKWave>):
Following the "[" is the CPU number the task executed on, followed by zero
or more "-" to make the width of the characters match the scaled execution
time, followed by a "]". If the scale is too small, the CPU number and
mtask number will not be printed. If the scale is very small, a "&"
indicates multiple mtasks started at that time position.
.. figure:: figures/fig_gantt_min.png
Example verilator_gantt output, as viewed with GTKWave.
The viewed waveform chart has time on the X-axis, with one unit for each
time tick of the system's high-performance counter.
Also creates a value change dump (VCD) format dump file which may be viewed
in a waveform viewer (e.g. C<GTKWave>). See below.
Gantt Chart VCD Signals
-----------------------
In waveforms there are the following signals. Most signals the "decimal"
format will remove the leading zeros and make the traces easier to read.
In waveforms there are the following signals. In GTKWave, using a data
format of "decimal" will remove the leading zeros and make the traces
easier to read.
parallelism
evals
Increments each time when eval_step was measured to be active. This
allow visualization of how much time eval_step was active.
eval_loop
Increments each time when the evaluation loop within eval_step was
measured to be active. For best performance there is only a single
evaluation loop within each eval_step call, that is the eval_loop
waveform looks identical to the evals waveform.
measured_parallelism
The number of mtasks active at this time, for best performance this will
match the thread count. You may want to use an "analog step" format to
match the thread count. In GTKWave, use a data format of "analog step" to
view this signal.
predicted_parallelism
The number of mtasks Verilator predicted would be active at this time,
for best performance this will match the thread count. In GTKWave, use a
data format of "analog step" to view this signal.
cpu#_thread
For the given CPU number, the thread number executing.
For the given CPU number, the thread number measured to be executing.
mtask#_cpu
For the given mtask id, the CPU it is executing on.
For the given mtask id, the CPU it was measured to execute on.
thread#_mtask
For the given thread number, the mtask id executing.
For the given thread number, the mtask id it was executing.
predicted_thread#_mtask
For the given thread number, the mtask id Verilator predicted would be
executing.
verilator_gantt Arguments
-------------------------
@@ -60,13 +78,6 @@ The filename to read data from, defaults to "profile_threads.dat".
Displays a help summary, the program version, and exits.
.. option:: --scale <n>
Sets the number of time units per character on the X-axis of the generated
Gantt chart. (On x86, time units are rdtsc ticks.) Defaults to 0, which
will automatically compute a reasonable scale where no two mtasks need to
fit into same character width's worth of scaled time.
.. option:: --no-vcd
Disables creating a .vcd file.
@@ -1,20 +1,24 @@
.. Copyright 2003-2021 by Wilson Snyder.
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
verilator_profcfuncs
====================
verilator_profcfunc
===================
Verilator_profcfunc reads a profile report created by gprof. The names of
the functions are then transformed, assuming the user used Verilator's
--prof-cfuncs, and a report printed showing the percentage of time, etc, in
each Verilog block.
For an overview of use of verilator_profcfuncs, see :ref:`Profiling`.
Due to rounding errors in gprof reports, the input report's percentages may
not total to 100%. In the verilator_profcfunc report this will get
reported as a rounding error.
verilator_profcfuncs Arguments
------------------------------
For an overview of use of verilator_profcfunc, see :ref:`Profiling`.
.. program:: verilator_profcfuncs
verilator_profcfunc Arguments
-----------------------------
.. program:: verilator_profcfunc
.. option:: <filename>
+1 -1
View File
@@ -15,5 +15,5 @@ options to each executable.
exe_verilator.rst
exe_verilator_coverage.rst
exe_verilator_gantt.rst
exe_verilator_profcfuncs.rst
exe_verilator_profcfunc.rst
exe_sim.rst
Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 37 KiB

+21 -1
View File
@@ -74,7 +74,7 @@ For --cc/--sc, it creates:
- DPI import and export declarations (from --dpi)
* - *{prefix}*\ __Dpi.cpp
- Global DPI export wrappers (from --dpi)
* - *{prefix}*\ __Dpi_Export\ *{__n}\ .cpp
* - *{prefix}*\ __Dpi_Export\ *{__n}*\ .cpp
- DPI export wrappers scoped to this particular model (from --dpi)
* - *{prefix}*\ __Inlines.h
- Inline support functions
@@ -145,3 +145,23 @@ After running Make, the C++ compiler may produce the following:
- Intermediate dependencies
* - *{prefix}{misc}*\ .o
- Intermediate objects
The Verilated executable may produce the following:
.. list-table::
* - coverage.dat
- Code coverage output, and default input filename for :command:`verilator_coverage`
* - gmon.out
- GCC/clang code profiler output, often fed into :command:`verilator_profcfunc`
* - profile.vlt
- -profile data file for :ref:`Thread PGO`
* - profile_threads.dat
- -profile-threads data file for :command:`verilator_gnatt`
Verilator_gantt may produce the following:
.. list-table::
* - profile_threads.vcd
- Gantt report waveform output
+8 -7
View File
@@ -23,6 +23,8 @@ package:
apt-get install verilator # On Ubuntu
For other distributions refer to `Repology Verilator Distro Packages
<https://repology.org/project/verilator>`__.
.. _Git Install:
@@ -44,21 +46,20 @@ In brief, to install from git:
#sudo apt-get install zlibc zlib1g zlib1g-dev # Ubuntu only (ignore if gives error)
git clone https://github.com/verilator/verilator # Only first time
## Note the URL above is not a page you can see with a browser, it's for git only
# Every time you need to build:
unsetenv VERILATOR_ROOT # For csh; ignore error if on bash
unset VERILATOR_ROOT # For bash
cd verilator
git pull # Make sure git repository is up-to-date
git tag # See what versions exist
git pull # Make sure git repository is up-to-date
git tag # See what versions exist
#git checkout master # Use development branch (e.g. recent bug fixes)
#git checkout stable # Use most recent stable release
#git checkout v{version} # Switch to specified release version
autoconf # Create ./configure script
./configure # Configure and create Makefile
make -j # Build Verilator itself
autoconf # Create ./configure script
./configure # Configure and create Makefile
make -j `nproc` # Build Verilator itself (if error, try just 'make')
sudo make install
@@ -286,7 +287,7 @@ Compile Verilator:
::
make -j
make -j `nproc` # Or if error on `nproc`, the number of CPUs in system
Test
+126 -6
View File
@@ -26,7 +26,8 @@ risk of reset bugs in trade for performance; see the above documentation
for these options.
If using Verilated multithreaded, use ``numactl`` to ensure you are using
non-conflicting hardware resources. See :ref:`Multithreading`.
non-conflicting hardware resources. See :ref:`Multithreading`. Also
consider using profile-guided optimization, see :ref:`Thread PGO`.
Minor Verilog code changes can also give big wins. You should not have any
UNOPTFLAT warnings from Verilator. Fixing these warnings can result in
@@ -93,9 +94,7 @@ cases, for example regressions, it is usually worth spending extra
compilation time to reduce total CPU time.
If you will be running many simulations on a single model, you can
investigate profile guided optimization. With GCC, using GCC's
"-fprofile-arcs", then GCC's "-fbranch-probabilities" will yield another
15% or so.
investigate profile guided optimization. See :ref:`Compiler PGO`.
Modern compilers also support link-time optimization (LTO), which can help
especially if you link in DPI code. To enable LTO on GCC, pass "-flto" in
@@ -230,7 +229,7 @@ coverage point insertions into the model and collect the coverage data.
To get the coverage data from the model, in the user wrapper code,
typically at the end once a test passes, call
:code:`Verilated::coveragep()->write` with an argument of the filename for
:code:`Verilated::threadContextp()->coveragep()->write` with an argument of the filename for
the coverage data file to write coverage data to (typically
"logs/coverage.dat").
@@ -298,6 +297,9 @@ With the :vlopt:`--prof-threads` option, Verilator will:
* Add code to save profiling data in non-human-friendly form to the file
specified with :vlopt:`+verilator+prof+threads+file+\<filename\>`.
* Add code to save profiling data for thread profile-guided
optimization. See :ref:`Thread PGO`.
The :command:`verilator_gantt` program may then be run to transform the
saved profiling file into a nicer visual format and produce some related
statistics.
@@ -306,7 +308,7 @@ statistics.
Example verilator_gantt output, as viewed with GTKWave.
The parallelism shows the number of CPUs being used at a given moment.
The measured_parallelism shows the number of CPUs being used at a given moment.
The cpu_thread section shows which thread is executing on each of the physical CPUs.
@@ -314,6 +316,7 @@ statistics.
For more information see :command:`verilator_gantt`.
.. _Profiling ccache efficiency:
Profiling ccache efficiency
@@ -377,3 +380,120 @@ For example:
os >> main_time;
os >> *topp;
}
Profile-Guided Optimization
===========================
Profile-guided optimization is the technique where profiling data is
collected by running your simulation executable, then this information is
used to guide the next Verilation or compilation.
There are two forms of profile-guided optimizations. Unfortunately for
best results they must each be performed from the highest level code to the
lowest, which means performing them separately and in this order:
* :ref:`Thread PGO`
* :ref:`Compiler PGO`
Other forms of PGO may be supported in the future, such as clock and reset
toggle rate PGO, branch prediction PGO, statement execution time PGO, or
others as they prove beneficial.
.. _Thread PGO:
Thread Profile-Guided Optimization
----------------------------------
Verilator supports thread profile-guided optimization (Thread PGO) to
improve multithreaded performance.
When using multithreading, Verilator computes how long macro tasks take and
tries to balance those across threads. (What is a macro-task? See the
Verilator internals document (:file:`docs/internals.rst` in the
distribution.) If the estimations are incorrect, the threads will not be
balanced, leading to decreased performance. Thread PGO allows collecting
profiling data to replace the estimates and better optimize these
decisions.
To use Thread PGO, Verilate the model with the :vlopt:`--prof-threads`
option.
Run the model executable. When the executable exits, it will create a
profile.vlt file.
Rerun Verilator, optionally omitting the :vlopt:`--prof-threads` option,
and adding the profile.vlt generated earlier to the command line.
Note there is no Verilator equivalent to GCC's --fprofile-use. Verilator's
profile data file (profile.vlt) can be placed on the verilator command line
directly without any prefix.
If results from multiple simulations are to be used in generating the
optimization, multiple simulation's profile.vlt may be concatenated
externally, or each of the files may be fed as separate command line
options into Verilator. Verilator will simply sum the profile results, so
a longer running test will have proportionally more weight for optimization
than a shorter running test.
If you provide any profile feedback data to Verilator, and it cannot use
it, it will issue the :option:`PROFOUTOFDATE` warning that threads were
scheduled using estimated costs. This usually indicates that the profile
data was generated from different Verilog source code than Verilator is
currently running against. Therefore, repeat the data collection phase to
create new profiling data, then rerun Verilator with the same input source
files and that new profiling data.
.. _Compiler PGO:
Compiler Profile-Guided Optimization
------------------------------------
GCC and Clang support compiler profile-guided optimization (PGO). This
optimizes any C/C++ program including Verilated code. Using compiler PGO
typically yields improvements of 5-15% on both single-threaded and
multi-threaded models.
To use compiler PGO with GCC or Clang, please see the appropriate compiler
documentation. The process in GCC 10 was as follows:
1. Compile the Verilated model with the compiler's "-fprofile-generate"
flag:
.. code-block:: bash
verilator [whatever_flags] --make \
-CFLAGS -fprofile-generate -LDFLAGS -fprofile-generate
or, if calling make yourself, add -fprofile-generate appropriately to your
Makefile.
2. Run your simulation. This will create \*.gcda file(s) in the same
directory as the source files.
3. Recompile the model with -fprofile-use. The compiler will read the
\*.gcda file(s).
For GCC:
.. code-block:: bash
verilator [whatever_flags] --build \
-CFLAGS "-fprofile-use -fprofile-correction"
For Clang:
.. code-block:: bash
llvm-profdata merge -output default.profdata *.profraw
verilator [whatever_flags] --build \
-CFLAGS "-fprofile-use -fprofile-correction"
or, if calling make yourself, add these CFLAGS switches appropriately to
your Makefile.
Clang and GCC also support -fauto-profile which uses sample-based
feedback-directed optimization. See the appropriate compiler
documentation.
+6
View File
@@ -216,6 +216,12 @@ be done by a "main thread". In most cases the eval thread and main thread
are the same thread (i.e. the user's top C++ testbench runs on a single
thread), but this is not required.
When making frequent use of DPI imported functions in a multi-threaded
model, it may be beneficial to performance to adjust the
:vlopt:`--instr-count-dpi` option based on some experimentation. This
influences the partitioning of the model by adjusting the assumed execution
time of DPI imports.
The :vlopt:`--trace-threads` options can be used to produce trace dumps
using multiple threads. If :vlopt:`--trace-threads` is set without
:vlopt:`--threads`, then :vlopt:`--trace-threads` will imply
+85 -36
View File
@@ -130,9 +130,10 @@ List Of Warnings
.. option:: BADSTDPRAGMA
Error that a pragma is badly formed, when that pragma is defined by IEEE1800-2017.
For example, an empty `pragma line, or an incorrect specified '`pragma protect'.
Note that 3rd party pragmas not defined by IEEE1800-2017 are ignored.
Error that a pragma is badly formed, when that pragma is defined by IEEE
1800-2017. For example, an empty pragma line, or an incorrect specified
'pragma protect'. Note that 3rd party pragmas not defined by IEEE
1800-2017 are ignored.
.. option:: BLKANDNBLK
@@ -481,39 +482,44 @@ List Of Warnings
Faulty example:
.. code-block:: sv
.. include:: ../../docs/gen/ex_DIDNOTCONVERGE_faulty.rst
always_comb b = ~a;
always_comb a = b
Results in at runtime (not when Verilated):
This code will toggle forever, and thus to prevent an infinite loop, the
executable will give the didn't converge error.
.. include:: ../../docs/gen/ex_DIDNOTCONVERGE_nodbg_msg.rst
To debug this, first review any UNOPTFLAT warnings that were ignored.
Though typically it is safe to ignore UNOPTFLAT (at a performance cost),
at the time of issuing a UNOPTFLAT Verilator did not know if the logic
would eventually converge and assumed it would.
This is because the signals keep toggling even with out time
passing. Thus to prevent an infinite loop, the Verilated executable
gives the DIDNOTCONVERGE error.
To debug this, first review any UNOPT or UNOPTFLAT warnings that were
ignored. Though typically it is safe to ignore UNOPTFLAT (at a
performance cost), at the time of issuing a UNOPTFLAT Verilator did not
know if the logic would eventually converge and assumed it would.
Next, run Verilator with :vlopt:`--prof-cfuncs -CFLAGS -DVL_DEBUG
<--prof-cfuncs>`. Rerun the test. Now just before the convergence
error you should see additional output similar to this:
.. code-block::
.. include:: ../../docs/gen/ex_DIDNOTCONVERGE_msg.rst
CHANGE: filename.v:1: b
CHANGE: filename.v:2: a
This means that signal b and signal a keep changing, inspect the code
that modifies these signals. Note if many signals are getting printed
then most likely all of them are oscillating. It may also be that
e.g. "a" may be oscillating, then "a" feeds signal "c" which then is
also reported as oscillating.
The CHANGE line means that on the given filename and line number that
drove a signal, the signal 'a' kept changing. Inspect the code that
modifies these signals. Note if many signals are getting printed then
most likely all of them are oscillating. It may also be that e.g. "a"
may be oscillating, then "a" feeds signal "c" which then is also
reported as oscillating.
One way DIDNOTCONVERGE may occur is flops are built out of gate
primitives. error. Verilator does not support building flops or latches
out of gate primitives, and any such code must change to use behavioral
primitives. Verilator does not support building flops or latches out of
gate primitives, and any such code must change to use behavioral
constructs (e.g. always_ff and always_latch).
Another way DIDNOTCONVERGE may occur is if # delays are used to generate
clocks. Verilator ignores the delays and gives an :option:`ASSIGNDLY`
or :option:`STMTDLY` warning. If these were suppressed, due to the
absense of the delay, the code may now oscillate.
Finally, rare, more difficult cases can be debugged like a C++ program;
either enter :command:`gdb` and use its tracing facilities, or edit the
generated C++ code to add appropriate prints to see what is going on.
@@ -572,7 +578,7 @@ List Of Warnings
must end in newline, as otherwise for example :command:`cat` with the
file as an argument may produce undesirable results.
Repair by adding a newline to the end of the file.
Repair by appending a newline to the end of the file.
Disabled by default as this is a code style warning; it will simulate
correctly.
@@ -792,8 +798,8 @@ List Of Warnings
.. TODO better example
Warns that a module has multiple definitions. Generally this indicates
a coding error, or a mistake in a library file and it's good practice to
have one module per file (and only put each file once on the command
a coding error, or a mistake in a library file, and it's good practice
to have one module per file (and only put each file once on the command
line) to avoid these issues. For some gate level netlists duplicates
are sometimes unavoidable, and MODDUP should be disabled.
@@ -1005,9 +1011,25 @@ List Of Warnings
a var/reg must be used as the target of procedural assignments.
.. option:: PROFOUTOFDATE
Warns that threads were scheduled using estimated costs, despite the
fact that data was provided from profile-guided optimization (see
:ref:`Thread PGO`) as fed into Verilator using the
:option:`profile_data` configuration file option. This usually
indicates that the profile data was generated from different Verilog
source code than Verilator is currently running against.
It is recommended to create new profiling data, then rerun Verilator
with the same input source files and that new profiling data.
Ignoring this warning may only slow simulations, it will simulate
correctly.
.. option:: PROTECTED
Warning that a '`pragma protected' section was encountered. The code
Warning that a 'pragma protected' section was encountered. The code
inside the protected region will be partly checked for correctness, but is
otherwise ignored.
@@ -1183,9 +1205,25 @@ List Of Warnings
Faulty example:
.. code-block:: sv
.. include:: ../../docs/gen/ex_STMTDLY_faulty.rst
#100 $finish; //<--- Warning
Results in:
.. include:: ../../docs/gen/ex_STMTDLY_msg.rst
This is a warning because Verilator does not support delayed statements.
It will simply ignore all such delays. In many cases ignoring a delay
might be harmless, but if the delayed statement is, as in this example,
used to cause some important action at a later time, it might be an
important difference.
Some possible work arounds:
* Move the delayed statement into the C++ wrapper file, where the
stimulus and clock generation can be done in C++.
* Convert the statement into a FSM, or other statement that tests
against $time.
.. option:: SYMRSVDWORD
@@ -1353,7 +1391,9 @@ List Of Warnings
Often UNOPTFLAT is caused by logic that isn't truly circular as viewed by
synthesis which analyzes interconnection per-bit, but is circular to
simulation which analyzes per-bus:
simulation which analyzes per-bus.
Faulty example:
.. code-block:: sv
@@ -1466,9 +1506,16 @@ List Of Warnings
Error that a construct might be legal according to IEEE but is not
currently supported by Verilator.
A typical workaround is to recode the construct into a simpler and more
common alternative language construct.
Alternatively, check if the construct is supported by other tools, and
if so please consider submitting a github pull request against the
Verilator sources to implement the missing unsupported feature.
This error may be ignored with :vlopt:`--bbox-unsup`, however this will
make the design simulate incorrectly; see the details under
:vlopt:`--bbox-unsup`.
make the design simulate incorrectly and is only intended for lint
usage; see the details under :vlopt:`--bbox-unsup`.
.. option:: UNUSED
@@ -1598,7 +1645,7 @@ List Of Warnings
* A part select has a different size then needed to index into the
packed or unpacked array (etc).
Verilator for attempts to track the minimum width of unsized constants
Verilator attempts to track the minimum width of unsized constants
and will suppress the warning when the minimum width is appropriate to
fit the required size.
@@ -1607,11 +1654,13 @@ List Of Warnings
The recommendation is to fix these issues by:
* Resizing the variable or constant. E.g. :code:`2'd2` instead of :code:`3'd2`.
* Resizing the variable or constant to match the needed size for the
expression. E.g. :code:`2'd2` instead of :code:`3'd2`.
* Using :code:`'0` or :code:`'1`.
* Using :code:`'0` or :code:`'1` which automatically resize in an
expression.
* Using part select to narrow a variable. E.g. :code:`too_wide[1:0]`.
* Using part selects to narrow a variable. E.g. :code:`too_wide[1:0]`.
* Using concatenate to widen a variable. E.g. :code:`{1'b1, too_narrow}`.
+3
View File
@@ -405,6 +405,9 @@ routines in the sources to rely more heavily on randomness, and
generally try harder not to keep input nodes together when we have the
option to scramble things.
Profile-guided optimization make this a bit better, by adjusting mtask
scheduling, but this does not yet guide the packing into mtasks.
Performance Regression
""""""""""""""""""""""
-1
View File
@@ -676,7 +676,6 @@ prev
printf
printtimescale
profcfunc
profcfuncs
prototyptes
ps
pthread
+2 -7
View File
@@ -4989,9 +4989,7 @@ unsigned int secnum = 0;
int blocks_skipped = 0;
fst_off_t blkpos = 0;
uint64_t seclen, beg_tim;
#ifdef FST_DEBUG
uint64_t end_tim;
#endif
uint64_t frame_uclen, frame_clen, frame_maxhandle, vc_maxhandle;
fst_off_t vc_start;
fst_off_t indx_pntr, indx_pos;
@@ -5058,14 +5056,11 @@ for(;;)
if(!seclen) break;
beg_tim = fstReaderUint64(xc->f);
#ifdef FST_DEBUG
end_tim =
#endif
fstReaderUint64(xc->f);
end_tim = fstReaderUint64(xc->f);
if(xc->limit_range_valid)
{
if(beg_tim < xc->limit_range_start)
if(end_tim < xc->limit_range_start)
{
blocks_skipped++;
blkpos += seclen;
+122 -111
View File
@@ -173,9 +173,9 @@ void vl_stop_maybe(const char* filename, int linenum, const char* hier, bool may
void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE {
#ifdef VL_THREADED
VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { //
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
vl_finish(filename, linenum, hier);
}));
}});
#else
vl_finish(filename, linenum, hier);
#endif
@@ -183,9 +183,9 @@ void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAF
void VL_STOP_MT(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_SAFE {
#ifdef VL_THREADED
VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { //
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
vl_stop_maybe(filename, linenum, hier, maybe);
}));
}});
#else
vl_stop_maybe(filename, linenum, hier, maybe);
#endif
@@ -193,9 +193,9 @@ void VL_STOP_MT(const char* filename, int linenum, const char* hier, bool maybe)
void VL_FATAL_MT(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_SAFE {
#ifdef VL_THREADED
VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { //
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
vl_fatal(filename, linenum, hier, msg);
}));
}});
#else
vl_fatal(filename, linenum, hier, msg);
#endif
@@ -208,14 +208,14 @@ void VL_FATAL_MT(const char* filename, int linenum, const char* hier, const char
std::string _vl_string_vprintf(const char* formatp, va_list ap) VL_MT_SAFE {
va_list aq;
va_copy(aq, ap);
int len = VL_VSNPRINTF(nullptr, 0, formatp, aq);
size_t len = VL_VSNPRINTF(nullptr, 0, formatp, aq);
va_end(aq);
if (VL_UNLIKELY(len < 1)) return "";
char* bufp = new char[len + 1];
char* const bufp = new char[len + 1];
VL_VSNPRINTF(bufp, len + 1, formatp, ap);
const std::string out = std::string(bufp, len);
std::string out{bufp, len}; // Not const to allow move optimization
delete[] bufp;
return out;
}
@@ -263,9 +263,9 @@ void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE {
va_start(ap, formatp);
const std::string out = _vl_string_vprintf(formatp, ap);
va_end(ap);
VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { //
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
VL_PRINTF("%s", out.c_str());
}));
}});
}
#endif
@@ -276,7 +276,7 @@ static vluint32_t vl_sys_rand32() VL_MT_UNSAFE {
// Return random 32-bits using system library.
// Used only to construct seed for Verilator's PNRG.
static VerilatedMutex s_mutex;
const VerilatedLockGuard lock(s_mutex); // Otherwise rand is unsafe
const VerilatedLockGuard lock{s_mutex}; // Otherwise rand is unsafe
#if defined(_WIN32) && !defined(__CYGWIN__)
// Windows doesn't have lrand48(), although Cygwin does.
return (std::rand() << 16) ^ std::rand();
@@ -742,7 +742,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
} else if (!inPct) { // Normal text
// Fast-forward to next escape and add to output
const char* ep = pos;
while (ep[0] && ep[0] != '%') ep++;
while (ep[0] && ep[0] != '%') ++ep;
if (ep != pos) {
output.append(pos, ep - pos);
pos += ep - pos - 1;
@@ -776,7 +776,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
output += '%';
break;
case 'N': { // "C" string with name of module, add . if needed
const char* cstrp = va_arg(ap, const char*);
const char* const cstrp = va_arg(ap, const char*);
if (VL_LIKELY(*cstrp)) {
output += cstrp;
output += '.';
@@ -784,13 +784,13 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
break;
}
case 'S': { // "C" string
const char* cstrp = va_arg(ap, const char*);
const char* const cstrp = va_arg(ap, const char*);
output += cstrp;
break;
}
case '@': { // Verilog/C++ string
va_arg(ap, int); // # bits is ignored
const std::string* cstrp = va_arg(ap, const std::string*);
const std::string* const cstrp = va_arg(ap, const std::string*);
std::string padding;
if (width > cstrp->size()) padding.append(width - cstrp->size(), ' ');
output += left ? (*cstrp + padding) : (padding + *cstrp);
@@ -808,7 +808,8 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
const int timeunit = va_arg(ap, int);
output += _vl_vsformat_time(t_tmp, d, timeunit, left, width);
} else {
std::string fmts(pctp, pos - pctp + 1);
const size_t len = pos - pctp + 1;
std::string fmts{pctp, len};
VL_SNPRINTF(t_tmp, VL_VALUE_STRING_MAX_WIDTH, fmts.c_str(), d);
output += t_tmp;
}
@@ -862,7 +863,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
if (VL_SIGN_E(lbits, lwp[VL_WORDS_I(lbits) - 1])) {
VlWide<VL_VALUE_STRING_MAX_WIDTH / 4 + 2> neg;
VL_NEGATE_W(VL_WORDS_I(lbits), neg, lwp);
append = std::string("-") + VL_DECIMAL_NW(lbits, neg);
append = std::string{"-"} + VL_DECIMAL_NW(lbits, neg);
} else {
append = VL_DECIMAL_NW(lbits, lwp);
}
@@ -957,7 +958,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
}
break;
default: { // LCOV_EXCL_START
const std::string msg = std::string("Unknown _vl_vsformat code: ") + pos[0];
const std::string msg = std::string{"Unknown _vl_vsformat code: "} + pos[0];
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
break;
} // LCOV_EXCL_STOP
@@ -969,14 +970,14 @@ 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 {
if (fp) {
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 {
if (fp) {
if (VL_LIKELY(fp)) {
std::fgetc(fp);
} else {
floc -= 8;
@@ -985,7 +986,7 @@ static inline void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE {
static inline int _vl_vsss_peek(FILE* fp, int& floc, const WDataInP fromp,
const std::string& fstr) VL_MT_SAFE {
// Get a character without advancing
if (fp) {
if (VL_LIKELY(fp)) {
const int data = std::fgetc(fp);
if (data == EOF) return EOF;
ungetc(data, fp);
@@ -1217,7 +1218,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
case 'u': {
// Read packed 2-value binary data
const int bytes = VL_BYTES_I(obits);
char* out = reinterpret_cast<char*>(owp);
char* const out = reinterpret_cast<char*>(owp);
if (!_vl_vsss_read_bin(fp, floc, fromp, fstr, out, bytes)) goto done;
const int last = bytes % 4;
if (last != 0
@@ -1242,7 +1243,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
break;
}
default: { // LCOV_EXCL_START
const std::string msg = std::string("Unknown _vl_vsscanf code: ") + pos[0];
const std::string msg = std::string{"Unknown _vl_vsscanf code: "} + pos[0];
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
break;
} // LCOV_EXCL_STOP
@@ -1253,19 +1254,19 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
// Reload data if non-wide (if wide, we put it in the right place directly)
if (obits == 0) { // Due to inIgnore
} else if (obits == -1) { // string
std::string* p = va_arg(ap, std::string*);
std::string* const p = va_arg(ap, std::string*);
*p = t_tmp;
} else if (obits <= VL_BYTESIZE) {
CData* p = va_arg(ap, CData*);
CData* const p = va_arg(ap, CData*);
*p = owp[0];
} else if (obits <= VL_SHORTSIZE) {
SData* p = va_arg(ap, SData*);
SData* const p = va_arg(ap, SData*);
*p = owp[0];
} else if (obits <= VL_IDATASIZE) {
IData* p = va_arg(ap, IData*);
IData* const p = va_arg(ap, IData*);
*p = owp[0];
} else if (obits <= VL_QUADSIZE) {
QData* p = va_arg(ap, QData*);
QData* const p = va_arg(ap, QData*);
*p = VL_SET_QW(owp);
}
}
@@ -1337,7 +1338,7 @@ IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE {
if (VL_UNLIKELY(str.empty())) return 0;
// V3Emit has static check that bytes < VL_TO_STRING_MAX_WORDS, but be safe
// V3Emit has static check that bytes < VL_VALUE_STRING_MAX_WORDS, but be safe
if (VL_UNCOVERABLE(bytes < str.size())) {
VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: fgets buffer overrun"); // LCOV_EXCL_LINE
}
@@ -1346,7 +1347,6 @@ IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE {
return got;
}
// declared in verilated_heavy.h
IData VL_FGETS_NI(std::string& dest, IData fpi) VL_MT_SAFE {
return getLine(dest, fpi, std::numeric_limits<size_t>::max());
}
@@ -1354,7 +1354,7 @@ IData VL_FGETS_NI(std::string& dest, IData fpi) VL_MT_SAFE {
IData VL_FERROR_IN(IData, std::string& outputr) VL_MT_SAFE {
// We ignore lhs/fpi - IEEE says "most recent error" so probably good enough
const IData ret = errno;
outputr = std::string(::std::strerror(ret));
outputr = std::string{::std::strerror(ret)};
return ret;
}
@@ -1541,19 +1541,19 @@ IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi
// Shift value in
IData entry = read_elements + start - array_lsb;
if (width <= 8) {
CData* datap = &(reinterpret_cast<CData*>(memp))[entry];
CData* const datap = &(reinterpret_cast<CData*>(memp))[entry];
if (shift == start_shift) *datap = 0;
*datap |= (c << shift) & VL_MASK_I(width);
} else if (width <= 16) {
SData* datap = &(reinterpret_cast<SData*>(memp))[entry];
SData* const datap = &(reinterpret_cast<SData*>(memp))[entry];
if (shift == start_shift) *datap = 0;
*datap |= (c << shift) & VL_MASK_I(width);
} else if (width <= VL_IDATASIZE) {
IData* datap = &(reinterpret_cast<IData*>(memp))[entry];
IData* const datap = &(reinterpret_cast<IData*>(memp))[entry];
if (shift == start_shift) *datap = 0;
*datap |= (c << shift) & VL_MASK_I(width);
} else if (width <= VL_QUADSIZE) {
QData* datap = &(reinterpret_cast<QData*>(memp))[entry];
QData* const datap = &(reinterpret_cast<QData*>(memp))[entry];
if (shift == start_shift) *datap = 0;
*datap |= ((static_cast<QData>(c) << static_cast<QData>(shift)) & VL_MASK_Q(width));
} else {
@@ -1579,7 +1579,7 @@ IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE {
return VL_SYSTEM_IW(VL_WQ_WORDS_E, lhsw);
}
IData VL_SYSTEM_IW(int lhswords, const WDataInP lhsp) VL_MT_SAFE {
char filenamez[VL_TO_STRING_MAX_WORDS * VL_EDATASIZE + 1];
char filenamez[VL_VALUE_STRING_MAX_CHARS + 1];
_vl_vint_to_string(lhswords * VL_EDATASIZE, filenamez, lhsp);
const int code = std::system(filenamez); // Yes, std::system() is threadsafe
return code >> 8; // Want exit status
@@ -1615,7 +1615,7 @@ IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_M
}
const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefix.c_str());
const char* dp = match.c_str() + 1 /*leading + */ + prefix.length();
const char* const dp = match.c_str() + 1 /*leading + */ + prefix.length();
if (match.empty()) return 0;
VL_ZERO_RESET_W(rbits, rwp);
@@ -1684,9 +1684,9 @@ IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_S
}
}
const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefix.c_str());
const char* dp = match.c_str() + 1 /*leading + */ + prefix.length();
const char* const dp = match.c_str() + 1 /*leading + */ + prefix.length();
if (match.empty()) return 0;
rdr = std::string(dp);
rdr = std::string{dp};
return 1;
}
@@ -1726,22 +1726,22 @@ std::string VL_TOUPPER_NN(const std::string& ld) VL_MT_SAFE {
std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_MT_SAFE {
// See also _vl_vint_to_string
char destout[VL_TO_STRING_MAX_WORDS * VL_EDATASIZE + 1];
char destout[VL_VALUE_STRING_MAX_CHARS + 1];
int obits = lwords * VL_EDATASIZE;
int lsb = obits - 1;
bool start = true;
char* destp = destout;
int len = 0;
size_t len = 0;
for (; lsb >= 0; --lsb) {
lsb = (lsb / 8) * 8; // Next digit
IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xff;
if (!start || charval) {
*destp++ = (charval == 0) ? ' ' : charval;
len++;
++len;
start = false; // Drop leading 0s
}
}
return std::string(destout, len);
return std::string{destout, len};
}
std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE {
@@ -1924,19 +1924,19 @@ void VlReadMem::setData(void* valuep, const std::string& rhs) {
const int value
= (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0'));
if (m_bits <= 8) {
CData* datap = reinterpret_cast<CData*>(valuep);
CData* const datap = reinterpret_cast<CData*>(valuep);
if (!innum) *datap = 0;
*datap = ((*datap << shift) + value) & VL_MASK_I(m_bits);
} else if (m_bits <= 16) {
SData* datap = reinterpret_cast<SData*>(valuep);
SData* const datap = reinterpret_cast<SData*>(valuep);
if (!innum) *datap = 0;
*datap = ((*datap << shift) + value) & VL_MASK_I(m_bits);
} else if (m_bits <= VL_IDATASIZE) {
IData* datap = reinterpret_cast<IData*>(valuep);
IData* const datap = reinterpret_cast<IData*>(valuep);
if (!innum) *datap = 0;
*datap = ((*datap << shift) + value) & VL_MASK_I(m_bits);
} else if (m_bits <= VL_QUADSIZE) {
QData* datap = reinterpret_cast<QData*>(valuep);
QData* const datap = reinterpret_cast<QData*>(valuep);
if (!innum) *datap = 0;
*datap = ((*datap << static_cast<QData>(shift)) + static_cast<QData>(value))
& VL_MASK_Q(m_bits);
@@ -1979,7 +1979,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
}
m_addr = addr + 1;
if (m_bits <= 8) {
const CData* datap = reinterpret_cast<const CData*>(valuep);
const CData* const datap = reinterpret_cast<const CData*>(valuep);
if (m_hex) {
fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap);
fprintf(m_fp, "\n");
@@ -1987,7 +1987,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap));
}
} else if (m_bits <= 16) {
const SData* datap = reinterpret_cast<const SData*>(valuep);
const SData* const datap = reinterpret_cast<const SData*>(valuep);
if (m_hex) {
fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap);
fprintf(m_fp, "\n");
@@ -1995,7 +1995,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap));
}
} else if (m_bits <= 32) {
const IData* datap = reinterpret_cast<const IData*>(valuep);
const IData* const datap = reinterpret_cast<const IData*>(valuep);
if (m_hex) {
fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap);
fprintf(m_fp, "\n");
@@ -2003,7 +2003,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap));
}
} else if (m_bits <= 64) {
const QData* datap = reinterpret_cast<const QData*>(valuep);
const QData* const datap = reinterpret_cast<const QData*>(valuep);
const vluint64_t value = VL_MASK_Q(m_bits) & *datap;
const vluint32_t lo = value & 0xffffffff;
const vluint32_t hi = value >> 32;
@@ -2038,7 +2038,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
fprintf(m_fp, "%s", formatBinary(32, data));
}
}
word_idx--;
--word_idx;
first = false;
}
fprintf(m_fp, "\n");
@@ -2057,7 +2057,7 @@ void VL_READMEM_N(bool hex, // Hex format, else binary
) VL_MT_SAFE {
if (start < static_cast<QData>(array_lsb)) start = array_lsb;
VlReadMem rmem(hex, bits, filename, start, end);
VlReadMem rmem{hex, bits, filename, start, end};
if (VL_UNLIKELY(!rmem.isOpen())) return;
while (true) {
QData addr = 0;
@@ -2070,16 +2070,16 @@ void VL_READMEM_N(bool hex, // Hex format, else binary
} else {
QData entry = addr - array_lsb;
if (bits <= 8) {
CData* datap = &(reinterpret_cast<CData*>(memp))[entry];
CData* const datap = &(reinterpret_cast<CData*>(memp))[entry];
rmem.setData(datap, value);
} else if (bits <= 16) {
SData* datap = &(reinterpret_cast<SData*>(memp))[entry];
SData* const datap = &(reinterpret_cast<SData*>(memp))[entry];
rmem.setData(datap, value);
} else if (bits <= VL_IDATASIZE) {
IData* datap = &(reinterpret_cast<IData*>(memp))[entry];
IData* const datap = &(reinterpret_cast<IData*>(memp))[entry];
rmem.setData(datap, value);
} else if (bits <= VL_QUADSIZE) {
QData* datap = &(reinterpret_cast<QData*>(memp))[entry];
QData* const datap = &(reinterpret_cast<QData*>(memp))[entry];
rmem.setData(datap, value);
} else {
WDataOutP datap
@@ -2107,22 +2107,22 @@ void VL_WRITEMEM_N(bool hex, // Hex format, else binary
if (start < static_cast<QData>(array_lsb)) start = array_lsb;
if (end > addr_max) end = addr_max;
VlWriteMem wmem(hex, bits, filename, start, end);
VlWriteMem wmem{hex, bits, filename, start, end};
if (VL_UNLIKELY(!wmem.isOpen())) return;
for (QData addr = start; addr <= end; ++addr) {
const QData row_offset = addr - array_lsb;
if (bits <= 8) {
const CData* datap = &(reinterpret_cast<const CData*>(memp))[row_offset];
const CData* const datap = &(reinterpret_cast<const CData*>(memp))[row_offset];
wmem.print(addr, false, datap);
} else if (bits <= 16) {
const SData* datap = &(reinterpret_cast<const SData*>(memp))[row_offset];
const SData* const datap = &(reinterpret_cast<const SData*>(memp))[row_offset];
wmem.print(addr, false, datap);
} else if (bits <= 32) {
const IData* datap = &(reinterpret_cast<const IData*>(memp))[row_offset];
const IData* const datap = &(reinterpret_cast<const IData*>(memp))[row_offset];
wmem.print(addr, false, datap);
} else if (bits <= 64) {
const QData* datap = &(reinterpret_cast<const QData*>(memp))[row_offset];
const QData* const datap = &(reinterpret_cast<const QData*>(memp))[row_offset];
wmem.print(addr, false, datap);
} else {
const WDataInP memDatap = reinterpret_cast<WDataInP>(memp);
@@ -2142,8 +2142,8 @@ int VL_TIME_STR_CONVERT(const char* strp) VL_PURE {
if (!strp) return 0;
if (*strp++ != '1') return 0;
while (*strp == '0') {
scale++;
strp++;
++scale;
++strp;
}
switch (*strp++) {
case 's': break;
@@ -2258,8 +2258,9 @@ VerilatedContext::VerilatedContext()
Verilated::lastContextp(this);
Verilated::threadContextp(this);
m_ns.m_profThreadsFilename = "profile_threads.dat";
m_ns.m_profVltFilename = "profile.vlt";
m_fdps.resize(31);
std::fill(m_fdps.begin(), m_fdps.end(), (FILE*)0);
std::fill(m_fdps.begin(), m_fdps.end(), static_cast<FILE*>(nullptr));
m_fdFreeMct.resize(30);
for (std::size_t i = 0, id = 1; i < m_fdFreeMct.size(); ++i, ++id) m_fdFreeMct[i] = id;
}
@@ -2273,19 +2274,19 @@ VerilatedContext::Serialized::Serialized() {
}
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_assertOn = flag;
}
void VerilatedContext::calcUnusedSigs(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_calcUnusedSigs = flag;
}
void VerilatedContext::dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
const VerilatedLockGuard lock(m_timeDumpMutex);
const VerilatedLockGuard lock{m_timeDumpMutex};
m_dumpfile = flag;
}
std::string VerilatedContext::dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
const VerilatedLockGuard lock(m_timeDumpMutex);
const VerilatedLockGuard lock{m_timeDumpMutex};
return m_dumpfile;
}
std::string VerilatedContext::dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
@@ -2297,61 +2298,69 @@ std::string VerilatedContext::dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDu
return out;
}
void VerilatedContext::errorCount(int val) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_errorCount = val;
}
void VerilatedContext::errorCountInc() VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
++m_s.m_errorCount;
}
void VerilatedContext::errorLimit(int val) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_errorLimit = val;
}
void VerilatedContext::fatalOnError(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_fatalOnError = flag;
}
void VerilatedContext::fatalOnVpiError(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_fatalOnVpiError = flag;
}
void VerilatedContext::gotError(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_gotError = flag;
}
void VerilatedContext::gotFinish(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_gotFinish = flag;
}
void VerilatedContext::profThreadsStart(vluint64_t flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_ns.m_profThreadsStart = flag;
}
void VerilatedContext::profThreadsWindow(vluint64_t flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_ns.m_profThreadsWindow = flag;
}
void VerilatedContext::profThreadsFilename(const std::string& flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_ns.m_profThreadsFilename = flag;
}
std::string VerilatedContext::profThreadsFilename() const VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
return m_ns.m_profThreadsFilename;
}
void VerilatedContext::profVltFilename(const std::string& flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_ns.m_profVltFilename = flag;
}
std::string VerilatedContext::profVltFilename() const VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
return m_ns.m_profVltFilename;
}
void VerilatedContext::randReset(int val) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_randReset = val;
}
void VerilatedContext::timeunit(int value) VL_MT_SAFE {
if (value < 0) value = -value; // Stored as 0..15
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_timeunit = value;
}
void VerilatedContext::timeprecision(int value) VL_MT_SAFE {
if (value < 0) value = -value; // Stored as 0..15
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_timeprecision = value;
#ifdef SYSTEMC_VERSION
const sc_time sc_res = sc_get_time_resolution();
@@ -2387,13 +2396,13 @@ const char* VerilatedContext::timeprecisionString() const VL_MT_SAFE {
}
void VerilatedContext::commandArgs(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex) {
const VerilatedLockGuard lock(m_argMutex);
const VerilatedLockGuard lock{m_argMutex};
m_args.m_argVec.clear(); // Empty first, then add
impp()->commandArgsAddGuts(argc, argv);
}
void VerilatedContext::commandArgsAdd(int argc, const char** argv)
VL_MT_SAFE_EXCLUDES(m_argMutex) {
const VerilatedLockGuard lock(m_argMutex);
const VerilatedLockGuard lock{m_argMutex};
impp()->commandArgsAddGuts(argc, argv);
}
const char* VerilatedContext::commandArgsPlusMatch(const char* prefixp)
@@ -2422,20 +2431,20 @@ void VerilatedContext::internalsDump() const VL_MT_SAFE {
void VerilatedContextImp::commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(m_argMutex) {
if (!m_args.m_argVecLoaded) m_args.m_argVec.clear();
for (int i = 0; i < argc; ++i) {
m_args.m_argVec.push_back(argv[i]);
m_args.m_argVec.emplace_back(argv[i]);
commandArgVl(argv[i]);
}
m_args.m_argVecLoaded = true; // Can't just test later for empty vector, no arguments is ok
}
void VerilatedContextImp::commandArgDump() const VL_MT_SAFE_EXCLUDES(m_argMutex) {
const VerilatedLockGuard lock(m_argMutex);
const VerilatedLockGuard lock{m_argMutex};
VL_PRINTF_MT(" Argv:");
for (const auto& i : m_args.m_argVec) VL_PRINTF_MT(" %s", i.c_str());
VL_PRINTF_MT("\n");
}
std::string VerilatedContextImp::argPlusMatch(const char* prefixp)
VL_MT_SAFE_EXCLUDES(m_argMutex) {
const VerilatedLockGuard lock(m_argMutex);
const VerilatedLockGuard lock{m_argMutex};
// Note prefixp does not include the leading "+"
const size_t len = std::strlen(prefixp);
if (VL_UNLIKELY(!m_args.m_argVecLoaded)) {
@@ -2454,7 +2463,7 @@ std::string VerilatedContextImp::argPlusMatch(const char* prefixp)
// Return string representing current argv
// Only used by VPI so uses static storage, only supports most recent called context
std::pair<int, char**> VerilatedContextImp::argc_argv() VL_MT_SAFE_EXCLUDES(m_argMutex) {
const VerilatedLockGuard lock(m_argMutex);
const VerilatedLockGuard lock{m_argMutex};
static bool s_loaded = false;
static int s_argc = 0;
static char** s_argvp = nullptr;
@@ -2495,6 +2504,8 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
profThreadsWindow(std::atol(value.c_str()));
} else if (commandArgVlValue(arg, "+verilator+prof+threads+file+", value /*ref*/)) {
profThreadsFilename(value);
} else if (commandArgVlValue(arg, "+verilator+prof+vlt+file+", value /*ref*/)) {
profVltFilename(value);
} else if (commandArgVlValue(arg, "+verilator+rand+reset+", value /*ref*/)) {
randReset(std::atoi(value.c_str()));
} else if (commandArgVlValue(arg, "+verilator+seed+", value /*ref*/)) {
@@ -2529,7 +2540,7 @@ bool VerilatedContextImp::commandArgVlValue(const std::string& arg, const std::s
void VerilatedContext::randSeed(int val) VL_MT_SAFE {
// As we have per-thread state, the epoch must be static,
// and so the rand seed's mutex must also be static
const VerilatedLockGuard lock(VerilatedContextImp::s().s_randMutex);
const VerilatedLockGuard lock{VerilatedContextImp::s().s_randMutex};
m_s.m_randSeed = val;
const vluint64_t newEpoch = VerilatedContextImp::s().s_randSeedEpoch + 1;
// Obververs must see new epoch AFTER seed updated
@@ -2552,10 +2563,10 @@ vluint64_t VerilatedContextImp::randSeedDefault64() const VL_MT_SAFE {
// VerilatedContext:: Methods - scopes
void VerilatedContext::scopesDump() const VL_MT_SAFE {
const VerilatedLockGuard lock(m_impdatap->m_nameMutex);
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
VL_PRINTF_MT(" scopesDump:\n");
for (const auto& i : m_impdatap->m_nameMap) {
const VerilatedScope* scopep = i.second;
const VerilatedScope* const scopep = i.second;
scopep->scopeDump();
}
VL_PRINTF_MT("\n");
@@ -2563,20 +2574,20 @@ void VerilatedContext::scopesDump() const VL_MT_SAFE {
void VerilatedContextImp::scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE {
// Slow ok - called once/scope at construction
const VerilatedLockGuard lock(m_impdatap->m_nameMutex);
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
const auto it = m_impdatap->m_nameMap.find(scopep->name());
if (it == m_impdatap->m_nameMap.end()) m_impdatap->m_nameMap.emplace(scopep->name(), scopep);
}
void VerilatedContextImp::scopeErase(const VerilatedScope* scopep) VL_MT_SAFE {
// Slow ok - called once/scope at destruction
const VerilatedLockGuard lock(m_impdatap->m_nameMutex);
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
VerilatedImp::userEraseScope(scopep);
const auto it = m_impdatap->m_nameMap.find(scopep->name());
if (it != m_impdatap->m_nameMap.end()) m_impdatap->m_nameMap.erase(it);
}
const VerilatedScope* VerilatedContext::scopeFind(const char* namep) const VL_MT_SAFE {
// Thread save only assuming this is called only after model construction completed
const VerilatedLockGuard lock(m_impdatap->m_nameMutex);
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
// If too slow, can assume this is only VL_MT_SAFE_POSINIT
const auto& it = m_impdatap->m_nameMap.find(namep);
if (VL_UNLIKELY(it == m_impdatap->m_nameMap.end())) return nullptr;
@@ -2670,11 +2681,11 @@ static void runCallbacks(const VoidPCbList& cbs) VL_MT_SAFE {
}
void Verilated::addFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE {
const VerilatedLockGuard lock(VlCbStatic.s_flushMutex);
const VerilatedLockGuard lock{VlCbStatic.s_flushMutex};
addCb(cb, datap, VlCbStatic.s_flushCbs);
}
void Verilated::removeFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE {
const VerilatedLockGuard lock(VlCbStatic.s_flushMutex);
const VerilatedLockGuard lock{VlCbStatic.s_flushMutex};
removeCb(cb, datap, VlCbStatic.s_flushCbs);
}
void Verilated::runFlushCallbacks() VL_MT_SAFE {
@@ -2685,7 +2696,7 @@ void Verilated::runFlushCallbacks() VL_MT_SAFE {
static int s_recursing = 0;
#endif
if (!s_recursing++) {
const VerilatedLockGuard lock(VlCbStatic.s_flushMutex);
const VerilatedLockGuard lock{VlCbStatic.s_flushMutex};
runCallbacks(VlCbStatic.s_flushCbs);
}
--s_recursing;
@@ -2698,11 +2709,11 @@ void Verilated::runFlushCallbacks() VL_MT_SAFE {
}
void Verilated::addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE {
const VerilatedLockGuard lock(VlCbStatic.s_exitMutex);
const VerilatedLockGuard lock{VlCbStatic.s_exitMutex};
addCb(cb, datap, VlCbStatic.s_exitCbs);
}
void Verilated::removeExitCb(VoidPCb cb, void* datap) VL_MT_SAFE {
const VerilatedLockGuard lock(VlCbStatic.s_exitMutex);
const VerilatedLockGuard lock{VlCbStatic.s_exitMutex};
removeCb(cb, datap, VlCbStatic.s_exitCbs);
}
void Verilated::runExitCallbacks() VL_MT_SAFE {
@@ -2712,7 +2723,7 @@ void Verilated::runExitCallbacks() VL_MT_SAFE {
static int s_recursing = 0;
#endif
if (!s_recursing++) {
const VerilatedLockGuard lock(VlCbStatic.s_exitMutex);
const VerilatedLockGuard lock{VlCbStatic.s_exitMutex};
runCallbacks(VlCbStatic.s_exitCbs);
}
--s_recursing;
@@ -2729,7 +2740,7 @@ void Verilated::nullPointerError(const char* filename, int linenum) VL_MT_SAFE {
void Verilated::overWidthError(const char* signame) VL_MT_SAFE {
// Slowpath - Called only when signal sets too high of a bit
const std::string msg = (std::string("Testbench C set input '") + signame
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
@@ -2846,7 +2857,7 @@ void VerilatedScope::configure(VerilatedSyms* symsp, const char* prefixp, const
m_type = type;
m_timeunit = timeunit;
{
char* namep = new char[std::strlen(prefixp) + std::strlen(suffixp) + 2];
char* const namep = new char[std::strlen(prefixp) + std::strlen(suffixp) + 2];
char* dp = namep;
for (const char* sp = prefixp; *sp;) *dp++ = *sp++;
if (*prefixp && *suffixp) *dp++ = '.';
@@ -2886,7 +2897,7 @@ void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, boo
// statically construct from that.
if (!finalize) return;
if (!m_varsp) m_varsp = new VerilatedVarNameMap();
if (!m_varsp) m_varsp = new VerilatedVarNameMap;
VerilatedVar var(namep, datap, vltype, static_cast<VerilatedVarFlags>(vlflags), dims, isParam);
va_list ap;
@@ -2903,9 +2914,9 @@ void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, boo
} else {
// We could have a linked list of ranges, but really this whole thing needs
// to be generalized to support structs and unions, etc.
VL_FATAL_MT(
__FILE__, __LINE__, "",
(std::string("Unsupported multi-dimensional public varInsert: ") + namep).c_str());
std::string msg
= std::string{"Unsupported multi-dimensional public varInsert: "} + namep;
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
}
}
va_end(ap);
@@ -2925,7 +2936,7 @@ VerilatedVar* VerilatedScope::varFind(const char* namep) const VL_MT_SAFE_POSTIN
void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE {
// Slowpath - Called only when find has failed
const std::string msg
= (std::string("Testbench C called '") + VerilatedImp::exportName(funcnum)
= (std::string{"Testbench C called '"} + VerilatedImp::exportName(funcnum)
+ "' but scope wasn't set, perhaps due to dpi import call without "
+ "'context', or missing svSetScope. See IEEE 1800-2017 35.5.3.");
VL_FATAL_MT("unknown", 0, "", msg.c_str());
@@ -2935,7 +2946,7 @@ void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE {
void* VerilatedScope::exportFindError(int funcnum) const {
// Slowpath - Called only when find has failed
const std::string msg
= (std::string("Testbench C called '") + VerilatedImp::exportName(funcnum)
= (std::string{"Testbench C called '"} + VerilatedImp::exportName(funcnum)
+ "' but this DPI export function exists only in other scopes, not scope '" + name()
+ "'");
VL_FATAL_MT("unknown", 0, "", msg.c_str());
@@ -2950,7 +2961,7 @@ void VerilatedScope::scopeDump() const {
VerilatedImp::exportName(i));
}
}
if (VerilatedVarNameMap* varsp = this->varsp()) {
if (VerilatedVarNameMap* const varsp = this->varsp()) {
for (const auto& i : *varsp) VL_PRINTF_MT(" VAR %p: %s\n", &(i.second), i.first);
}
}
+16 -2138
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -23,8 +23,6 @@ CFG_WITH_LONGTESTS = @CFG_WITH_LONGTESTS@
CFG_CXXFLAGS_PROFILE = @CFG_CXXFLAGS_PROFILE@
# Select newest language
CFG_CXXFLAGS_STD_NEWEST = @CFG_CXXFLAGS_STD_NEWEST@
# Select oldest language (for Verilator internal testing only)
CFG_CXXFLAGS_STD_OLDEST = @CFG_CXXFLAGS_STD_OLDEST@
# Compiler flags to use to turn off unused and generated code warnings, such as -Wno-div-by-zero
CFG_CXXFLAGS_NO_UNUSED = @CFG_CXXFLAGS_NO_UNUSED@
# Compiler flags that turn on extra warnings
+32 -29
View File
@@ -132,7 +132,7 @@ private:
int valueIndex(const std::string& value) VL_REQUIRES(m_mutex) {
const auto iter = m_valueIndexes.find(value);
if (iter != m_valueIndexes.end()) return iter->second;
m_nextIndex++;
++m_nextIndex;
assert(m_nextIndex > 0); // Didn't rollover
m_valueIndexes.emplace(value, m_nextIndex);
m_indexValues.emplace(m_nextIndex, value);
@@ -143,8 +143,9 @@ private:
std::string rtn;
for (const char* pos = text.c_str(); *pos; ++pos) {
if (!std::isprint(*pos) || *pos == '%' || *pos == '"') {
char hex[10];
VL_SNPRINTF(hex, 10, "%%%02X", pos[0]);
constexpr size_t LEN_MAX_HEX = 20;
char hex[LEN_MAX_HEX];
VL_SNPRINTF(hex, LEN_MAX_HEX, "%%%02X", pos[0]);
rtn += hex;
} else {
rtn += *pos;
@@ -165,11 +166,11 @@ private:
const std::string& value) VL_PURE {
std::string name;
if (key.length() == 1 && std::isalpha(key[0])) {
name += std::string("\001") + key;
name += std::string{"\001"} + key;
} else {
name += std::string("\001") + dequote(key);
name += std::string{"\001"} + dequote(key);
}
name += std::string("\002") + dequote(value);
name += std::string{"\002"} + dequote(value);
return name;
}
static std::string combineHier(const std::string& old, const std::string& add) VL_PURE {
@@ -180,31 +181,32 @@ private:
if (old.empty()) return add;
if (add.empty()) return old;
const char* a = old.c_str();
const char* b = add.c_str();
const char* const a = old.c_str();
const char* const b = add.c_str();
// Scan forward to first mismatch
const char* apre = a;
const char* bpre = b;
while (*apre == *bpre) {
apre++;
bpre++;
++apre;
++bpre;
}
// We used to backup and split on only .'s but it seems better to be verbose
// and not assume . is the separator
const std::string prefix = std::string(a, apre - a);
const size_t prefix_len = apre - a;
const std::string prefix = std::string{a, prefix_len};
// Scan backward to last mismatch
const char* apost = a + std::strlen(a) - 1;
const char* bpost = b + std::strlen(b) - 1;
while (*apost == *bpost && apost > apre && bpost > bpre) {
apost--;
bpost--;
--apost;
--bpost;
}
// Forward to . so we have a whole word
std::string suffix = *bpost ? std::string(bpost + 1) : "";
std::string suffix = *bpost ? std::string{bpost + 1} : "";
std::string out = prefix + "*" + suffix;
@@ -253,17 +255,17 @@ public:
// PUBLIC METHODS
void forcePerInstance(const bool flag) VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce();
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_forcePerInstance = flag;
}
void clear() VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce();
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
clearGuts();
}
void clearNonMatch(const char* const matchp) VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce();
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
if (matchp && matchp[0]) {
ItemList newlist;
for (const auto& itemp : m_items) {
@@ -278,24 +280,24 @@ public:
}
void zero() VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce();
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
for (const auto& itemp : m_items) itemp->zero();
}
// We assume there's always call to i/f/p in that order
void inserti(VerilatedCovImpItem* itemp) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
assert(!m_insertp);
m_insertp = itemp;
}
void insertf(const char* const filenamep, const int lineno) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_insertFilenamep = filenamep;
m_insertLineno = lineno;
}
void insertp(const char* ckeyps[VerilatedCovConst::MAX_KEYS],
const char* valps[VerilatedCovConst::MAX_KEYS]) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
assert(m_insertp);
// First two key/vals are filename
ckeyps[0] = "filename";
@@ -308,7 +310,8 @@ public:
while (const char* foundp = std::strchr(fnstartp, '/')) fnstartp = foundp + 1;
const char* fnendp = fnstartp;
for (; *fnendp && *fnendp != '.'; fnendp++) {}
std::string page_default = "sp_user/" + std::string(fnstartp, fnendp - fnstartp);
const size_t page_len = fnendp - fnstartp;
const std::string page_default = "sp_user/" + std::string{fnstartp, page_len};
ckeyps[2] = "page";
valps[2] = page_default.c_str();
@@ -337,7 +340,7 @@ public:
// cout<<" "<<__FUNCTION__<<" "<<key<<" = "<<val<<endl;
m_insertp->m_keys[addKeynum] = valueIndex(key);
m_insertp->m_vals[addKeynum] = valueIndex(val);
addKeynum++;
++addKeynum;
if (VL_UNCOVERABLE(!legalKey(key))) {
std::string msg
= ("%Error: Coverage keys of one character, or letter+digit are illegal: "
@@ -353,15 +356,15 @@ public:
void write(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce();
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
#ifndef VM_COVERAGE
VL_FATAL_MT("", 0, "", "%Error: Called VerilatedCov::write when VM_COVERAGE disabled\n");
#endif
selftest();
std::ofstream os(filename);
std::ofstream os{filename};
if (os.fail()) {
std::string msg = std::string("%Error: Can't write '") + filename + "'";
const std::string msg = std::string{"%Error: Can't write '"} + filename + "'";
VL_FATAL_MT("", 0, "", msg.c_str());
return;
}
@@ -435,10 +438,10 @@ void VerilatedCovContext::clearNonMatch(const char* matchp) VL_MT_SAFE {
void VerilatedCovContext::zero() VL_MT_SAFE { impp()->zero(); }
void VerilatedCovContext::write(const char* filenamep) VL_MT_SAFE { impp()->write(filenamep); }
void VerilatedCovContext::_inserti(vluint32_t* itemp) VL_MT_SAFE {
impp()->inserti(new VerilatedCoverItemSpec<vluint32_t>(itemp));
impp()->inserti(new VerilatedCoverItemSpec<vluint32_t>{itemp});
}
void VerilatedCovContext::_inserti(vluint64_t* itemp) VL_MT_SAFE {
impp()->inserti(new VerilatedCoverItemSpec<vluint64_t>(itemp));
impp()->inserti(new VerilatedCoverItemSpec<vluint64_t>{itemp});
}
void VerilatedCovContext::_insertf(const char* filename, int lineno) VL_MT_SAFE {
impp()->insertf(filename, lineno);
@@ -511,7 +514,7 @@ VerilatedCovContext* VerilatedCov::threadCovp() VL_MT_SAFE {
VerilatedCovContext* VerilatedContext::coveragep() VL_MT_SAFE {
static VerilatedMutex s_mutex;
if (VL_UNLIKELY(!m_coveragep)) {
const VerilatedLockGuard lock(s_mutex);
const VerilatedLockGuard lock{s_mutex};
if (VL_LIKELY(!m_coveragep)) { // Not redundant, prevents race
m_coveragep.reset(new VerilatedCovImp);
}
+30 -30
View File
@@ -184,7 +184,7 @@ static inline const VerilatedDpiOpenVar* _vl_openhandle_varp(const svOpenArrayHa
VL_FATAL_MT(__FILE__, __LINE__, "",
"%%Error: DPI svOpenArrayHandle function called with nullptr handle");
}
const VerilatedDpiOpenVar* varp = reinterpret_cast<const VerilatedDpiOpenVar*>(h);
const VerilatedDpiOpenVar* const varp = reinterpret_cast<const VerilatedDpiOpenVar*>(h);
if (VL_UNLIKELY(!varp->magicOk())) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"%%Error: DPI svOpenArrayHandle function called with non-Verilator handle");
@@ -205,13 +205,13 @@ int svDimensions(const svOpenArrayHandle h) { return _vl_openhandle_varp(h)->udi
// Return pointer to open array data, or nullptr if not in IEEE standard C layout
void* svGetArrayPtr(const svOpenArrayHandle h) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h);
if (VL_UNLIKELY(!varp->isDpiStdLayout())) return nullptr;
return varp->datap();
}
// Return size of open array, or 0 if not in IEEE standard C layout
int svSizeOfArray(const svOpenArrayHandle h) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h);
if (VL_UNLIKELY(!varp->isDpiStdLayout())) return 0;
// Truncate 64 bits to int; DPI is limited to 4GB
return static_cast<int>(varp->totalSize());
@@ -264,15 +264,15 @@ static void* _vl_svGetArrElemPtr(const svOpenArrayHandle h, int nargs, int indx1
int indx3) VL_MT_SAFE {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
if (VL_UNLIKELY(!varp->isDpiStdLayout())) return nullptr;
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
return datap;
}
// Copy to user bit array from simulator open array
static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int nargs,
int indx1, int indx2, int indx3) VL_MT_SAFE {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
if (VL_UNLIKELY(!datap)) return;
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
case VLVT_UINT8: d[0] = *(reinterpret_cast<CData*>(datap)); return;
@@ -299,8 +299,8 @@ static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s,
// Copy to user logic array from simulator open array
static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int nargs,
int indx1, int indx2, int indx3) VL_MT_SAFE {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
if (VL_UNLIKELY(!datap)) return;
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
case VLVT_UINT8:
@@ -342,8 +342,8 @@ static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandl
// Copy to simulator open array from from user bit array
static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int nargs,
int indx1, int indx2, int indx3) VL_MT_SAFE {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
if (VL_UNLIKELY(!datap)) return;
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
case VLVT_UINT8: *(reinterpret_cast<CData*>(datap)) = s[0]; return;
@@ -364,8 +364,8 @@ static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecV
// Copy to simulator open array from from user logic array
static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s,
int nargs, int indx1, int indx2, int indx3) VL_MT_SAFE {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
if (VL_UNLIKELY(!datap)) return;
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
case VLVT_UINT8: *(reinterpret_cast<CData*>(datap)) = s[0].aval; return;
@@ -386,10 +386,10 @@ static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogic
// Return bit from simulator open array
static svBit _vl_svGetBitArrElem(const svOpenArrayHandle s, int nargs, int indx1, int indx2,
int indx3, int indx4) VL_MT_SAFE {
int indx3, int) VL_MT_SAFE {
// One extra index supported, as need bit number
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
if (VL_UNLIKELY(!datap)) return 0;
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
case VLVT_UINT8: return (*(reinterpret_cast<CData*>(datap))) & 1;
@@ -401,11 +401,11 @@ static svBit _vl_svGetBitArrElem(const svOpenArrayHandle s, int nargs, int indx1
}
// Update simulator open array from bit
static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int nargs, int indx1,
int indx2, int indx3, int indx4) VL_MT_SAFE {
int indx2, int indx3, int) VL_MT_SAFE {
// One extra index supported, as need bit number
value &= 1; // Make sure clean
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
if (VL_UNLIKELY(!datap)) return;
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
case VLVT_UINT8: *(reinterpret_cast<CData*>(datap)) = value; return;
@@ -420,7 +420,7 @@ static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int narg
// DPI accessors that simply call above functions
void* svGetArrElemPtr(const svOpenArrayHandle h, int indx1, ...) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h);
void* datap;
va_list ap;
va_start(ap, indx1);
@@ -454,7 +454,7 @@ void* svGetArrElemPtr3(const svOpenArrayHandle h, int indx1, int indx2, int indx
}
void svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int indx1, ...) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
va_list ap;
va_start(ap, indx1);
switch (varp->udims()) {
@@ -486,7 +486,7 @@ void svPutBitArrElem3VecVal(const svOpenArrayHandle d, const svBitVecVal* s, int
_vl_svPutBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
}
void svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s, int indx1, ...) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
va_list ap;
va_start(ap, indx1);
switch (varp->udims()) {
@@ -522,7 +522,7 @@ void svPutLogicArrElem3VecVal(const svOpenArrayHandle d, const svLogicVecVal* s,
// From simulator storage into user space
void svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1, ...) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
va_list ap;
va_start(ap, indx1);
switch (varp->udims()) {
@@ -553,7 +553,7 @@ void svGetBitArrElem3VecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1
_vl_svGetBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
}
void svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int indx1, ...) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
va_list ap;
va_start(ap, indx1);
switch (varp->udims()) {
@@ -585,7 +585,7 @@ void svGetLogicArrElem3VecVal(svLogicVecVal* d, const svOpenArrayHandle s, int i
}
svBit svGetBitArrElem(const svOpenArrayHandle s, int indx1, ...) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
svBit out;
va_list ap;
va_start(ap, indx1);
@@ -618,7 +618,7 @@ svBit svGetBitArrElem3(const svOpenArrayHandle s, int indx1, int indx2, int indx
}
svLogic svGetLogicArrElem(const svOpenArrayHandle s, int indx1, ...) {
// Verilator doesn't support X/Z so can just call Bit version
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
svBit out;
va_list ap;
va_start(ap, indx1);
@@ -654,7 +654,7 @@ svLogic svGetLogicArrElem3(const svOpenArrayHandle s, int indx1, int indx2, int
}
void svPutBitArrElem(const svOpenArrayHandle d, svBit value, int indx1, ...) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
va_list ap;
va_start(ap, indx1);
switch (varp->udims()) {
@@ -685,7 +685,7 @@ void svPutBitArrElem3(const svOpenArrayHandle d, svBit value, int indx1, int ind
}
void svPutLogicArrElem(const svOpenArrayHandle d, svLogic value, int indx1, ...) {
// Verilator doesn't support X/Z so can just call Bit version
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
va_list ap;
va_start(ap, indx1);
switch (varp->udims()) {
@@ -732,15 +732,15 @@ svScope svGetScope() {
}
svScope svSetScope(const svScope scope) {
const VerilatedScope* prevScopep = Verilated::dpiScope();
const VerilatedScope* vscopep = reinterpret_cast<const VerilatedScope*>(scope);
const VerilatedScope* const prevScopep = Verilated::dpiScope();
const VerilatedScope* const vscopep = reinterpret_cast<const VerilatedScope*>(scope);
Verilated::dpiScope(vscopep);
// NOLINTNEXTLINE(google-readability-casting)
return (svScope)(prevScopep);
}
const char* svGetNameFromScope(const svScope scope) {
const VerilatedScope* vscopep = reinterpret_cast<const VerilatedScope*>(scope);
const VerilatedScope* const vscopep = reinterpret_cast<const VerilatedScope*>(scope);
return vscopep->name();
}
-1
View File
@@ -28,7 +28,6 @@
#include "verilatedos.h"
#include "verilated.h" // Also presumably included by caller
#include "verilated_heavy.h" // Also presumably included by caller
#include "verilated_sym_props.h"
#include "svdpi.h"
+22 -20
View File
@@ -53,29 +53,31 @@
//=============================================================================
// Check that vltscope_t matches fstScopeType
static_assert((int)FST_ST_VCD_MODULE == (int)VLT_TRACE_SCOPE_MODULE,
static_assert(static_cast<int>(FST_ST_VCD_MODULE) == static_cast<int>(VLT_TRACE_SCOPE_MODULE),
"VLT_TRACE_SCOPE_MODULE mismatches");
static_assert((int)FST_ST_VCD_TASK == (int)VLT_TRACE_SCOPE_TASK,
static_assert(static_cast<int>(FST_ST_VCD_TASK) == static_cast<int>(VLT_TRACE_SCOPE_TASK),
"VLT_TRACE_SCOPE_TASK mismatches");
static_assert((int)FST_ST_VCD_FUNCTION == (int)VLT_TRACE_SCOPE_FUNCTION,
static_assert(static_cast<int>(FST_ST_VCD_FUNCTION) == static_cast<int>(VLT_TRACE_SCOPE_FUNCTION),
"VLT_TRACE_SCOPE_FUNCTION mismatches");
static_assert((int)FST_ST_VCD_BEGIN == (int)VLT_TRACE_SCOPE_BEGIN,
static_assert(static_cast<int>(FST_ST_VCD_BEGIN) == static_cast<int>(VLT_TRACE_SCOPE_BEGIN),
"VLT_TRACE_SCOPE_BEGIN mismatches");
static_assert((int)FST_ST_VCD_FORK == (int)VLT_TRACE_SCOPE_FORK,
static_assert(static_cast<int>(FST_ST_VCD_FORK) == static_cast<int>(VLT_TRACE_SCOPE_FORK),
"VLT_TRACE_SCOPE_FORK mismatches");
static_assert((int)FST_ST_VCD_GENERATE == (int)VLT_TRACE_SCOPE_GENERATE,
static_assert(static_cast<int>(FST_ST_VCD_GENERATE) == static_cast<int>(VLT_TRACE_SCOPE_GENERATE),
"VLT_TRACE_SCOPE_GENERATE mismatches");
static_assert((int)FST_ST_VCD_STRUCT == (int)VLT_TRACE_SCOPE_STRUCT,
static_assert(static_cast<int>(FST_ST_VCD_STRUCT) == static_cast<int>(VLT_TRACE_SCOPE_STRUCT),
"VLT_TRACE_SCOPE_STRUCT mismatches");
static_assert((int)FST_ST_VCD_UNION == (int)VLT_TRACE_SCOPE_UNION,
static_assert(static_cast<int>(FST_ST_VCD_UNION) == static_cast<int>(VLT_TRACE_SCOPE_UNION),
"VLT_TRACE_SCOPE_UNION mismatches");
static_assert((int)FST_ST_VCD_CLASS == (int)VLT_TRACE_SCOPE_CLASS,
static_assert(static_cast<int>(FST_ST_VCD_CLASS) == static_cast<int>(VLT_TRACE_SCOPE_CLASS),
"VLT_TRACE_SCOPE_CLASS mismatches");
static_assert((int)FST_ST_VCD_INTERFACE == (int)VLT_TRACE_SCOPE_INTERFACE,
static_assert(static_cast<int>(FST_ST_VCD_INTERFACE)
== static_cast<int>(VLT_TRACE_SCOPE_INTERFACE),
"VLT_TRACE_SCOPE_INTERFACE mismatches");
static_assert((int)FST_ST_VCD_PACKAGE == (int)VLT_TRACE_SCOPE_PACKAGE,
static_assert(static_cast<int>(FST_ST_VCD_PACKAGE) == static_cast<int>(VLT_TRACE_SCOPE_PACKAGE),
"VLT_TRACE_SCOPE_PACKAGE mismatches");
static_assert((int)FST_ST_VCD_PROGRAM == (int)VLT_TRACE_SCOPE_PROGRAM,
static_assert(static_cast<int>(FST_ST_VCD_PROGRAM) == static_cast<int>(VLT_TRACE_SCOPE_PROGRAM),
"VLT_TRACE_SCOPE_PROGRAM mismatches");
//=============================================================================
@@ -98,7 +100,7 @@ VerilatedFst::~VerilatedFst() {
}
void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_fst = fstWriterCreate(filename, 1);
fstWriterSetPackType(m_fst, FST_WR_PT_LZ4);
fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str()); // lintok-begin-on-ref
@@ -130,14 +132,14 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
}
void VerilatedFst::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
VerilatedTrace<VerilatedFst>::closeBase();
fstWriterClose(m_fst);
m_fst = nullptr;
}
void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
VerilatedTrace<VerilatedFst>::flushBase();
fstWriterFlushContext(m_fst);
}
@@ -161,11 +163,11 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
VerilatedTrace<VerilatedFst>::declCode(code, bits, false);
std::istringstream nameiss(name);
std::istringstream nameiss{name};
std::istream_iterator<std::string> beg(nameiss);
std::istream_iterator<std::string> end;
std::list<std::string> tokens(beg, end); // Split name
std::string symbol_name(tokens.back());
std::string symbol_name{tokens.back()};
tokens.pop_back(); // Remove symbol name from hierarchy
tokens.insert(tokens.begin(), moduleName()); // Add current module to the hierarchy
std::string tmpModName;
@@ -191,8 +193,8 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
tmpModName = *new_it;
tmpModName.pop_back();
// If the scope ends with a non-ascii character, it will be 0x80 + fstScopeType
fstWriterSetScope(m_fst, (fstScopeType)(new_it->back() & 0x7f), tmpModName.c_str(),
nullptr);
fstWriterSetScope(m_fst, static_cast<fstScopeType>(new_it->back() & 0x7f),
tmpModName.c_str(), nullptr);
} else
fstWriterSetScope(m_fst, FST_ST_VCD_SCOPE, new_it->c_str(), nullptr);
@@ -202,7 +204,7 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
std::stringstream name_ss;
name_ss << symbol_name;
if (array) name_ss << "(" << arraynum << ")";
if (array) name_ss << "[" << arraynum << "]";
std::string name_str = name_ss.str();
if (dtypenum > 0) {
File diff suppressed because it is too large Load Diff
+7 -956
View File
@@ -12,969 +12,20 @@
//*************************************************************************
///
/// \file
/// \brief Verilated string and data-type header
/// \brief Verilated old string and data-type header
///
/// This file is included automatically by Verilator at the top of
/// all C++ files it generates. It is used when strings or other
/// heavyweight types are required; these contents are not part of
/// verilated.h to save compile time when such types aren't used.
/// This file is deprecated, and provided for backwards compatibility.
/// Include verilated.h instead.
///
//*************************************************************************
#ifndef VERILATOR_VERILATED_HEAVY_H_
#define VERILATOR_VERILATED_HEAVY_H_
#ifdef VL_NO_LEGACY
#error "Include <verilated.h> instead of <verilated_heavy.h>"
#endif
#include "verilated.h"
#include <algorithm>
#include <array>
#include <deque>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <unordered_set>
//===================================================================
// String formatters (required by below containers)
extern std::string VL_TO_STRING(CData lhs);
extern std::string VL_TO_STRING(SData lhs);
extern std::string VL_TO_STRING(IData lhs);
extern std::string VL_TO_STRING(QData lhs);
inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; }
extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
//===================================================================
// Shuffle RNG
class VlURNG final {
public:
using result_type = size_t;
static constexpr size_t min() { return 0; }
static constexpr size_t max() { return 1ULL << 31; }
size_t operator()() { return VL_MASK_I(31) & VL_RANDOM_I(32); }
};
//===================================================================
// Readmem/Writemem operation classes
class VlReadMem final {
bool m_hex; // Hex format
int m_bits; // Bit width of values
const std::string& m_filename; // Filename
QData m_end; // End address (as specified by user)
FILE* m_fp; // File handle for filename
QData m_addr; // Next address to read
int m_linenum; // Line number last read from file
public:
VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end);
~VlReadMem();
bool isOpen() const { return m_fp != nullptr; }
int linenum() const { return m_linenum; }
bool get(QData& addrr, std::string& valuer);
void setData(void* valuep, const std::string& rhs);
};
class VlWriteMem final {
bool m_hex; // Hex format
int m_bits; // Bit width of values
FILE* m_fp; // File handle for filename
QData m_addr; // Next address to write
public:
VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end);
~VlWriteMem();
bool isOpen() const { return m_fp != nullptr; }
void print(QData addr, bool addrstamp, const void* valuep);
};
//===================================================================
/// Verilog wide packed bit container.
/// Similar to std::array<WData, N>, but lighter weight, only methods needed
/// by Verilator, to help compile time.
///
/// A 'struct' as we want this to be an aggregate type that allows
/// static aggregate initialization. Consider data members private.
///
/// For example a Verilog "bit [94:0]" will become a VlWide<3> because 3*32
/// bits are needed to hold the 95 bits. The MSB (bit 96) must always be
/// zero in memory, but during intermediate operations in the Verilated
/// internals is unpredictable.
template <std::size_t T_Words> struct VlWide final {
// MEMBERS
// This should be the only data member, otherwise generated static initializers need updating
EData m_storage[T_Words]; // Contents of the packed array
// CONSTRUCTORS
// cppcheck-suppress uninitVar
VlWide() = default;
~VlWide() = default;
VlWide(const VlWide&) = default;
VlWide(VlWide&&) = default;
// OPERATOR METHODS
VlWide& operator=(const VlWide&) = default;
VlWide& operator=(VlWide&&) = default;
operator WDataOutP() { return &m_storage[0]; } // This also allows []
operator WDataInP() const { return &m_storage[0]; } // This also allows []
// METHODS
const EData& at(size_t index) const { return m_storage[index]; }
EData& at(size_t index) { return m_storage[index]; }
WData* data() { return &m_storage[0]; }
const WData* data() const { return &m_storage[0]; }
bool operator<(const VlWide<T_Words>& rhs) const {
return VL_LT_W(T_Words, data(), rhs.data());
}
};
// Convert a C array to std::array reference by pointer magic, without copy.
// Data type (second argument) is so the function template can automatically generate.
template <std::size_t T_Words>
VlWide<T_Words>& VL_CVT_W_A(const WDataInP inp, const VlWide<T_Words>&) {
return *((VlWide<T_Words>*)inp);
}
template <std::size_t T_Words> std::string VL_TO_STRING(const VlWide<T_Words>& obj) {
return VL_TO_STRING_W(T_Words, obj.data());
}
//===================================================================
// Verilog queue and dynamic array container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
//
// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound
// For dynamic arrays it is always zero
template <class T_Value, size_t T_MaxSize = 0> class VlQueue final {
private:
// TYPES
using Deque = std::deque<T_Value>;
public:
using const_iterator = typename Deque::const_iterator;
private:
// MEMBERS
Deque m_deque; // State of the assoc array
T_Value m_defaultValue; // Default value
public:
// CONSTRUCTORS
// m_defaultValue isn't defaulted. Caller's constructor must do it.
VlQueue() = default;
~VlQueue() = default;
VlQueue(const VlQueue&) = default;
VlQueue(VlQueue&&) = default;
VlQueue& operator=(const VlQueue&) = default;
VlQueue& operator=(VlQueue&&) = default;
// Standard copy constructor works. Verilog: assoca = assocb
// Also must allow conversion from a different T_MaxSize queue
template <size_t U_MaxSize = 0> VlQueue operator=(const VlQueue<T_Value, U_MaxSize>& rhs) {
m_deque = rhs.privateDeque();
if (VL_UNLIKELY(T_MaxSize && T_MaxSize < m_deque.size())) m_deque.resize(T_MaxSize - 1);
return *this;
}
static VlQueue cons(const T_Value& lhs) {
VlQueue out;
out.push_back(lhs);
return out;
}
static VlQueue cons(const T_Value& lhs, const T_Value& rhs) {
VlQueue out;
out.push_back(rhs);
out.push_back(lhs);
return out;
}
static VlQueue cons(const VlQueue& lhs, const T_Value& rhs) {
VlQueue out = lhs;
out.push_front(rhs);
return out;
}
static VlQueue cons(const T_Value& lhs, const VlQueue& rhs) {
VlQueue out = rhs;
out.push_back(lhs);
return out;
}
static VlQueue cons(const VlQueue& lhs, const VlQueue& rhs) {
VlQueue out = rhs;
for (const auto& i : lhs.m_deque) out.push_back(i);
return out;
}
// METHODS
T_Value& atDefault() { return m_defaultValue; }
const T_Value& atDefault() const { return m_defaultValue; }
const Deque& privateDeque() const { return m_deque; }
// Size. Verilog: function int size(), or int num()
int size() const { return m_deque.size(); }
// Clear array. Verilog: function void delete([input index])
void clear() { m_deque.clear(); }
void erase(vlsint32_t index) {
if (VL_LIKELY(index >= 0 && index < m_deque.size()))
m_deque.erase(m_deque.begin() + index);
}
// Dynamic array new[] becomes a renew()
void renew(size_t size) {
clear();
m_deque.resize(size, atDefault());
}
// Dynamic array new[]() becomes a renew_copy()
void renew_copy(size_t size, const VlQueue<T_Value, T_MaxSize>& rhs) {
if (size == 0) {
clear();
} else {
*this = rhs;
m_deque.resize(size, atDefault());
}
}
// function void q.push_front(value)
void push_front(const T_Value& value) {
m_deque.push_front(value);
if (VL_UNLIKELY(T_MaxSize != 0 && m_deque.size() > T_MaxSize)) m_deque.pop_back();
}
// function void q.push_back(value)
void push_back(const T_Value& value) {
if (VL_LIKELY(T_MaxSize == 0 || m_deque.size() < T_MaxSize)) m_deque.push_back(value);
}
// function value_t q.pop_front();
T_Value pop_front() {
if (m_deque.empty()) return m_defaultValue;
T_Value v = m_deque.front();
m_deque.pop_front();
return v;
}
// function value_t q.pop_back();
T_Value pop_back() {
if (m_deque.empty()) return m_defaultValue;
T_Value v = m_deque.back();
m_deque.pop_back();
return v;
}
// Setting. Verilog: assoc[index] = v
// Can't just overload operator[] or provide a "at" reference to set,
// because we need to be able to insert only when the value is set
T_Value& at(vlsint32_t index) {
static T_Value s_throwAway;
// Needs to work for dynamic arrays, so does not use T_MaxSize
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
s_throwAway = atDefault();
return s_throwAway;
} else {
return m_deque[index];
}
}
// Accessing. Verilog: v = assoc[index]
const T_Value& at(vlsint32_t index) const {
static T_Value s_throwAway;
// Needs to work for dynamic arrays, so does not use T_MaxSize
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
return atDefault();
} else {
return m_deque[index];
}
}
// function void q.insert(index, value);
void insert(vlsint32_t index, const T_Value& value) {
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return;
m_deque.insert(m_deque.begin() + index, value);
}
// Return slice q[lsb:msb]
VlQueue slice(vlsint32_t lsb, vlsint32_t msb) const {
VlQueue out;
if (VL_UNLIKELY(lsb < 0)) lsb = 0;
if (VL_UNLIKELY(lsb >= m_deque.size())) lsb = m_deque.size() - 1;
if (VL_UNLIKELY(msb >= m_deque.size())) msb = m_deque.size() - 1;
for (vlsint32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]);
return out;
}
// For save/restore
const_iterator begin() const { return m_deque.begin(); }
const_iterator end() const { return m_deque.end(); }
// Methods
void sort() { std::sort(m_deque.begin(), m_deque.end()); }
template <typename Func> void sort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
std::sort(m_deque.begin(), m_deque.end(), [=](const T_Value& a, const T_Value& b) {
// index number is meaninless with sort, as it changes
return with_func(0, a) < with_func(0, b);
});
}
void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); }
template <typename Func> void rsort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
std::sort(m_deque.rbegin(), m_deque.rend(), [=](const T_Value& a, const T_Value& b) {
// index number is meaninless with sort, as it changes
return with_func(0, a) < with_func(0, b);
});
}
void reverse() { std::reverse(m_deque.begin(), m_deque.end()); }
void shuffle() { std::shuffle(m_deque.begin(), m_deque.end(), VlURNG()); }
VlQueue unique() const {
VlQueue out;
std::unordered_set<T_Value> saw;
for (const auto& i : m_deque) {
auto it = saw.find(i);
if (it == saw.end()) {
saw.insert(it, i);
out.push_back(i);
}
}
return out;
}
VlQueue<IData> unique_index() const {
VlQueue<IData> out;
IData index = 0;
std::unordered_set<T_Value> saw;
for (const auto& i : m_deque) {
auto it = saw.find(i);
if (it == saw.end()) {
saw.insert(it, i);
out.push_back(index);
}
++index;
}
return out;
}
template <typename Func> VlQueue find(Func with_func) const {
VlQueue out;
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) out.push_back(i);
++index;
}
return out;
}
template <typename Func> VlQueue<IData> find_index(Func with_func) const {
VlQueue<IData> out;
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) out.push_back(index);
++index;
}
return out;
}
template <typename Func> VlQueue find_first(Func with_func) const {
// Can't use std::find_if as need index number
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) return VlQueue::cons(i);
++index;
}
return VlQueue{};
}
template <typename Func> VlQueue<IData> find_first_index(Func with_func) const {
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) return VlQueue<IData>::cons(index);
++index;
}
return VlQueue<IData>{};
}
template <typename Func> VlQueue find_last(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
if (with_func(index, *it)) return VlQueue::cons(*it);
--index;
}
return VlQueue{};
}
template <typename Func> VlQueue<IData> find_last_index(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
if (with_func(index, *it)) return VlQueue<IData>::cons(index);
--index;
}
return VlQueue<IData>{};
}
// Reduction operators
VlQueue min() const {
if (m_deque.empty()) return VlQueue();
const auto it = std::min_element(m_deque.begin(), m_deque.end());
return VlQueue::cons(*it);
}
VlQueue max() const {
if (m_deque.empty()) return VlQueue();
const auto it = std::max_element(m_deque.begin(), m_deque.end());
return VlQueue::cons(*it);
}
T_Value r_sum() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_deque) out += i;
return out;
}
template <typename Func> T_Value r_sum(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out += with_func(index++, i);
return out;
}
T_Value r_product() const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
T_Value out{*it};
++it;
for (; it != m_deque.end(); ++it) out *= *it;
return out;
}
template <typename Func> T_Value r_product(Func with_func) const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
IData index = 0;
T_Value out{with_func(index, *it)};
++it;
++index;
for (; it != m_deque.end(); ++it) out *= with_func(index++, *it);
return out;
}
T_Value r_and() const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
T_Value out{*it};
++it;
for (; it != m_deque.end(); ++it) out &= *it;
return out;
}
template <typename Func> T_Value r_and(Func with_func) const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
IData index = 0;
T_Value out{with_func(index, *it)};
++it;
++index;
for (; it != m_deque.end(); ++it) out &= with_func(index, *it);
return out;
}
T_Value r_or() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_deque) out |= i;
return out;
}
template <typename Func> T_Value r_or(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out |= with_func(index++, i);
return out;
}
T_Value r_xor() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_deque) out ^= i;
return out;
}
template <typename Func> T_Value r_xor(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out ^= with_func(index++, i);
return out;
}
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
if (m_deque.empty()) return "'{}"; // No trailing space
std::string out = "'{";
std::string comma;
for (const auto& i : m_deque) {
out += comma + VL_TO_STRING(i);
comma = ", ";
}
return out + "} ";
}
};
template <class T_Value> std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
return obj.to_string();
}
//===================================================================
// Verilog associative array container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
//
template <class T_Key, class T_Value> class VlAssocArray final {
private:
// TYPES
using Map = std::map<T_Key, T_Value>;
public:
using const_iterator = typename Map::const_iterator;
private:
// MEMBERS
Map m_map; // State of the assoc array
T_Value m_defaultValue; // Default value
public:
// CONSTRUCTORS
// m_defaultValue isn't defaulted. Caller's constructor must do it.
VlAssocArray() = default;
~VlAssocArray() = default;
VlAssocArray(const VlAssocArray&) = default;
VlAssocArray(VlAssocArray&&) = default;
VlAssocArray& operator=(const VlAssocArray&) = default;
VlAssocArray& operator=(VlAssocArray&&) = default;
// METHODS
T_Value& atDefault() { return m_defaultValue; }
const T_Value& atDefault() const { return m_defaultValue; }
// Size of array. Verilog: function int size(), or int num()
int size() const { return m_map.size(); }
// Clear array. Verilog: function void delete([input index])
void clear() { m_map.clear(); }
void erase(const T_Key& index) { m_map.erase(index); }
// Return 0/1 if element exists. Verilog: function int exists(input index)
int exists(const T_Key& index) const { return m_map.find(index) != m_map.end(); }
// Return first element. Verilog: function int first(ref index);
int first(T_Key& indexr) const {
const auto it = m_map.cbegin();
if (it == m_map.end()) return 0;
indexr = it->first;
return 1;
}
// Return last element. Verilog: function int last(ref index)
int last(T_Key& indexr) const {
const auto it = m_map.crbegin();
if (it == m_map.rend()) return 0;
indexr = it->first;
return 1;
}
// Return next element. Verilog: function int next(ref index)
int next(T_Key& indexr) const {
auto it = m_map.find(indexr);
if (VL_UNLIKELY(it == m_map.end())) return 0;
++it;
if (VL_UNLIKELY(it == m_map.end())) return 0;
indexr = it->first;
return 1;
}
// Return prev element. Verilog: function int prev(ref index)
int prev(T_Key& indexr) const {
auto it = m_map.find(indexr);
if (VL_UNLIKELY(it == m_map.end())) return 0;
if (VL_UNLIKELY(it == m_map.begin())) return 0;
--it;
indexr = it->first;
return 1;
}
// Setting. Verilog: assoc[index] = v
// Can't just overload operator[] or provide a "at" reference to set,
// because we need to be able to insert only when the value is set
T_Value& at(const T_Key& index) {
const auto it = m_map.find(index);
if (it == m_map.end()) {
std::pair<typename Map::iterator, bool> pit = m_map.emplace(index, m_defaultValue);
return pit.first->second;
}
return it->second;
}
// Accessing. Verilog: v = assoc[index]
const T_Value& at(const T_Key& index) const {
const auto it = m_map.find(index);
if (it == m_map.end()) {
return m_defaultValue;
} else {
return it->second;
}
}
// Setting as a chained operation
VlAssocArray& set(const T_Key& index, const T_Value& value) {
at(index) = value;
return *this;
}
VlAssocArray& setDefault(const T_Value& value) {
atDefault() = value;
return *this;
}
// For save/restore
const_iterator begin() const { return m_map.begin(); }
const_iterator end() const { return m_map.end(); }
// Methods
VlQueue<T_Value> unique() const {
VlQueue<T_Value> out;
std::set<T_Value> saw;
for (const auto& i : m_map) {
auto it = saw.find(i.second);
if (it == saw.end()) {
saw.insert(it, i.second);
out.push_back(i.second);
}
}
return out;
}
VlQueue<T_Key> unique_index() const {
VlQueue<T_Key> out;
std::set<T_Key> saw;
for (const auto& i : m_map) {
auto it = saw.find(i.second);
if (it == saw.end()) {
saw.insert(it, i.second);
out.push_back(i.first);
}
}
return out;
}
template <typename Func> VlQueue<T_Value> find(Func with_func) const {
VlQueue<T_Value> out;
for (const auto& i : m_map)
if (with_func(i.first, i.second)) out.push_back(i.second);
return out;
}
template <typename Func> VlQueue<T_Key> find_index(Func with_func) const {
VlQueue<T_Key> out;
for (const auto& i : m_map)
if (with_func(i.first, i.second)) out.push_back(i.first);
return out;
}
template <typename Func> VlQueue<T_Value> find_first(Func with_func) const {
const auto it
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.end()) return VlQueue<T_Value>{};
return VlQueue<T_Value>::cons(it->second);
}
template <typename Func> VlQueue<T_Key> find_first_index(Func with_func) const {
const auto it
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.end()) return VlQueue<T_Value>{};
return VlQueue<T_Key>::cons(it->first);
}
template <typename Func> VlQueue<T_Value> find_last(Func with_func) const {
const auto it
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.rend()) return VlQueue<T_Value>{};
return VlQueue<T_Value>::cons(it->second);
}
template <typename Func> VlQueue<T_Key> find_last_index(Func with_func) const {
const auto it
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.rend()) return VlQueue<T_Value>{};
return VlQueue<T_Key>::cons(it->first);
}
// Reduction operators
VlQueue<T_Value> min() const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::min_element(
m_map.begin(), m_map.end(),
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return a.second < b.second;
});
return VlQueue<T_Value>::cons(it->second);
}
VlQueue<T_Value> max() const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::max_element(
m_map.begin(), m_map.end(),
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return a.second < b.second;
});
return VlQueue<T_Value>::cons(it->second);
}
T_Value r_sum() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out += i.second;
return out;
}
template <typename Func> T_Value r_sum(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out += with_func(i.first, i.second);
return out;
}
T_Value r_product() const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{it->second};
++it;
for (; it != m_map.end(); ++it) out *= it->second;
return out;
}
template <typename Func> T_Value r_product(Func with_func) const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{with_func(it->first, it->second)};
++it;
for (; it != m_map.end(); ++it) out *= with_func(it->first, it->second);
return out;
}
T_Value r_and() const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{it->second};
++it;
for (; it != m_map.end(); ++it) out &= it->second;
return out;
}
template <typename Func> T_Value r_and(Func with_func) const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{with_func(it->first, it->second)};
++it;
for (; it != m_map.end(); ++it) out &= with_func(it->first, it->second);
return out;
}
T_Value r_or() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out |= i.second;
return out;
}
template <typename Func> T_Value r_or(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out |= with_func(i.first, i.second);
return out;
}
T_Value r_xor() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out ^= i.second;
return out;
}
template <typename Func> T_Value r_xor(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out ^= with_func(i.first, i.second);
return out;
}
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
if (m_map.empty()) return "'{}"; // No trailing space
std::string out = "'{";
std::string comma;
for (const auto& i : m_map) {
out += comma + VL_TO_STRING(i.first) + ":" + VL_TO_STRING(i.second);
comma = ", ";
}
// Default not printed - maybe random init data
return out + "} ";
}
};
template <class T_Key, class T_Value>
std::string VL_TO_STRING(const VlAssocArray<T_Key, T_Value>& obj) {
return obj.to_string();
}
template <class T_Key, class T_Value>
void VL_READMEM_N(bool hex, int bits, const std::string& filename,
VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
VlReadMem rmem(hex, bits, filename, start, end);
if (VL_UNLIKELY(!rmem.isOpen())) return;
while (true) {
QData addr;
std::string data;
if (rmem.get(addr /*ref*/, data /*ref*/)) {
rmem.setData(&(obj.at(addr)), data);
} else {
break;
}
}
}
template <class T_Key, class T_Value>
void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
const VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
VlWriteMem wmem(hex, bits, filename, start, end);
if (VL_UNLIKELY(!wmem.isOpen())) return;
for (const auto& i : obj) {
const QData addr = i.first;
if (addr >= start && addr <= end) wmem.print(addr, true, &(i.second));
}
}
//===================================================================
/// Verilog unpacked array container
/// For when a standard C++[] array is not sufficient, e.g. an
/// array under a queue, or methods operating on the array.
///
/// A 'struct' as we want this to be an aggregate type that allows
/// static aggregate initialization. Consider data members private.
///
/// This class may get exposed to a Verilated Model's top I/O, if the top
/// IO has an unpacked array.
template <class T_Value, std::size_t T_Depth> struct VlUnpacked final {
// MEMBERS
// This should be the only data member, otherwise generated static initializers need updating
T_Value m_storage[T_Depth]; // Contents of the unpacked array
// CONSTRUCTORS
VlUnpacked() = default;
~VlUnpacked() = default;
VlUnpacked(const VlUnpacked&) = default;
VlUnpacked(VlUnpacked&&) = default;
VlUnpacked& operator=(const VlUnpacked&) = default;
VlUnpacked& operator=(VlUnpacked&&) = default;
// METHODS
// Raw access
WData* data() { return &m_storage[0]; }
const WData* data() const { return &m_storage[0]; }
T_Value& operator[](size_t index) { return m_storage[index]; };
const T_Value& operator[](size_t index) const { return m_storage[index]; };
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
std::string out = "'{";
std::string comma;
for (int i = 0; i < T_Depth; ++i) {
out += comma + VL_TO_STRING(m_storage[i]);
comma = ", ";
}
return out + "} ";
}
};
template <class T_Value, std::size_t T_Depth>
std::string VL_TO_STRING(const VlUnpacked<T_Value, T_Depth>& obj) {
return obj.to_string();
}
//===================================================================
// Verilog class reference container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
#define VlClassRef std::shared_ptr
template <class T> // T typically of type VlClassRef<x>
inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
if (VL_UNLIKELY(!t)) Verilated::nullPointerError(filename, linenum);
return t;
}
template <typename T, typename U>
static inline bool VL_CAST_DYNAMIC(VlClassRef<T> in, VlClassRef<U>& outr) {
VlClassRef<U> casted = std::dynamic_pointer_cast<U>(in);
if (VL_LIKELY(casted)) {
outr = casted;
return true;
} else {
return false;
}
}
//======================================================================
// Conversion functions
extern std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_MT_SAFE;
inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE {
VlWide<VL_WQ_WORDS_E> lw;
VL_SET_WQ(lw, lhs);
return VL_CVT_PACK_STR_NW(VL_WQ_WORDS_E, lw);
}
inline std::string VL_CVT_PACK_STR_NN(const std::string& lhs) VL_PURE { return lhs; }
inline std::string& VL_CVT_PACK_STR_NN(std::string& lhs) VL_PURE { return lhs; }
inline std::string VL_CVT_PACK_STR_NI(IData lhs) VL_PURE {
VlWide<VL_WQ_WORDS_E> lw;
VL_SET_WI(lw, lhs);
return VL_CVT_PACK_STR_NW(1, lw);
}
inline std::string VL_CONCATN_NNN(const std::string& lhs, const std::string& rhs) VL_PURE {
return lhs + rhs;
}
inline std::string VL_REPLICATEN_NNQ(int, int, int, const std::string& lhs, IData rep) VL_PURE {
std::string out;
out.reserve(lhs.length() * rep);
for (unsigned times = 0; times < rep; ++times) out += lhs;
return out;
}
inline std::string VL_REPLICATEN_NNI(int obits, int lbits, int rbits, const std::string& lhs,
IData rep) VL_PURE {
return VL_REPLICATEN_NNQ(obits, lbits, rbits, lhs, rep);
}
inline IData VL_LEN_IN(const std::string& ld) { return ld.length(); }
extern std::string VL_TOLOWER_NN(const std::string& ld);
extern std::string VL_TOUPPER_NN(const std::string& ld);
extern IData VL_FERROR_IN(IData fpi, std::string& outputr) VL_MT_SAFE;
extern IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) VL_MT_SAFE;
extern IData VL_FOPEN_MCD_N(const std::string& filename) VL_MT_SAFE;
extern void VL_READMEM_N(bool hex, int bits, QData depth, int array_lsb,
const std::string& filename, void* memp, QData start,
QData end) VL_MT_SAFE;
extern void VL_WRITEMEM_N(bool hex, int bits, QData depth, int array_lsb,
const std::string& filename, const void* memp, QData start,
QData end) VL_MT_SAFE;
extern IData VL_SSCANF_INX(int lbits, const std::string& ld, const char* formatp, ...) VL_MT_SAFE;
extern void VL_SFORMAT_X(int obits_ignored, std::string& output, const char* formatp,
...) VL_MT_SAFE;
extern std::string VL_SFORMATF_NX(const char* formatp, ...) VL_MT_SAFE;
extern void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix, int width,
VerilatedContext* contextp) VL_MT_SAFE;
extern IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE;
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, CData& rdr) VL_MT_SAFE {
VlWide<2> rwp; // WData must always be at least 2
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
if (got) rdr = rwp[0];
return got;
}
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, SData& rdr) VL_MT_SAFE {
VlWide<2> rwp; // WData must always be at least 2
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
if (got) rdr = rwp[0];
return got;
}
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, IData& rdr) VL_MT_SAFE {
VlWide<2> rwp;
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
if (got) rdr = rwp[0];
return got;
}
inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, QData& rdr) VL_MT_SAFE {
VlWide<2> rwp;
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
if (got) rdr = VL_SET_QW(rwp);
return got;
}
inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, double& rdr) VL_MT_SAFE {
VlWide<2> rwp;
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
if (got) rdr = VL_CVT_D_Q(VL_SET_QW(rwp));
return got;
}
extern IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_SAFE;
//======================================================================
// Strings
extern std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE;
extern CData VL_GETC_N(const std::string& lhs, IData rhs) VL_PURE;
extern std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_PURE;
inline IData VL_CMP_NN(const std::string& lhs, const std::string& rhs, bool ignoreCase) VL_PURE {
// SystemVerilog does not allow a string variable to contain '\0'.
// So C functions such as strcmp() can correctly compare strings.
if (ignoreCase) {
return VL_STRCASECMP(lhs.c_str(), rhs.c_str());
} else {
return std::strcmp(lhs.c_str(), rhs.c_str());
}
}
extern IData VL_ATOI_N(const std::string& str, int base) VL_PURE;
extern IData VL_FGETS_NI(std::string& dest, IData fpi);
#endif // Guard
+26 -27
View File
@@ -30,7 +30,6 @@
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_heavy.h"
#include "verilated_syms.h"
#include <deque>
@@ -104,7 +103,7 @@ public:
// METHODS
// Add message to queue (called by producer)
void post(const VerilatedMsg& msg) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_queue.insert(msg); // Pass by value to copy the message into queue
++m_depth;
}
@@ -124,7 +123,7 @@ public:
const VerilatedMsg msg = *(it);
m_queue.erase(it);
m_mutex.unlock();
m_depth--; // Ok if outside critical section as only this code checks the value
--m_depth; // Ok if outside critical section as only this code checks the value
{
VL_DEBUG_IF(VL_DBG_MSGF("Executing callback from mtaskId=%d\n", msg.mtaskId()););
msg.run();
@@ -179,7 +178,7 @@ public:
// FILE* list constructed from a file-descriptor
class VerilatedFpList final {
FILE* m_fp[31];
FILE* m_fp[31] = {};
std::size_t m_sz = 0;
public:
@@ -256,11 +255,11 @@ public: // But only for verilated*.cpp
int timeFormatWidth() const VL_MT_SAFE { return m_s.m_timeFormatWidth; }
void timeFormatWidth(int value) VL_MT_SAFE { m_s.m_timeFormatWidth = value; }
std::string timeFormatSuffix() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
const VerilatedLockGuard lock(m_timeDumpMutex);
const VerilatedLockGuard lock{m_timeDumpMutex};
return m_timeFormatSuffix;
}
void timeFormatSuffix(const std::string& value) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
const VerilatedLockGuard lock(m_timeDumpMutex);
const VerilatedLockGuard lock{m_timeDumpMutex};
m_timeFormatSuffix = value;
}
@@ -276,7 +275,7 @@ public: // But only for verilated*.cpp
public: // But only for verilated*.cpp
// METHODS - file IO
IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
if (m_fdFreeMct.empty()) return 0;
IData idx = m_fdFreeMct.back();
m_fdFreeMct.pop_back();
@@ -285,10 +284,10 @@ public: // But only for verilated*.cpp
return (1 << idx);
}
IData fdNew(const char* filenamep, const char* modep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
FILE* fp = std::fopen(filenamep, modep);
FILE* const fp = std::fopen(filenamep, modep);
if (VL_UNLIKELY(!fp)) return 0;
// Bit 31 indicates it's a descriptor not a MCD
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
if (m_fdFree.empty()) {
// Need to create more space in m_fdps and m_fdFree
const std::size_t start = std::max<std::size_t>(31UL + 1UL + 3UL, m_fdps.size());
@@ -306,25 +305,25 @@ public: // But only for verilated*.cpp
return (idx | (1UL << 31)); // bit 31 indicates not MCD
}
void fdFlush(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
for (const auto& i : fdlist) std::fflush(i);
}
IData fdSeek(IData fdi, IData offset, IData origin) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
if (VL_UNLIKELY(fdlist.size() != 1)) return 0;
return static_cast<IData>(
std::fseek(*fdlist.begin(), static_cast<long>(offset), static_cast<int>(origin)));
}
IData fdTell(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
if (VL_UNLIKELY(fdlist.size() != 1)) return 0;
return static_cast<IData>(std::ftell(*fdlist.begin()));
}
void fdWrite(IData fdi, const std::string& output) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
for (const auto& i : fdlist) {
if (VL_UNLIKELY(!i)) continue;
@@ -332,7 +331,7 @@ public: // But only for verilated*.cpp
}
}
void fdClose(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
if (VL_BITISSET_I(fdi, 31)) {
// Non-MCD case
IData idx = VL_MASK_I(31) & fdi;
@@ -356,7 +355,7 @@ public: // But only for verilated*.cpp
}
}
inline FILE* fdToFp(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
if (VL_UNLIKELY(fdlist.size() != 1)) return nullptr;
return *fdlist.begin();
@@ -463,7 +462,7 @@ public:
// per map overhead * N scopes would take much more space and cache thrashing.
// As scopep's are pointers, this implicitly handles multiple Context's
static inline void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE {
const VerilatedLockGuard lock(s().m_userMapMutex);
const VerilatedLockGuard lock{s().m_userMapMutex};
const auto it = s().m_userMap.find(std::make_pair(scopep, userKey));
if (it != s().m_userMap.end()) {
it->second = userData;
@@ -472,7 +471,7 @@ public:
}
}
static inline void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
const VerilatedLockGuard lock(s().m_userMapMutex);
const VerilatedLockGuard lock{s().m_userMapMutex};
const auto& it = vlstd::as_const(s().m_userMap).find(std::make_pair(scopep, userKey));
if (VL_UNLIKELY(it == s().m_userMap.end())) return nullptr;
return it->second;
@@ -482,7 +481,7 @@ public: // But only for verilated.cpp
// Symbol table destruction cleans up the entries for each scope.
static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE {
// Slow ok - called once/scope on destruction, so we simply iterate.
const VerilatedLockGuard lock(s().m_userMapMutex);
const VerilatedLockGuard lock{s().m_userMapMutex};
for (auto it = s().m_userMap.begin(); it != s().m_userMap.end();) {
if (it->first.first == scopep) {
s().m_userMap.erase(it++);
@@ -492,7 +491,7 @@ public: // But only for verilated.cpp
}
}
static void userDump() VL_MT_SAFE {
const VerilatedLockGuard lock(s().m_userMapMutex); // Avoid it changing in middle of dump
const VerilatedLockGuard lock{s().m_userMapMutex}; // Avoid it changing in middle of dump
bool first = true;
for (const auto& i : s().m_userMap) {
if (first) {
@@ -508,13 +507,13 @@ public: // But only for verilated*.cpp
// METHODS - hierarchy
static void hierarchyAdd(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE {
// Slow ok - called at construction for VPI accessible elements
const VerilatedLockGuard lock(s().m_hierMapMutex);
const VerilatedLockGuard lock{s().m_hierMapMutex};
s().m_hierMap[fromp].push_back(top);
}
static void hierarchyRemove(const VerilatedScope* fromp,
const VerilatedScope* top) VL_MT_SAFE {
// Slow ok - called at destruction for VPI accessible elements
const VerilatedLockGuard lock(s().m_hierMapMutex);
const VerilatedLockGuard lock{s().m_hierMapMutex};
VerilatedHierarchyMap& map = s().m_hierMap;
if (map.find(fromp) == map.end()) return;
auto& scopes = map[fromp];
@@ -537,7 +536,7 @@ public: // But only for verilated*.cpp
// miss at the cost of a multiply, and all lookups move to slowpath.
static int exportInsert(const char* namep) VL_MT_SAFE {
// Slow ok - called once/function at creation
const VerilatedLockGuard lock(s().m_exportMutex);
const VerilatedLockGuard lock{s().m_exportMutex};
const auto it = s().m_exportMap.find(namep);
if (it == s().m_exportMap.end()) {
s().m_exportMap.emplace(namep, s().m_exportNext++);
@@ -547,24 +546,24 @@ public: // But only for verilated*.cpp
}
}
static int exportFind(const char* namep) VL_MT_SAFE {
const VerilatedLockGuard lock(s().m_exportMutex);
const VerilatedLockGuard lock{s().m_exportMutex};
const auto& it = s().m_exportMap.find(namep);
if (VL_LIKELY(it != s().m_exportMap.end())) return it->second;
std::string msg = (std::string("%Error: Testbench C called ") + namep
+ " but no such DPI export function name exists in ANY model");
const std::string msg = (std::string{"%Error: Testbench C called "} + namep
+ " but no such DPI export function name exists in ANY model");
VL_FATAL_MT("unknown", 0, "", msg.c_str());
return -1;
}
static const char* exportName(int funcnum) VL_MT_SAFE {
// Slowpath; find name for given export; errors only so no map to reverse-map it
const VerilatedLockGuard lock(s().m_exportMutex);
const VerilatedLockGuard lock{s().m_exportMutex};
for (const auto& i : s().m_exportMap) {
if (i.second == funcnum) return i.first;
}
return "*UNKNOWN*";
}
static void exportsDump() VL_MT_SAFE {
const VerilatedLockGuard lock(s().m_exportMutex);
const VerilatedLockGuard lock{s().m_exportMutex};
bool first = true;
for (const auto& i : s().m_exportMap) {
if (first) {
+113
View File
@@ -0,0 +1,113 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//=============================================================================
//
// Code available from: https://verilator.org
//
// Copyright 2012-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//=============================================================================
///
/// \file
/// \brief Verilated general profiling header
///
/// This file is not part of the Verilated public-facing API.
/// It is only for internal use by Verilated library routines.
///
//=============================================================================
#ifndef VERILATOR_VERILATED_PROFILER_H_
#define VERILATOR_VERILATED_PROFILER_H_
#include "verilatedos.h"
#include "verilated.h" // for VerilatedMutex and clang annotations
// Profile record, private class used only by this header
class VerilatedProfilerRec final {
std::string m_name; // Hashed name of mtask/etc
size_t m_counterNumber = 0; // Which counter has data
public:
// METHODS
VerilatedProfilerRec(size_t counterNumber, const std::string& name)
: m_name{name}
, m_counterNumber{counterNumber} {}
VerilatedProfilerRec() = default;
size_t counterNumber() const { return m_counterNumber; }
std::string name() const { return m_name; }
};
// Create some number of bucketed profilers
template <std::size_t T_Entries> class VerilatedProfiler final {
// Counters are stored packed, all together, versus in VerilatedProfilerRec to
// reduce cache effects
std::array<vluint64_t, T_Entries> m_counters{}; // Time spent on this record
std::deque<VerilatedProfilerRec> m_records; // Record information
public:
// METHODS
VerilatedProfiler() = default;
~VerilatedProfiler() = default;
void write(const char* modelp, const std::string& filename) VL_MT_SAFE;
void addCounter(size_t counter, const std::string& name) {
VL_DEBUG_IF(assert(counter < T_Entries););
m_records.emplace_back(VerilatedProfilerRec{counter, name});
}
void startCounter(size_t counter) {
vluint64_t val;
VL_RDTSC(val);
// -= so when we add end time in stopCounter, we already subtracted
// out, without needing to hold another temporary
m_counters[counter] -= val;
}
void stopCounter(size_t counter) {
vluint64_t val;
VL_RDTSC(val);
m_counters[counter] += val;
}
};
template <std::size_t T_Entries>
void VerilatedProfiler<T_Entries>::write(const char* modelp,
const std::string& filename) VL_MT_SAFE {
static VerilatedMutex s_mutex;
const VerilatedLockGuard lock{s_mutex};
// On the first call we create the file. On later calls we append.
// So when we have multiple models in an executable, possibly even
// running on different threads, each will have a different symtab so
// each will collect is own data correctly. However when each is
// destroid we need to get all the data, not keep overwriting and only
// get the last model's data.
static bool s_firstCall = true;
VL_DEBUG_IF(VL_DBG_MSGF("+prof+vlt+file writing to '%s'\n", filename.c_str()););
FILE* fp = nullptr;
if (!s_firstCall) fp = std::fopen(filename.c_str(), "a");
if (VL_UNLIKELY(!fp))
fp = std::fopen(filename.c_str(), "w"); // firstCall, or doesn't exist yet
if (VL_UNLIKELY(!fp)) {
VL_FATAL_MT(filename.c_str(), 0, "", "+prof+vlt+file file not writable");
// cppcheck-suppress resourceLeak // bug, doesn't realize fp is nullptr
return; // LCOV_EXCL_LINE
}
s_firstCall = false;
// TODO Perhaps merge with verilated_coverage output format, so can
// have a common merging and reporting tool, etc.
fprintf(fp, "// Verilated model profile-guided optimization data dump file\n");
fprintf(fp, "`verilator_config\n");
for (const auto& it : m_records) {
const std::string& name = it.name();
fprintf(fp, "profile_data -model \"%s\" -mtask \"%s\" -cost 64'd%" VL_PRI64 "u\n", modelp,
name.c_str(), m_counters[it.counterNumber()]);
}
std::fclose(fp);
}
#endif
+5 -5
View File
@@ -93,8 +93,8 @@ void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE {
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, std::strlen(VLTSAVE_HEADER_STR)))) {
const std::string fn = filename();
const std::string msg
= std::string(
"Can't deserialize; file has wrong header signature, or file not found: ")
= std::
string{"Can't deserialize; file has wrong header signature, or file not found: "}
+ filename();
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
// Die before we close() as close would infinite loop
@@ -112,7 +112,7 @@ void VerilatedDeserialize::trailer() VL_MT_UNSAFE_ONE {
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_TRAILER_STR, std::strlen(VLTSAVE_TRAILER_STR)))) {
const std::string fn = filename();
const std::string msg
= std::string("Can't deserialize; file has wrong end-of-file signature: ")
= std::string{"Can't deserialize; file has wrong end-of-file signature: "}
+ filename();
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
// Die before we close() as close would infinite loop
@@ -204,7 +204,7 @@ void VerilatedSave::flush() VL_MT_UNSAFE_ONE {
if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) {
// LCOV_EXCL_START
// write failed, presume error (perhaps out of disk space)
std::string msg = std::string(__FUNCTION__) + ": " + std::strerror(errno);
const std::string msg = std::string{__FUNCTION__} + ": " + std::strerror(errno);
VL_FATAL_MT("", 0, "", msg.c_str());
close();
break;
@@ -235,7 +235,7 @@ void VerilatedRestore::fill() VL_MT_UNSAFE_ONE {
if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) {
// LCOV_EXCL_START
// write failed, presume error (perhaps out of disk space)
const std::string msg = std::string(__FUNCTION__) + ": " + std::strerror(errno);
const std::string msg = std::string{__FUNCTION__} + ": " + std::strerror(errno);
VL_FATAL_MT("", 0, "", msg.c_str());
close();
break;
+1 -1
View File
@@ -23,7 +23,7 @@
#define VERILATOR_VERILATED_SAVE_C_H_
#include "verilatedos.h"
#include "verilated_heavy.h"
#include "verilated.h"
#include <string>
+1 -1
View File
@@ -30,7 +30,7 @@
#define VERILATOR_VERILATED_SYMS_H_
#include "verilatedos.h"
#include "verilated_heavy.h"
#include "verilated.h"
#include "verilated_sym_props.h"
#include <map>
+40 -12
View File
@@ -25,6 +25,7 @@
#include "verilated_threads.h"
#include <cstdio>
#include <fstream>
//=============================================================================
// Globals
@@ -48,7 +49,8 @@ VlMTaskVertex::VlMTaskVertex(vluint32_t upstreamDepCount)
// VlWorkerThread
VlWorkerThread::VlWorkerThread(VlThreadPool* poolp, VerilatedContext* contextp, bool profiling)
: m_poolp{poolp}
: m_ready_size{0}
, m_poolp{poolp}
, m_profiling{profiling} // Must init this last -- after setting up fields that it might read:
, m_exiting{false}
, m_cthread{startWorker, this}
@@ -104,7 +106,7 @@ VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads, bool profil
}
// Create'em
for (int i = 0; i < nThreads; ++i) {
m_workers.push_back(new VlWorkerThread(this, contextp, profiling));
m_workers.push_back(new VlWorkerThread{this, contextp, profiling});
}
// Set up a profile buffer for the current thread too -- on the
// assumption that it's the same thread that calls eval and may be
@@ -131,22 +133,22 @@ void VlThreadPool::setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex) {
// try not to malloc while collecting profiling.
t_profilep->reserve(4096);
{
const VerilatedLockGuard lk(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_allProfiles.insert(t_profilep);
}
}
void VlThreadPool::profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lk(m_mutex);
const VerilatedLockGuard lock{m_mutex};
for (const auto& profilep : m_allProfiles) {
// Every thread's profile trace gets a copy of rec.
profilep->emplace_back(rec);
}
}
void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
void VlThreadPool::profileDump(const char* filenamep, vluint64_t tickStart, vluint64_t tickEnd)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lk(m_mutex);
const VerilatedLockGuard lock{m_mutex};
VL_DEBUG_IF(VL_DBG_MSGF("+prof+threads writing to '%s'\n", filenamep););
FILE* const fp = std::fopen(filenamep, "w");
@@ -158,7 +160,7 @@ void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
// TODO Perhaps merge with verilated_coverage output format, so can
// have a common merging and reporting tool, etc.
fprintf(fp, "VLPROFTHREAD 1.0 # Verilator thread profile dump version 1.0\n");
fprintf(fp, "VLPROFTHREAD 1.1 # Verilator thread profile dump version 1.1\n");
fprintf(fp, "VLPROF arg --threads %" VL_PRI64 "u\n", vluint64_t(m_workers.size() + 1));
fprintf(fp, "VLPROF arg +verilator+prof+threads+start+%" VL_PRI64 "u\n",
Verilated::threadContextp()->profThreadsStart());
@@ -166,6 +168,16 @@ void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
Verilated::threadContextp()->profThreadsWindow());
fprintf(fp, "VLPROF stat yields %" VL_PRI64 "u\n", VlMTaskVertex::yields());
// Copy /proc/cpuinfo into this output so verilator_gantt can be run on
// a different machine
{
const std::unique_ptr<std::ifstream> ifp{new std::ifstream("/proc/cpuinfo")};
if (!ifp->fail()) {
std::string line;
while (std::getline(*ifp, line)) { fprintf(fp, "VLPROFPROC %s\n", line.c_str()); }
}
}
vluint32_t thread_id = 0;
for (const auto& pi : m_allProfiles) {
++thread_id;
@@ -176,20 +188,36 @@ void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
case VlProfileRec::TYPE_BARRIER: //
printing = true;
break;
case VlProfileRec::TYPE_EVAL:
if (!printing) break;
fprintf(fp,
"VLPROF eval start %" VL_PRI64 "u elapsed %" VL_PRI64 "u"
" cpu %u on thread %u\n",
ei.m_startTime - tickStart, (ei.m_endTime - ei.m_startTime), ei.m_cpu,
thread_id);
break;
case VlProfileRec::TYPE_EVAL_LOOP:
if (!printing) break;
fprintf(fp,
"VLPROF eval_loop start %" VL_PRI64 "u elapsed %" VL_PRI64 "u"
" cpu %u on thread %u\n",
ei.m_startTime - tickStart, (ei.m_endTime - ei.m_startTime), ei.m_cpu,
thread_id);
break;
case VlProfileRec::TYPE_MTASK_RUN:
if (!printing) break;
fprintf(fp,
"VLPROF mtask %d"
" start %" VL_PRI64 "u end %" VL_PRI64 "u elapsed %" VL_PRI64 "u"
" predict_time %u cpu %u on thread %u\n",
ei.m_mtaskId, ei.m_startTime, ei.m_endTime,
(ei.m_endTime - ei.m_startTime), ei.m_predictTime, ei.m_cpu, thread_id);
" start %" VL_PRI64 "u elapsed %" VL_PRI64 "u"
" predict_start %u predict_cost %u cpu %u on thread %u\n",
ei.m_mtaskId, ei.m_startTime - tickStart, (ei.m_endTime - ei.m_startTime),
ei.m_predictStart, ei.m_predictCost, ei.m_cpu, thread_id);
break;
default: assert(false); break; // LCOV_EXCL_LINE
}
}
}
fprintf(fp, "VLPROF stat ticks %" VL_PRI64 "u\n", ticksElapsed);
fprintf(fp, "VLPROF stat ticks %" VL_PRI64 "u\n", tickEnd - tickStart);
std::fclose(fp);
}
+27 -11
View File
@@ -131,21 +131,36 @@ public:
class VlProfileRec final {
protected:
friend class VlThreadPool;
enum VlProfileE { TYPE_MTASK_RUN, TYPE_BARRIER };
VlProfileE m_type = TYPE_BARRIER; // Record type
vluint32_t m_mtaskId = 0; // Mtask we're logging
vluint32_t m_predictTime = 0; // How long scheduler predicted would take
vluint64_t m_startTime = 0; // Tick at start of execution
enum VlProfileE { TYPE_MTASK_RUN, TYPE_EVAL, TYPE_EVAL_LOOP, TYPE_BARRIER };
// Layout below allows efficient packing.
// Leave endTime first, so no math needed to calculate address in endRecord
vluint64_t m_endTime = 0; // Tick at end of execution
vluint64_t m_startTime = 0; // Tick at start of execution
vluint32_t m_mtaskId = 0; // Mtask we're logging
vluint32_t m_predictStart = 0; // Time scheduler predicted would start
vluint32_t m_predictCost = 0; // How long scheduler predicted would take
VlProfileE m_type = TYPE_BARRIER; // Record type
unsigned m_cpu; // Execution CPU number (at start anyways)
public:
class Barrier {};
VlProfileRec() = default;
explicit VlProfileRec(Barrier) { m_cpu = getcpu(); }
void startRecord(vluint64_t time, uint32_t mtask, uint32_t predict) {
void startEval(vluint64_t time) {
m_type = VlProfileRec::TYPE_EVAL;
m_startTime = time;
m_cpu = getcpu();
}
void startEvalLoop(vluint64_t time) {
m_type = VlProfileRec::TYPE_EVAL_LOOP;
m_startTime = time;
m_cpu = getcpu();
}
void startRecord(vluint64_t time, vluint32_t mtask, vluint32_t predictStart,
vluint32_t predictCost) {
m_type = VlProfileRec::TYPE_MTASK_RUN;
m_mtaskId = mtask;
m_predictTime = predict;
m_predictStart = predictStart;
m_predictCost = predictCost;
m_startTime = time;
m_cpu = getcpu();
}
@@ -225,10 +240,10 @@ public:
}
VL_CPU_RELAX();
}
VerilatedLockGuard lk(m_mutex);
VerilatedLockGuard lock{m_mutex};
while (m_ready.empty()) {
m_waiting = true;
m_cv.wait(lk);
m_cv.wait(lock);
}
m_waiting = false;
// As noted above this is inefficient if our ready list is ever
@@ -242,7 +257,7 @@ public:
VL_MT_SAFE_EXCLUDES(m_mutex) {
bool notify;
{
const VerilatedLockGuard lk(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_ready.emplace_back(fnp, selfp, evenCycle);
m_ready_size.fetch_add(1, std::memory_order_relaxed);
notify = m_waiting;
@@ -292,7 +307,8 @@ public:
return &(t_profilep->back());
}
void profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex);
void profileDump(const char* filenamep, vluint64_t ticksElapsed) VL_MT_SAFE_EXCLUDES(m_mutex);
void profileDump(const char* filenamep, vluint64_t tickStart, vluint64_t tickEnd)
VL_MT_SAFE_EXCLUDES(m_mutex);
// In profiling mode, each executing thread must call
// this once to setup profiling state:
void setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex);
+4 -4
View File
@@ -52,21 +52,21 @@ private:
public:
// Put an element at the back of the queue
void put(T value) VL_MT_SAFE_EXCLUDES(m_mutex) {
VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_queue.push_back(value);
m_cv.notify_one();
}
// Put an element at the front of the queue
void put_front(T value) VL_MT_SAFE_EXCLUDES(m_mutex) {
VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_queue.push_front(value);
m_cv.notify_one();
}
// Get an element from the front of the queue. Blocks if none available
T get() VL_MT_SAFE_EXCLUDES(m_mutex) {
VerilatedLockGuard lock(m_mutex);
VerilatedLockGuard lock{m_mutex};
m_cv.wait(lock, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); });
assert(!m_queue.empty());
T value = m_queue.front();
@@ -76,7 +76,7 @@ public:
// Non blocking get
bool tryGet(T& result) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lockGuard(m_mutex);
const VerilatedLockGuard lockGuard{m_mutex};
if (m_queue.empty()) return false;
result = m_queue.front();
m_queue.pop_front();
+10 -10
View File
@@ -246,7 +246,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::closeBase() {
shutdownWorker();
while (m_numTraceBuffers) {
delete[] m_buffersFromWorker.get();
m_numTraceBuffers--;
--m_numTraceBuffers;
}
#endif
}
@@ -351,7 +351,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
m_traceBufferSize = nextCode() + numSignals() * 2 + 4;
// Start the worker thread
m_workerThread.reset(new std::thread(&VerilatedTrace<VL_DERIVED_T>::workerThreadMain, this));
m_workerThread.reset(new std::thread{&VerilatedTrace<VL_DERIVED_T>::workerThreadMain, this});
#endif
}
@@ -398,7 +398,7 @@ void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m
// Not really VL_MT_SAFE but more VL_MT_UNSAFE_ONE.
// This does get the mutex, but if multiple threads are trying to dump
// chances are the data being dumped will have other problems
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
if (VL_UNCOVERABLE(m_timeLastDump && timeui <= m_timeLastDump)) { // LCOV_EXCL_START
VL_PRINTF_MT("%%Warning: previous dump at t=%" VL_PRI64 "u, requesting t=%" VL_PRI64
"u, dump call ignored\n",
@@ -479,29 +479,29 @@ template <>
void VerilatedTrace<VL_DERIVED_T>::addCallbackRecord(std::vector<CallbackRecord>& cbVec,
CallbackRecord& cbRec)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
if (VL_UNCOVERABLE(timeLastDump() != 0)) { // LCOV_EXCL_START
std::string msg = (std::string("Internal: ") + __FILE__ + "::" + __FUNCTION__
+ " called with already open file");
const std::string msg = (std::string{"Internal: "} + __FILE__ + "::" + __FUNCTION__
+ " called with already open file");
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
} // LCOV_EXCL_STOP
cbVec.push_back(cbRec);
}
template <> void VerilatedTrace<VL_DERIVED_T>::addInitCb(initCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr(cb, userp);
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_initCbs, cbr);
}
template <> void VerilatedTrace<VL_DERIVED_T>::addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr(cb, userp);
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_fullCbs, cbr);
}
template <> void VerilatedTrace<VL_DERIVED_T>::addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr(cb, userp);
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_chgCbs, cbr);
}
template <> void VerilatedTrace<VL_DERIVED_T>::addCleanupCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr(cb, userp);
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_cleanupCbs, cbr);
}
template <> void VerilatedTrace<VL_DERIVED_T>::module(const std::string& name) VL_MT_UNSAFE {
+897
View File
@@ -0,0 +1,897 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Code available from: https://verilator.org
//
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
///
/// \file
/// \brief Verilated common data type containers
///
/// verilated.h should be included instead of this file.
///
/// Those macro/function/variable starting or ending in _ are internal,
/// however many of the other function/macros here are also internal.
///
//*************************************************************************
#ifndef VERILATOR_VERILATED_TYPES_H_
#define VERILATOR_VERILATED_TYPES_H_
#ifndef VERILATOR_VERILATED_H_INTERNAL_
#error "verilated_types.h should only be included by verilated.h"
#endif
//===================================================================
// String formatters (required by below containers)
extern std::string VL_TO_STRING(CData lhs);
extern std::string VL_TO_STRING(SData lhs);
extern std::string VL_TO_STRING(IData lhs);
extern std::string VL_TO_STRING(QData lhs);
inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; }
extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
//=========================================================================
// Declare net data types
#define VL_SIG8(name, msb, lsb) CData name ///< Declare signal, 1-8 bits
#define VL_SIG16(name, msb, lsb) SData name ///< Declare signal, 9-16 bits
#define VL_SIG64(name, msb, lsb) QData name ///< Declare signal, 33-64 bits
#define VL_SIG(name, msb, lsb) IData name ///< Declare signal, 17-32 bits
#define VL_SIGW(name, msb, lsb, words) WData name[words] ///< Declare signal, 65+ bits
#define VL_IN8(name, msb, lsb) CData name ///< Declare input signal, 1-8 bits
#define VL_IN16(name, msb, lsb) SData name ///< Declare input signal, 9-16 bits
#define VL_IN64(name, msb, lsb) QData name ///< Declare input signal, 33-64 bits
#define VL_IN(name, msb, lsb) IData name ///< Declare input signal, 17-32 bits
#define VL_INW(name, msb, lsb, words) WData name[words] ///< Declare input signal, 65+ bits
#define VL_INOUT8(name, msb, lsb) CData name ///< Declare bidir signal, 1-8 bits
#define VL_INOUT16(name, msb, lsb) SData name ///< Declare bidir signal, 9-16 bits
#define VL_INOUT64(name, msb, lsb) QData name ///< Declare bidir signal, 33-64 bits
#define VL_INOUT(name, msb, lsb) IData name ///< Declare bidir signal, 17-32 bits
#define VL_INOUTW(name, msb, lsb, words) WData name[words] ///< Declare bidir signal, 65+ bits
#define VL_OUT8(name, msb, lsb) CData name ///< Declare output signal, 1-8 bits
#define VL_OUT16(name, msb, lsb) SData name ///< Declare output signal, 9-16 bits
#define VL_OUT64(name, msb, lsb) QData name ///< Declare output signal, 33-64bits
#define VL_OUT(name, msb, lsb) IData name ///< Declare output signal, 17-32 bits
#define VL_OUTW(name, msb, lsb, words) WData name[words] ///< Declare output signal, 65+ bits
//===================================================================
// Shuffle RNG
extern vluint64_t vl_rand64() VL_MT_SAFE;
class VlURNG final {
public:
using result_type = size_t;
static constexpr size_t min() { return 0; }
static constexpr size_t max() { return 1ULL << 31; }
size_t operator()() { return VL_MASK_I(31) & vl_rand64(); }
};
//===================================================================
// Readmem/Writemem operation classes
class VlReadMem final {
bool m_hex; // Hex format
int m_bits; // Bit width of values
const std::string& m_filename; // Filename
QData m_end; // End address (as specified by user)
FILE* m_fp; // File handle for filename
QData m_addr; // Next address to read
int m_linenum; // Line number last read from file
public:
VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end);
~VlReadMem();
bool isOpen() const { return m_fp != nullptr; }
int linenum() const { return m_linenum; }
bool get(QData& addrr, std::string& valuer);
void setData(void* valuep, const std::string& rhs);
};
class VlWriteMem final {
bool m_hex; // Hex format
int m_bits; // Bit width of values
FILE* m_fp; // File handle for filename
QData m_addr; // Next address to write
public:
VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end);
~VlWriteMem();
bool isOpen() const { return m_fp != nullptr; }
void print(QData addr, bool addrstamp, const void* valuep);
};
//===================================================================
/// Verilog wide packed bit container.
/// Similar to std::array<WData, N>, but lighter weight, only methods needed
/// by Verilator, to help compile time.
///
/// A 'struct' as we want this to be an aggregate type that allows
/// static aggregate initialization. Consider data members private.
///
/// For example a Verilog "bit [94:0]" will become a VlWide<3> because 3*32
/// bits are needed to hold the 95 bits. The MSB (bit 96) must always be
/// zero in memory, but during intermediate operations in the Verilated
/// internals is unpredictable.
static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE;
template <std::size_t T_Words> struct VlWide final {
// MEMBERS
// This should be the only data member, otherwise generated static initializers need updating
EData m_storage[T_Words]; // Contents of the packed array
// CONSTRUCTORS
// Default constructors and destructor are used. Note however that C++20 requires that
// aggregate types do not have a user declared constructor, not even an explicitly defaulted
// one.
// OPERATOR METHODS
// Default copy assignment operators are used.
operator WDataOutP() { return &m_storage[0]; } // This also allows []
operator WDataInP() const { return &m_storage[0]; } // This also allows []
// METHODS
const EData& at(size_t index) const { return m_storage[index]; }
EData& at(size_t index) { return m_storage[index]; }
WData* data() { return &m_storage[0]; }
const WData* data() const { return &m_storage[0]; }
bool operator<(const VlWide<T_Words>& rhs) const {
return _vl_cmp_w(T_Words, data(), rhs.data()) < 0;
}
};
// Convert a C array to std::array reference by pointer magic, without copy.
// Data type (second argument) is so the function template can automatically generate.
template <std::size_t T_Words>
VlWide<T_Words>& VL_CVT_W_A(const WDataInP inp, const VlWide<T_Words>&) {
return *((VlWide<T_Words>*)inp);
}
template <std::size_t T_Words> std::string VL_TO_STRING(const VlWide<T_Words>& obj) {
return VL_TO_STRING_W(T_Words, obj.data());
}
//===================================================================
// Verilog queue and dynamic array container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
//
// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound
// For dynamic arrays it is always zero
template <class T_Value, size_t T_MaxSize = 0> class VlQueue final {
private:
// TYPES
using Deque = std::deque<T_Value>;
public:
using const_iterator = typename Deque::const_iterator;
private:
// MEMBERS
Deque m_deque; // State of the assoc array
T_Value m_defaultValue; // Default value
public:
// CONSTRUCTORS
// m_defaultValue isn't defaulted. Caller's constructor must do it.
VlQueue() = default;
~VlQueue() = default;
VlQueue(const VlQueue&) = default;
VlQueue(VlQueue&&) = default;
VlQueue& operator=(const VlQueue&) = default;
VlQueue& operator=(VlQueue&&) = default;
// Standard copy constructor works. Verilog: assoca = assocb
// Also must allow conversion from a different T_MaxSize queue
template <size_t U_MaxSize = 0> VlQueue operator=(const VlQueue<T_Value, U_MaxSize>& rhs) {
m_deque = rhs.privateDeque();
if (VL_UNLIKELY(T_MaxSize && T_MaxSize < m_deque.size())) m_deque.resize(T_MaxSize - 1);
return *this;
}
static VlQueue cons(const T_Value& lhs) {
VlQueue out;
out.push_back(lhs);
return out;
}
static VlQueue cons(const T_Value& lhs, const T_Value& rhs) {
VlQueue out;
out.push_back(rhs);
out.push_back(lhs);
return out;
}
static VlQueue cons(const VlQueue& lhs, const T_Value& rhs) {
VlQueue out = lhs;
out.push_front(rhs);
return out;
}
static VlQueue cons(const T_Value& lhs, const VlQueue& rhs) {
VlQueue out = rhs;
out.push_back(lhs);
return out;
}
static VlQueue cons(const VlQueue& lhs, const VlQueue& rhs) {
VlQueue out = rhs;
for (const auto& i : lhs.m_deque) out.push_back(i);
return out;
}
// METHODS
T_Value& atDefault() { return m_defaultValue; }
const T_Value& atDefault() const { return m_defaultValue; }
const Deque& privateDeque() const { return m_deque; }
// Size. Verilog: function int size(), or int num()
int size() const { return m_deque.size(); }
// Clear array. Verilog: function void delete([input index])
void clear() { m_deque.clear(); }
void erase(vlsint32_t index) {
if (VL_LIKELY(index >= 0 && index < m_deque.size()))
m_deque.erase(m_deque.begin() + index);
}
// Dynamic array new[] becomes a renew()
void renew(size_t size) {
clear();
m_deque.resize(size, atDefault());
}
// Dynamic array new[]() becomes a renew_copy()
void renew_copy(size_t size, const VlQueue<T_Value, T_MaxSize>& rhs) {
if (size == 0) {
clear();
} else {
*this = rhs;
m_deque.resize(size, atDefault());
}
}
// function void q.push_front(value)
void push_front(const T_Value& value) {
m_deque.push_front(value);
if (VL_UNLIKELY(T_MaxSize != 0 && m_deque.size() > T_MaxSize)) m_deque.pop_back();
}
// function void q.push_back(value)
void push_back(const T_Value& value) {
if (VL_LIKELY(T_MaxSize == 0 || m_deque.size() < T_MaxSize)) m_deque.push_back(value);
}
// function value_t q.pop_front();
T_Value pop_front() {
if (m_deque.empty()) return m_defaultValue;
T_Value v = m_deque.front();
m_deque.pop_front();
return v;
}
// function value_t q.pop_back();
T_Value pop_back() {
if (m_deque.empty()) return m_defaultValue;
T_Value v = m_deque.back();
m_deque.pop_back();
return v;
}
// Setting. Verilog: assoc[index] = v
// Can't just overload operator[] or provide a "at" reference to set,
// because we need to be able to insert only when the value is set
T_Value& at(vlsint32_t index) {
static T_Value s_throwAway;
// Needs to work for dynamic arrays, so does not use T_MaxSize
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
s_throwAway = atDefault();
return s_throwAway;
} else {
return m_deque[index];
}
}
// Accessing. Verilog: v = assoc[index]
const T_Value& at(vlsint32_t index) const {
static T_Value s_throwAway;
// Needs to work for dynamic arrays, so does not use T_MaxSize
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
return atDefault();
} else {
return m_deque[index];
}
}
// function void q.insert(index, value);
void insert(vlsint32_t index, const T_Value& value) {
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return;
m_deque.insert(m_deque.begin() + index, value);
}
// Return slice q[lsb:msb]
VlQueue slice(vlsint32_t lsb, vlsint32_t msb) const {
VlQueue out;
if (VL_UNLIKELY(lsb < 0)) lsb = 0;
if (VL_UNLIKELY(lsb >= m_deque.size())) lsb = m_deque.size() - 1;
if (VL_UNLIKELY(msb >= m_deque.size())) msb = m_deque.size() - 1;
for (vlsint32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]);
return out;
}
// For save/restore
const_iterator begin() const { return m_deque.begin(); }
const_iterator end() const { return m_deque.end(); }
// Methods
void sort() { std::sort(m_deque.begin(), m_deque.end()); }
template <typename Func> void sort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
std::sort(m_deque.begin(), m_deque.end(), [=](const T_Value& a, const T_Value& b) {
// index number is meaninless with sort, as it changes
return with_func(0, a) < with_func(0, b);
});
}
void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); }
template <typename Func> void rsort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
std::sort(m_deque.rbegin(), m_deque.rend(), [=](const T_Value& a, const T_Value& b) {
// index number is meaninless with sort, as it changes
return with_func(0, a) < with_func(0, b);
});
}
void reverse() { std::reverse(m_deque.begin(), m_deque.end()); }
void shuffle() { std::shuffle(m_deque.begin(), m_deque.end(), VlURNG{}); }
VlQueue unique() const {
VlQueue out;
std::unordered_set<T_Value> saw;
for (const auto& i : m_deque) {
auto it = saw.find(i);
if (it == saw.end()) {
saw.insert(it, i);
out.push_back(i);
}
}
return out;
}
VlQueue<IData> unique_index() const {
VlQueue<IData> out;
IData index = 0;
std::unordered_set<T_Value> saw;
for (const auto& i : m_deque) {
auto it = saw.find(i);
if (it == saw.end()) {
saw.insert(it, i);
out.push_back(index);
}
++index;
}
return out;
}
template <typename Func> VlQueue find(Func with_func) const {
VlQueue out;
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) out.push_back(i);
++index;
}
return out;
}
template <typename Func> VlQueue<IData> find_index(Func with_func) const {
VlQueue<IData> out;
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) out.push_back(index);
++index;
}
return out;
}
template <typename Func> VlQueue find_first(Func with_func) const {
// Can't use std::find_if as need index number
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) return VlQueue::cons(i);
++index;
}
return VlQueue{};
}
template <typename Func> VlQueue<IData> find_first_index(Func with_func) const {
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) return VlQueue<IData>::cons(index);
++index;
}
return VlQueue<IData>{};
}
template <typename Func> VlQueue find_last(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
if (with_func(index, *it)) return VlQueue::cons(*it);
--index;
}
return VlQueue{};
}
template <typename Func> VlQueue<IData> find_last_index(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
if (with_func(index, *it)) return VlQueue<IData>::cons(index);
--index;
}
return VlQueue<IData>{};
}
// Reduction operators
VlQueue min() const {
if (m_deque.empty()) return VlQueue{};
const auto it = std::min_element(m_deque.begin(), m_deque.end());
return VlQueue::cons(*it);
}
VlQueue max() const {
if (m_deque.empty()) return VlQueue{};
const auto it = std::max_element(m_deque.begin(), m_deque.end());
return VlQueue::cons(*it);
}
T_Value r_sum() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_deque) out += i;
return out;
}
template <typename Func> T_Value r_sum(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out += with_func(index++, i);
return out;
}
T_Value r_product() const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
T_Value out{*it};
++it;
for (; it != m_deque.end(); ++it) out *= *it;
return out;
}
template <typename Func> T_Value r_product(Func with_func) const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
IData index = 0;
T_Value out{with_func(index, *it)};
++it;
++index;
for (; it != m_deque.end(); ++it) out *= with_func(index++, *it);
return out;
}
T_Value r_and() const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
T_Value out{*it};
++it;
for (; it != m_deque.end(); ++it) out &= *it;
return out;
}
template <typename Func> T_Value r_and(Func with_func) const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
IData index = 0;
T_Value out{with_func(index, *it)};
++it;
++index;
for (; it != m_deque.end(); ++it) out &= with_func(index, *it);
return out;
}
T_Value r_or() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_deque) out |= i;
return out;
}
template <typename Func> T_Value r_or(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out |= with_func(index++, i);
return out;
}
T_Value r_xor() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_deque) out ^= i;
return out;
}
template <typename Func> T_Value r_xor(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out ^= with_func(index++, i);
return out;
}
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
if (m_deque.empty()) return "'{}"; // No trailing space
std::string out = "'{";
std::string comma;
for (const auto& i : m_deque) {
out += comma + VL_TO_STRING(i);
comma = ", ";
}
return out + "} ";
}
};
template <class T_Value> std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
return obj.to_string();
}
//===================================================================
// Verilog associative array container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
//
template <class T_Key, class T_Value> class VlAssocArray final {
private:
// TYPES
using Map = std::map<T_Key, T_Value>;
public:
using const_iterator = typename Map::const_iterator;
private:
// MEMBERS
Map m_map; // State of the assoc array
T_Value m_defaultValue; // Default value
public:
// CONSTRUCTORS
// m_defaultValue isn't defaulted. Caller's constructor must do it.
VlAssocArray() = default;
~VlAssocArray() = default;
VlAssocArray(const VlAssocArray&) = default;
VlAssocArray(VlAssocArray&&) = default;
VlAssocArray& operator=(const VlAssocArray&) = default;
VlAssocArray& operator=(VlAssocArray&&) = default;
// METHODS
T_Value& atDefault() { return m_defaultValue; }
const T_Value& atDefault() const { return m_defaultValue; }
// Size of array. Verilog: function int size(), or int num()
int size() const { return m_map.size(); }
// Clear array. Verilog: function void delete([input index])
void clear() { m_map.clear(); }
void erase(const T_Key& index) { m_map.erase(index); }
// Return 0/1 if element exists. Verilog: function int exists(input index)
int exists(const T_Key& index) const { return m_map.find(index) != m_map.end(); }
// Return first element. Verilog: function int first(ref index);
int first(T_Key& indexr) const {
const auto it = m_map.cbegin();
if (it == m_map.end()) return 0;
indexr = it->first;
return 1;
}
// Return last element. Verilog: function int last(ref index)
int last(T_Key& indexr) const {
const auto it = m_map.crbegin();
if (it == m_map.rend()) return 0;
indexr = it->first;
return 1;
}
// Return next element. Verilog: function int next(ref index)
int next(T_Key& indexr) const {
auto it = m_map.find(indexr);
if (VL_UNLIKELY(it == m_map.end())) return 0;
++it;
if (VL_UNLIKELY(it == m_map.end())) return 0;
indexr = it->first;
return 1;
}
// Return prev element. Verilog: function int prev(ref index)
int prev(T_Key& indexr) const {
auto it = m_map.find(indexr);
if (VL_UNLIKELY(it == m_map.end())) return 0;
if (VL_UNLIKELY(it == m_map.begin())) return 0;
--it;
indexr = it->first;
return 1;
}
// Setting. Verilog: assoc[index] = v
// Can't just overload operator[] or provide a "at" reference to set,
// because we need to be able to insert only when the value is set
T_Value& at(const T_Key& index) {
const auto it = m_map.find(index);
if (it == m_map.end()) {
std::pair<typename Map::iterator, bool> pit = m_map.emplace(index, m_defaultValue);
return pit.first->second;
}
return it->second;
}
// Accessing. Verilog: v = assoc[index]
const T_Value& at(const T_Key& index) const {
const auto it = m_map.find(index);
if (it == m_map.end()) {
return m_defaultValue;
} else {
return it->second;
}
}
// Setting as a chained operation
VlAssocArray& set(const T_Key& index, const T_Value& value) {
at(index) = value;
return *this;
}
VlAssocArray& setDefault(const T_Value& value) {
atDefault() = value;
return *this;
}
// For save/restore
const_iterator begin() const { return m_map.begin(); }
const_iterator end() const { return m_map.end(); }
// Methods
VlQueue<T_Value> unique() const {
VlQueue<T_Value> out;
std::set<T_Value> saw;
for (const auto& i : m_map) {
auto it = saw.find(i.second);
if (it == saw.end()) {
saw.insert(it, i.second);
out.push_back(i.second);
}
}
return out;
}
VlQueue<T_Key> unique_index() const {
VlQueue<T_Key> out;
std::set<T_Key> saw;
for (const auto& i : m_map) {
auto it = saw.find(i.second);
if (it == saw.end()) {
saw.insert(it, i.second);
out.push_back(i.first);
}
}
return out;
}
template <typename Func> VlQueue<T_Value> find(Func with_func) const {
VlQueue<T_Value> out;
for (const auto& i : m_map)
if (with_func(i.first, i.second)) out.push_back(i.second);
return out;
}
template <typename Func> VlQueue<T_Key> find_index(Func with_func) const {
VlQueue<T_Key> out;
for (const auto& i : m_map)
if (with_func(i.first, i.second)) out.push_back(i.first);
return out;
}
template <typename Func> VlQueue<T_Value> find_first(Func with_func) const {
const auto it
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.end()) return VlQueue<T_Value>{};
return VlQueue<T_Value>::cons(it->second);
}
template <typename Func> VlQueue<T_Key> find_first_index(Func with_func) const {
const auto it
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.end()) return VlQueue<T_Value>{};
return VlQueue<T_Key>::cons(it->first);
}
template <typename Func> VlQueue<T_Value> find_last(Func with_func) const {
const auto it
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.rend()) return VlQueue<T_Value>{};
return VlQueue<T_Value>::cons(it->second);
}
template <typename Func> VlQueue<T_Key> find_last_index(Func with_func) const {
const auto it
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.rend()) return VlQueue<T_Value>{};
return VlQueue<T_Key>::cons(it->first);
}
// Reduction operators
VlQueue<T_Value> min() const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::min_element(
m_map.begin(), m_map.end(),
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return a.second < b.second;
});
return VlQueue<T_Value>::cons(it->second);
}
VlQueue<T_Value> max() const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::max_element(
m_map.begin(), m_map.end(),
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return a.second < b.second;
});
return VlQueue<T_Value>::cons(it->second);
}
T_Value r_sum() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out += i.second;
return out;
}
template <typename Func> T_Value r_sum(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out += with_func(i.first, i.second);
return out;
}
T_Value r_product() const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{it->second};
++it;
for (; it != m_map.end(); ++it) out *= it->second;
return out;
}
template <typename Func> T_Value r_product(Func with_func) const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{with_func(it->first, it->second)};
++it;
for (; it != m_map.end(); ++it) out *= with_func(it->first, it->second);
return out;
}
T_Value r_and() const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{it->second};
++it;
for (; it != m_map.end(); ++it) out &= it->second;
return out;
}
template <typename Func> T_Value r_and(Func with_func) const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{with_func(it->first, it->second)};
++it;
for (; it != m_map.end(); ++it) out &= with_func(it->first, it->second);
return out;
}
T_Value r_or() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out |= i.second;
return out;
}
template <typename Func> T_Value r_or(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out |= with_func(i.first, i.second);
return out;
}
T_Value r_xor() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out ^= i.second;
return out;
}
template <typename Func> T_Value r_xor(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out ^= with_func(i.first, i.second);
return out;
}
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
if (m_map.empty()) return "'{}"; // No trailing space
std::string out = "'{";
std::string comma;
for (const auto& i : m_map) {
out += comma + VL_TO_STRING(i.first) + ":" + VL_TO_STRING(i.second);
comma = ", ";
}
// Default not printed - maybe random init data
return out + "} ";
}
};
template <class T_Key, class T_Value>
std::string VL_TO_STRING(const VlAssocArray<T_Key, T_Value>& obj) {
return obj.to_string();
}
template <class T_Key, class T_Value>
void VL_READMEM_N(bool hex, int bits, const std::string& filename,
VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
VlReadMem rmem{hex, bits, filename, start, end};
if (VL_UNLIKELY(!rmem.isOpen())) return;
while (true) {
QData addr;
std::string data;
if (rmem.get(addr /*ref*/, data /*ref*/)) {
rmem.setData(&(obj.at(addr)), data);
} else {
break;
}
}
}
template <class T_Key, class T_Value>
void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
const VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
VlWriteMem wmem{hex, bits, filename, start, end};
if (VL_UNLIKELY(!wmem.isOpen())) return;
for (const auto& i : obj) {
const QData addr = i.first;
if (addr >= start && addr <= end) wmem.print(addr, true, &(i.second));
}
}
//===================================================================
/// Verilog unpacked array container
/// For when a standard C++[] array is not sufficient, e.g. an
/// array under a queue, or methods operating on the array.
///
/// A 'struct' as we want this to be an aggregate type that allows
/// static aggregate initialization. Consider data members private.
///
/// This class may get exposed to a Verilated Model's top I/O, if the top
/// IO has an unpacked array.
template <class T_Value, std::size_t T_Depth> struct VlUnpacked final {
// MEMBERS
// This should be the only data member, otherwise generated static initializers need updating
T_Value m_storage[T_Depth]; // Contents of the unpacked array
// CONSTRUCTORS
// Default constructors and destructor are used. Note however that C++20 requires that
// aggregate types do not have a user declared constructor, not even an explicitly defaulted
// one.
// OPERATOR METHODS
// Default copy assignment operators are used.
// METHODS
// Raw access
WData* data() { return &m_storage[0]; }
const WData* data() const { return &m_storage[0]; }
T_Value& operator[](size_t index) { return m_storage[index]; };
const T_Value& operator[](size_t index) const { return m_storage[index]; };
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
std::string out = "'{";
std::string comma;
for (int i = 0; i < T_Depth; ++i) {
out += comma + VL_TO_STRING(m_storage[i]);
comma = ", ";
}
return out + "} ";
}
};
template <class T_Value, std::size_t T_Depth>
std::string VL_TO_STRING(const VlUnpacked<T_Value, T_Depth>& obj) {
return obj.to_string();
}
//===================================================================
// Verilog class reference container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
#define VlClassRef std::shared_ptr
template <class T> // T typically of type VlClassRef<x>
inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
if (VL_UNLIKELY(!t)) Verilated::nullPointerError(filename, linenum);
return t;
}
template <typename T, typename U>
static inline bool VL_CAST_DYNAMIC(VlClassRef<T> in, VlClassRef<U>& outr) {
VlClassRef<U> casted = std::dynamic_pointer_cast<U>(in);
if (VL_LIKELY(casted)) {
outr = casted;
return true;
} else {
return false;
}
}
//======================================================================
#endif // Guard
+22 -21
View File
@@ -102,7 +102,7 @@ VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep) {
}
void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
if (isOpen()) return;
// Set member variables
@@ -120,7 +120,7 @@ void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
void VerilatedVcd::openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex) {
// Open next filename in concat sequence, mangle filename if
// incFilename is true.
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
openNextImp(incFilename);
}
@@ -195,11 +195,11 @@ void VerilatedVcd::makeNameMap() {
if (!hiername.empty() && hiername[0] == '\t') nullScope = true;
}
if (nullScope) {
NameMap* newmapp = new NameMap;
NameMap* const newmapp = new NameMap;
for (const auto& i : *m_namemapp) {
const std::string& hiername = i.first;
const std::string& decl = i.second;
std::string newname = std::string("top");
std::string newname{"top"};
if (hiername[0] != '\t') newname += ' ';
newname += hiername;
newmapp->emplace(newname, decl);
@@ -243,7 +243,7 @@ void VerilatedVcd::closeErr() {
void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
// This function is on the flush() call path
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
if (!isOpen()) return;
if (m_evcd) {
printStr("$vcdclose ");
@@ -257,7 +257,7 @@ void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
}
void VerilatedVcd::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
VerilatedTrace<VerilatedVcd>::flushBase();
bufferFlush();
}
@@ -271,8 +271,9 @@ void VerilatedVcd::printStr(const char* str) {
}
void VerilatedVcd::printQuad(vluint64_t n) {
char buf[100];
VL_SNPRINTF(buf, 100, "%" VL_PRI64 "u", n);
constexpr size_t LEN_STR_QUAD = 40;
char buf[LEN_STR_QUAD];
VL_SNPRINTF(buf, LEN_STR_QUAD, "%" VL_PRI64 "u", n);
printStr(buf);
}
@@ -311,7 +312,7 @@ void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
// LCOV_EXCL_START
// write failed, presume error (perhaps out of disk space)
std::string msg
= std::string("VerilatedVcd::bufferFlush: ") + std::strerror(errno);
= std::string{"VerilatedVcd::bufferFlush: "} + std::strerror(errno);
VL_FATAL_MT("", 0, "", msg.c_str());
closeErr();
break;
@@ -331,7 +332,7 @@ char* VerilatedVcd::writeCode(char* writep, vluint32_t code) {
*writep++ = static_cast<char>('!' + code % 94);
code /= 94;
while (code) {
code--;
--code;
*writep++ = static_cast<char>('!' + code % 94);
code /= 94;
}
@@ -355,9 +356,9 @@ void VerilatedVcd::dumpHeader() {
const time_t tick = time(nullptr);
tm ticktm;
VL_LOCALTIME_R(&tick, &ticktm);
constexpr int bufsize = 50;
char buf[bufsize];
std::strftime(buf, bufsize, "%c", &ticktm);
constexpr size_t LEN_BUF = 50;
char buf[LEN_BUF];
std::strftime(buf, LEN_BUF, "%c", &ticktm);
printStr(buf);
}
printStr(" $end\n");
@@ -393,8 +394,8 @@ void VerilatedVcd::dumpHeader() {
// Skip common prefix, it must break at a space or tab
for (; *np && (*np == *lp); np++, lp++) {}
while (np != hiername && *np && *np != ' ' && *np != '\t') {
np--;
lp--;
--np;
--lp;
}
// printf("hier %s\n lp=%s\n np=%s\n",hiername,lp,np);
@@ -430,9 +431,9 @@ void VerilatedVcd::dumpHeader() {
for (; *np && *np != ' ' && *np != '\t'; np++) {
if (*np == '[') {
printStr("(");
printStr("[");
} else if (*np == ']') {
printStr(")");
printStr("]");
} else if (!(*np & '\x80')) {
*m_writep++ = *np;
}
@@ -531,7 +532,7 @@ void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep,
decl += " ";
decl += basename;
if (array) {
VL_SNPRINTF(buf, bufsize, "(%d)", arraynum);
VL_SNPRINTF(buf, bufsize, "[%d]", arraynum);
decl += buf;
hiername += buf;
}
@@ -896,9 +897,9 @@ void vcdTestMain(const char* filenamep) {
VerilatedVcdC* vcdp = new VerilatedVcdC;
vcdp->evcd(true);
vcdp->set_time_unit("1ms");
vcdp->set_time_unit(std::string("1ms"));
vcdp->set_time_unit(std::string{"1ms"});
vcdp->set_time_resolution("1ns");
vcdp->set_time_resolution(std::string("1ns"));
vcdp->set_time_resolution(std::string{"1ns"});
vcdp->spTrace()->addInitCb(&vcdInit, 0);
vcdp->spTrace()->addFullCb(&vcdFull, 0);
vcdp->spTrace()->addChgCb(&vcdChange, 0);
@@ -927,7 +928,7 @@ void vcdTestMain(const char* filenamep) {
vcdp->dump(++timestamp);
vcdp->dump(++timestamp);
# ifdef VERILATED_VCD_TEST_64BIT
vluint64_t bytesPerDump = 15ULL;
const vluint64_t bytesPerDump = 15ULL;
for (vluint64_t i = 0; i < ((1ULL << 32) / bytesPerDump); i++) {
v1 = i;
vcdp->dump(++timestamp);
+2 -2
View File
@@ -44,9 +44,9 @@ public:
// We want to avoid a depreciated warning, but still be back compatible.
// Turning off the message just for this still results in an
// annoying "to turn off" message.
const sc_time t1sec(1, SC_SEC);
const sc_time t1sec{1, SC_SEC};
if (t1sec.to_default_time_units() != 0) {
const sc_time tunits(1.0 / t1sec.to_default_time_units(), SC_SEC);
const sc_time tunits{1.0 / t1sec.to_default_time_units(), SC_SEC};
spTrace()->set_time_unit(tunits.to_string());
}
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
+88 -82
View File
@@ -80,7 +80,7 @@ public:
static const size_t chunk = 96;
if (VL_UNCOVERABLE(size > chunk)) VL_FATAL_MT(__FILE__, __LINE__, "", "increase chunk");
if (VL_LIKELY(t_freeHead)) {
vluint8_t* newp = t_freeHead;
vluint8_t* const newp = t_freeHead;
t_freeHead = *(reinterpret_cast<vluint8_t**>(newp));
*(reinterpret_cast<vluint32_t*>(newp)) = activeMagic();
return newp + 8;
@@ -91,7 +91,7 @@ public:
return newp + 8;
}
static void operator delete(void* obj, size_t /*size*/)VL_MT_SAFE {
vluint8_t* oldp = (static_cast<vluint8_t*>(obj)) - 8;
vluint8_t* const oldp = (static_cast<vluint8_t*>(obj)) - 8;
if (VL_UNLIKELY(*(reinterpret_cast<vluint32_t*>(oldp)) != activeMagic())) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"vpi_release_handle() called on same object twice, or on non-Verilator "
@@ -128,7 +128,7 @@ class VerilatedVpioTimedCb final : public VerilatedVpio {
public:
VerilatedVpioTimedCb(vluint64_t id, QData time)
: m_id(id)
: m_id{id}
, m_time{time} {}
virtual ~VerilatedVpioTimedCb() override = default;
static VerilatedVpioTimedCb* castp(vpiHandle h) {
@@ -147,7 +147,7 @@ class VerilatedVpioReasonCb final : public VerilatedVpio {
public:
// cppcheck-suppress uninitVar // m_value
VerilatedVpioReasonCb(vluint64_t id, PLI_INT32 reason)
: m_id(id)
: m_id{id}
, m_reason{reason} {}
virtual ~VerilatedVpioReasonCb() override = default;
static VerilatedVpioReasonCb* castp(vpiHandle h) {
@@ -200,7 +200,7 @@ public:
virtual const char* name() const override { return m_varp->name(); }
virtual const char* fullname() const override {
static VL_THREAD_LOCAL std::string t_out;
t_out = std::string(m_scopep->name()) + "." + name();
t_out = std::string{m_scopep->name()} + "." + name();
return t_out.c_str();
}
};
@@ -208,7 +208,7 @@ public:
class VerilatedVpioParam final : public VerilatedVpioVarBase {
public:
VerilatedVpioParam(const VerilatedVar* varp, const VerilatedScope* scopep)
: VerilatedVpioVarBase(varp, scopep) {}
: VerilatedVpioVarBase{varp, scopep} {}
virtual ~VerilatedVpioParam() override = default;
static VerilatedVpioParam* castp(vpiHandle h) {
@@ -252,7 +252,7 @@ public:
return nullptr;
}
m_done = true;
return ((new VerilatedVpioRange(m_range))->castVpiHandle());
return ((new VerilatedVpioRange{m_range})->castVpiHandle());
}
};
@@ -286,13 +286,13 @@ protected:
public:
VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep)
: VerilatedVpioVarBase(varp, scopep) {
: VerilatedVpioVarBase{varp, scopep} {
m_mask.u32 = VL_MASK_I(varp->packed().elements());
m_entSize = varp->entSize();
m_varDatap = varp->datap();
}
explicit VerilatedVpioVar(const VerilatedVpioVar* varp)
: VerilatedVpioVarBase(varp) {
: VerilatedVpioVarBase{varp} {
if (varp) {
m_mask.u32 = varp->m_mask.u32;
m_entSize = varp->m_entSize;
@@ -343,9 +343,10 @@ public:
virtual const VerilatedRange* rangep() const override { return &(varp()->packed()); }
virtual const char* fullname() const override {
static VL_THREAD_LOCAL std::string t_out;
char num[25];
VL_SNPRINTF(num, 25, "%d", m_index);
t_out = std::string(scopep()->name()) + "." + name() + "[" + num + "]";
constexpr size_t LEN_MAX_INDEX = 25;
char num[LEN_MAX_INDEX];
VL_SNPRINTF(num, LEN_MAX_INDEX, "%d", m_index);
t_out = std::string{scopep()->name()} + "." + name() + "[" + num + "]";
return t_out.c_str();
}
};
@@ -379,7 +380,7 @@ public:
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
}
return ((new VerilatedVpioVar(&(m_it->second), m_scopep))->castVpiHandle());
return ((new VerilatedVpioVar{&(m_it->second), m_scopep})->castVpiHandle());
}
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr; // End of list - only one deep
@@ -457,7 +458,7 @@ public:
return nullptr;
}
const VerilatedScope* modp = *m_it++;
return (new VerilatedVpioModule(modp))->castVpiHandle();
return (new VerilatedVpioModule{modp})->castVpiHandle();
}
};
@@ -475,16 +476,16 @@ class VerilatedVpiCbHolder final {
public:
// cppcheck-suppress uninitVar // m_value
VerilatedVpiCbHolder(vluint64_t id, const s_cb_data* cbDatap, const VerilatedVpioVar* varop)
: m_id(id)
, m_cbData(*cbDatap)
, m_varo(varop) {
: m_id{id}
, m_cbData{*cbDatap}
, m_varo{varop} {
m_value.format = cbDatap->value ? cbDatap->value->format : vpiSuppressVal;
m_cbData.value = &m_value;
if (varop) {
m_cbData.obj = m_varo.castVpiHandle();
m_varo.createPrevDatap();
} else {
m_cbData.obj = NULL;
m_cbData.obj = nullptr;
}
}
~VerilatedVpiCbHolder() = default;
@@ -626,16 +627,18 @@ public:
continue;
}
VerilatedVpiCbHolder& ho = *it++;
if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(ho.cb_datap()->obj)) {
void* newDatap = varop->varDatap();
void* prevDatap = varop->prevDatap(); // Was malloced when we added the callback
if (VerilatedVpioVar* const varop = VerilatedVpioVar::castp(ho.cb_datap()->obj)) {
void* const newDatap = varop->varDatap();
void* const prevDatap
= varop->prevDatap(); // Was malloced when we added the callback
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
varop->fullname(), *((CData*)newDatap),
*((CData*)prevDatap), newDatap, prevDatap););
varop->fullname(), *(static_cast<CData*>(newDatap)),
*(static_cast<CData*>(prevDatap)), newDatap,
prevDatap););
if (std::memcmp(prevDatap, newDatap, varop->entSize()) != 0) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" VL_PRI64
"d %s v[0]=%d\n",
ho.id(), varop->fullname(), *((CData*)newDatap)););
VL_DEBUG_IF_PLI(
VL_DBG_MSGF("- vpi: value_callback %" VL_PRI64 "d %s v[0]=%d\n", ho.id(),
varop->fullname(), *(static_cast<CData*>(newDatap))););
update.insert(varop);
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
(ho.cb_rtnp())(ho.cb_datap());
@@ -706,7 +709,8 @@ public:
va_end(args);
m_errorInfo.state = vpiPLI;
t_filehold = file;
setError((PLI_BYTE8*)m_buff, nullptr, const_cast<PLI_BYTE8*>(t_filehold.c_str()), line);
setError(static_cast<PLI_BYTE8*>(m_buff), nullptr,
const_cast<PLI_BYTE8*>(t_filehold.c_str()), line);
}
p_vpi_error_info getError() {
if (m_flag) return &m_errorInfo;
@@ -758,7 +762,7 @@ PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() {
VerilatedVpiError* VerilatedVpiImp::error_info() VL_MT_UNSAFE_ONE {
VerilatedVpiImp::assertOneCheck();
if (VL_UNLIKELY(!s().m_errorInfop)) s().m_errorInfop = new VerilatedVpiError();
if (VL_UNLIKELY(!s().m_errorInfop)) s().m_errorInfop = new VerilatedVpiError;
return s().m_errorInfop;
}
@@ -1108,13 +1112,13 @@ const char* VerilatedVpiError::strFromVpiProp(PLI_INT32 vpiVal) VL_MT_SAFE {
#define SELF_CHECK_RESULT_CSTR(got, exp) \
if (0 != std::strcmp((got), (exp))) { \
std::string msg \
= std::string("%Error: ") + "GOT = '" + got + "'" + " EXP = '" + exp + "'"; \
= std::string{"%Error: "} + "GOT = '" + (got) + "'" + " EXP = '" + (exp) + "'"; \
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); \
}
#define SELF_CHECK_ENUM_STR(fn, enumn) \
do { \
const char* strVal = VerilatedVpiError::fn(enumn); \
const char* const strVal = VerilatedVpiError::fn(enumn); \
SELF_CHECK_RESULT_CSTR(strVal, #enumn); \
} while (0)
@@ -1358,7 +1362,7 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope);
std::string scopeAndName = namep;
if (voScopep) {
scopeAndName = std::string(voScopep->fullname()) + "." + namep;
scopeAndName = std::string{voScopep->fullname()} + "." + namep;
namep = const_cast<PLI_BYTE8*>(scopeAndName.c_str());
}
{
@@ -1366,9 +1370,9 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
scopep = Verilated::threadContextp()->scopeFind(namep);
if (scopep) { // Whole thing found as a scope
if (scopep->type() == VerilatedScope::SCOPE_MODULE) {
return (new VerilatedVpioModule(scopep))->castVpiHandle();
return (new VerilatedVpioModule{scopep})->castVpiHandle();
} else {
return (new VerilatedVpioScope(scopep))->castVpiHandle();
return (new VerilatedVpioScope{scopep})->castVpiHandle();
}
}
const char* baseNamep = scopeAndName.c_str();
@@ -1376,7 +1380,8 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
const char* const dotp = std::strrchr(namep, '.');
if (VL_LIKELY(dotp)) {
baseNamep = dotp + 1;
scopename = std::string(namep, dotp - namep);
const size_t len = dotp - namep;
scopename = std::string{namep, len};
}
if (scopename.find('.') == std::string::npos) {
@@ -1393,9 +1398,9 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
if (!varp) return nullptr;
if (varp->isParam()) {
return (new VerilatedVpioParam(varp, scopep))->castVpiHandle();
return (new VerilatedVpioParam{varp, scopep})->castVpiHandle();
} else {
return (new VerilatedVpioVar(varp, scopep))->castVpiHandle();
return (new VerilatedVpioVar{varp, scopep})->castVpiHandle();
}
}
@@ -1405,7 +1410,7 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) {
VerilatedVpiImp::assertOneCheck();
VL_VPI_ERROR_RESET_();
// Memory words are not indexable
VerilatedVpioMemoryWord* vop = VerilatedVpioMemoryWord::castp(object);
VerilatedVpioMemoryWord* const vop = VerilatedVpioMemoryWord::castp(object);
if (VL_UNLIKELY(vop)) return nullptr;
const VerilatedVpioVar* const varop = VerilatedVpioVar::castp(object);
if (VL_LIKELY(varop)) {
@@ -1414,15 +1419,15 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) {
if (VL_UNLIKELY(indx > varop->varp()->unpacked().left()
|| indx < varop->varp()->unpacked().right()))
return nullptr;
return (new VerilatedVpioMemoryWord(varop->varp(), varop->scopep(), indx,
indx - varop->varp()->unpacked().right()))
return (new VerilatedVpioMemoryWord{varop->varp(), varop->scopep(), indx,
indx - varop->varp()->unpacked().right()})
->castVpiHandle();
}
if (VL_UNLIKELY(indx < varop->varp()->unpacked().left()
|| indx > varop->varp()->unpacked().right()))
return nullptr;
return (new VerilatedVpioMemoryWord(varop->varp(), varop->scopep(), indx,
indx - varop->varp()->unpacked().left()))
return (new VerilatedVpioMemoryWord{varop->varp(), varop->scopep(), indx,
indx - varop->varp()->unpacked().left()})
->castVpiHandle();
}
VL_VPI_INTERNAL_(__FILE__, __LINE__, "%s : can't resolve handle", __func__);
@@ -1437,12 +1442,12 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
VL_VPI_ERROR_RESET_();
switch (type) {
case vpiLeftRange: {
if (VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object)) {
if (VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object)) {
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
return (new VerilatedVpioConst(vop->rangep()->left()))->castVpiHandle();
} else if (VerilatedVpioRange* vop = VerilatedVpioRange::castp(object)) {
return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle();
} else if (VerilatedVpioRange* const vop = VerilatedVpioRange::castp(object)) {
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
return (new VerilatedVpioConst(vop->rangep()->left()))->castVpiHandle();
return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle();
}
VL_VPI_WARNING_(__FILE__, __LINE__,
"%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
@@ -1450,12 +1455,12 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
return nullptr;
}
case vpiRightRange: {
if (VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object)) {
if (VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object)) {
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
return (new VerilatedVpioConst(vop->rangep()->right()))->castVpiHandle();
} else if (VerilatedVpioRange* vop = VerilatedVpioRange::castp(object)) {
return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle();
} else if (VerilatedVpioRange* const vop = VerilatedVpioRange::castp(object)) {
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
return (new VerilatedVpioConst(vop->rangep()->right()))->castVpiHandle();
return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle();
}
VL_VPI_WARNING_(__FILE__, __LINE__,
"%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
@@ -1463,19 +1468,20 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
return nullptr;
}
case vpiIndex: {
VerilatedVpioVar* vop = VerilatedVpioVar::castp(object);
VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
return (new VerilatedVpioConst(vop->index()))->castVpiHandle();
const vlsint32_t val = vop->index();
return (new VerilatedVpioConst{val})->castVpiHandle();
}
case vpiScope: {
VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object);
VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
return (new VerilatedVpioScope(vop->scopep()))->castVpiHandle();
return (new VerilatedVpioScope{vop->scopep()})->castVpiHandle();
}
case vpiParent: {
VerilatedVpioMemoryWord* vop = VerilatedVpioMemoryWord::castp(object);
VerilatedVpioMemoryWord* const vop = VerilatedVpioMemoryWord::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
return (new VerilatedVpioVar(vop->varp(), vop->scopep()))->castVpiHandle();
return (new VerilatedVpioVar{vop->varp(), vop->scopep()})->castVpiHandle();
}
default:
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
@@ -1505,7 +1511,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
VerilatedVpiError::strFromVpiMethod(type), vop->fullname(),
vop->varp()->dims());
}
return (new VerilatedVpioMemoryWordIter(object, vop->varp()))->castVpiHandle();
return (new VerilatedVpioMemoryWordIter{object, vop->varp()})->castVpiHandle();
}
case vpiRange: {
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
@@ -1518,12 +1524,12 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
VerilatedVpiError::strFromVpiMethod(type), vop->fullname(),
vop->varp()->dims());
}
return ((new VerilatedVpioRangeIter(vop->rangep()))->castVpiHandle());
return ((new VerilatedVpioRangeIter{vop->rangep()})->castVpiHandle());
}
case vpiReg: {
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
return ((new VerilatedVpioVarIter(vop->scopep()))->castVpiHandle());
return ((new VerilatedVpioVarIter{vop->scopep()})->castVpiHandle());
}
case vpiModule: {
const VerilatedVpioModule* const vop = VerilatedVpioModule::castp(object);
@@ -1531,7 +1537,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
const VerilatedScope* const modp = vop ? vop->scopep() : nullptr;
const auto it = vlstd::as_const(map)->find(const_cast<VerilatedScope*>(modp));
if (it == map->end()) return nullptr;
return ((new VerilatedVpioModuleIter(it->second))->castVpiHandle());
return ((new VerilatedVpioModuleIter{it->second})->castVpiHandle());
}
default:
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
@@ -1543,7 +1549,7 @@ vpiHandle vpi_scan(vpiHandle object) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_scan %p\n", object););
VerilatedVpiImp::assertOneCheck();
VL_VPI_ERROR_RESET_();
VerilatedVpio* vop = VerilatedVpio::castp(object);
VerilatedVpio* const vop = VerilatedVpio::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
return vop->dovpi_scan();
}
@@ -1689,7 +1695,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
const char* fullname) {
if (!vl_check_format(varp, valuep, fullname, true)) return;
// Maximum required size is for binary string, one byte per bit plus null termination
static VL_THREAD_LOCAL char t_outStr[1 + VL_MULS_MAX_WORDS * 32];
static VL_THREAD_LOCAL char t_outStr[VL_VALUE_STRING_MAX_WORDS * VL_EDATASIZE + 1];
// cppcheck-suppress variableScope
static VL_THREAD_LOCAL int t_outStrSz = sizeof(t_outStr) - 1;
// We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal
@@ -1697,7 +1703,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
if (valuep->format == vpiVectorVal) {
// Vector pointer must come from our memory pool
// It only needs to persist until the next vpi_get_value
static VL_THREAD_LOCAL t_vpi_vecval t_out[VL_MULS_MAX_WORDS * 2];
static VL_THREAD_LOCAL t_vpi_vecval t_out[VL_VALUE_STRING_MAX_WORDS * 2];
valuep->value.vector = t_out;
if (varp->vltype() == VLVT_UINT8) {
t_out[0].aval = *(reinterpret_cast<CData*>(varDatap));
@@ -1720,10 +1726,10 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
return;
} else if (varp->vltype() == VLVT_WDATA) {
int words = VL_WORDS_I(varp->packed().elements());
if (VL_UNCOVERABLE(words >= VL_MULS_MAX_WORDS)) {
VL_FATAL_MT(
__FILE__, __LINE__, "",
"vpi_get_value with more than VL_MULS_MAX_WORDS; increase and recompile");
if (VL_UNCOVERABLE(words >= VL_VALUE_STRING_MAX_WORDS)) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"vpi_get_value with more than VL_VALUE_STRING_MAX_WORDS; increase and "
"recompile");
}
const WDataInP datap = (reinterpret_cast<EData*>(varDatap));
for (int i = 0; i < words; ++i) {
@@ -1743,9 +1749,9 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)",
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
VL_MULS_MAX_WORDS, bits);
VL_VALUE_STRING_MAX_WORDS, bits);
}
for (i = 0; i < bits; ++i) {
char val = (datap[i >> 3] >> (i & 7)) & 1;
@@ -1764,9 +1770,9 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)",
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
VL_MULS_MAX_WORDS, chars);
VL_VALUE_STRING_MAX_WORDS, chars);
chars = t_outStrSz;
}
for (i = 0; i < chars; ++i) {
@@ -1822,9 +1828,9 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)",
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
VL_MULS_MAX_WORDS, chars);
VL_VALUE_STRING_MAX_WORDS, chars);
chars = t_outStrSz;
}
for (i = 0; i < chars; ++i) {
@@ -1853,12 +1859,12 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
int i;
if (bytes > t_outStrSz) {
// limit maximum size of output to size of buffer to prevent overrun.
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname,
t_outStrSz, VL_MULS_MAX_WORDS, bytes);
VL_VPI_WARNING_(__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than "
"required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format),
fullname, t_outStrSz, VL_VALUE_STRING_MAX_WORDS, bytes);
bytes = t_outStrSz;
}
for (i = 0; i < bytes; ++i) {
@@ -1893,13 +1899,13 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) {
VL_VPI_ERROR_RESET_();
if (VL_UNLIKELY(!valuep)) return;
if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) {
if (VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) {
vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname());
return;
} else if (const VerilatedVpioParam* const vop = VerilatedVpioParam::castp(object)) {
vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname());
return;
} else if (VerilatedVpioConst* vop = VerilatedVpioConst::castp(object)) {
} else if (VerilatedVpioConst* const vop = VerilatedVpioConst::castp(object)) {
if (valuep->format == vpiIntVal) {
valuep->value.integer = vop->num();
return;
@@ -2161,7 +2167,7 @@ void vpi_get_time(vpiHandle object, p_vpi_time time_p) {
return;
} else if (time_p->type == vpiScaledRealTime) {
double dtime = VL_TIME_D();
if (VerilatedVpioScope* vop = VerilatedVpioScope::castp(object)) {
if (VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object)) {
const int scalePow10
= Verilated::threadContextp()->timeprecision() - vop->scopep()->timeunit();
const double scale = vl_time_multiplier(scalePow10); // e.g. 0.0001
@@ -2271,7 +2277,7 @@ PLI_INT32 vpi_free_object(vpiHandle object) {
PLI_INT32 vpi_release_handle(vpiHandle object) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_release_handle %p\n", object););
VerilatedVpiImp::assertOneCheck();
VerilatedVpio* vop = VerilatedVpio::castp(object);
VerilatedVpio* const vop = VerilatedVpio::castp(object);
VL_VPI_ERROR_RESET_();
if (VL_UNLIKELY(!vop)) return 0;
VL_DO_DANGLING(delete vop, vop);
+8 -2
View File
@@ -421,7 +421,8 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
// Verilated function size macros
#define VL_MULS_MAX_WORDS 16 ///< Max size in words of MULS operation
#define VL_TO_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation
#define VL_VALUE_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation
#define VL_VALUE_STRING_MAX_CHARS (VL_VALUE_STRING_MAX_WORDS * VL_EDATASIZE / VL_BYTESIZE)
//=========================================================================
// Base macros
@@ -471,7 +472,12 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
(val) = ((vluint64_t)lo) | (((vluint64_t)hi) << 32); \
}
#elif defined(__aarch64__)
# define VL_RDTSC(val) asm volatile("mrs %[rt],PMCCNTR_EL0" : [rt] "=r"(val));
// 1 GHz virtual system timer on SBSA level 5 compliant systems, else often 100 MHz
# define VL_RDTSC(val) \
{ \
asm volatile("isb" : : : "memory"); \
asm volatile("mrs %[rt],CNTVCT_EL0" : [rt] "=r"(val)); \
}
#else
// We just silently ignore unknown OSes, as only leads to missing statistics
# define VL_RDTSC(val) (val) = 0;
+2 -2
View File
@@ -66,11 +66,11 @@ def cwrite(filename):
fh.write(" auto* gp = &m_graph;\n")
for ver in sorted(Vertexes, key=lambda ver: ver['num']):
fh.write(
" auto* %s = new V3GraphTestVertex(gp, \"%s\"); if (%s) {}\n"
" auto* %s = new V3GraphTestVertex{gp, \"%s\"}; if (%s) {}\n"
% (ver['name'], ver['name'], ver['name']))
fh.write("\n")
for edge in Edges:
fh.write(" new V3GraphEdge(gp, %s, %s, %s, %s);\n" %
fh.write(" new V3GraphEdge{gp, %s, %s, %s, %s};\n" %
(edge['from'], edge['to'], edge['weight'],
"true" if edge['cutable'] else "false"))
fh.write("}\n")
+4 -1
View File
@@ -169,6 +169,7 @@ RAW_OBJS = \
V3Clean.o \
V3Clock.o \
V3Combine.o \
V3Common.o \
V3Config.o \
V3Const__gen.o \
V3Coverage.o \
@@ -179,10 +180,11 @@ RAW_OBJS = \
V3DepthBlock.o \
V3Descope.o \
V3DupFinder.o \
V3EmitC.o \
V3EmitCBase.o \
V3EmitCConstPool.o \
V3EmitCFunc.o \
V3EmitCHeaders.o \
V3EmitCImp.o \
V3EmitCInlines.o \
V3EmitCMain.o \
V3EmitCMake.o \
@@ -254,6 +256,7 @@ RAW_OBJS = \
V3Undriven.o \
V3Unknown.o \
V3Unroll.o \
V3VariableOrder.o \
V3Waiver.o \
V3Width.o \
V3WidthSel.o \
+17 -17
View File
@@ -64,11 +64,11 @@ private:
public:
LatchDetectGraphVertex(V3Graph* graphp, const string& name, VertexType type = VT_BLOCK)
: V3GraphVertex(graphp)
, m_name(name)
, m_type(type) {}
virtual string name() const { return m_name + " " + typestr(); }
virtual string dotColor() const { return user() ? "green" : "black"; }
: V3GraphVertex{graphp}
, m_name{name}
, m_type{type} {}
virtual string name() const override { return m_name + " " + typestr(); }
virtual string dotColor() const override { return user() ? "green" : "black"; }
virtual int type() const { return m_type; }
};
@@ -120,7 +120,7 @@ protected:
public:
LatchDetectGraph() { clear(); }
~LatchDetectGraph() { clear(); }
virtual ~LatchDetectGraph() override { clear(); }
// ACCESSORS
LatchDetectGraphVertex* currentp() { return m_curVertexp; }
void currentp(LatchDetectGraphVertex* vertex) { m_curVertexp = vertex; }
@@ -142,17 +142,17 @@ public:
// Add a new control path and connect it to its parent
LatchDetectGraphVertex* addPathVertex(LatchDetectGraphVertex* parent, const string& name,
bool branch = false) {
m_curVertexp = new LatchDetectGraphVertex(this, name,
m_curVertexp = new LatchDetectGraphVertex{this, name,
branch ? LatchDetectGraphVertex::VT_BRANCH
: LatchDetectGraphVertex::VT_BLOCK);
new V3GraphEdge(this, parent, m_curVertexp, 1);
: LatchDetectGraphVertex::VT_BLOCK};
new V3GraphEdge{this, parent, m_curVertexp, 1};
return m_curVertexp;
}
// Add a new output variable vertex and store a pointer to it in the user1 field of the
// variables AstNode
LatchDetectGraphVertex* addOutputVertex(AstVarRef* nodep) {
LatchDetectGraphVertex* outVertexp
= new LatchDetectGraphVertex(this, nodep->name(), LatchDetectGraphVertex::VT_OUTPUT);
= new LatchDetectGraphVertex{this, nodep->name(), LatchDetectGraphVertex::VT_OUTPUT};
nodep->varp()->user1p(outVertexp);
m_outputs.push_back(nodep);
return outVertexp;
@@ -437,7 +437,7 @@ private:
virtual void visit(AstInitial* nodep) override {
// Relink to IACTIVE, unless already under it
UINFO(4, " INITIAL " << nodep << endl);
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_INITIAL);
ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
AstActive* wantactivep = m_namer.getIActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
@@ -470,7 +470,7 @@ private:
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_INITIAL);
ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
if (!m_scopeFinalp) {
m_scopeFinalp = new AstCFunc(
nodep->fileline(), "_final_" + m_namer.scopep()->nameDotless(), m_namer.scopep());
@@ -540,14 +540,14 @@ private:
// Warn and/or convert any delayed assignments
if (combo && !sequent) {
ActiveLatchCheckVisitor latchvisitor(nodep, kwd);
ActiveLatchCheckVisitor latchvisitor{nodep, kwd};
if (kwd == VAlwaysKwd::ALWAYS_LATCH) {
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_LATCH);
ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_LATCH};
} else {
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_COMBO);
ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_COMBO};
}
} else if (!combo && sequent) {
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_SEQ);
ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_SEQ};
}
}
virtual void visit(AstAlways* nodep) override {
@@ -620,6 +620,6 @@ public:
void V3Active::activeAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ActiveVisitor visitor(nodep); } // Destruct before checking
{ ActiveVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("active", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+1 -1
View File
@@ -136,6 +136,6 @@ public:
void V3ActiveTop::activeTopAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ActiveTopVisitor visitor(nodep); } // Destruct before checking
{ ActiveTopVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("activetop", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+1 -1
View File
@@ -474,6 +474,6 @@ public:
void V3Assert::assertAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ AssertVisitor visitor(nodep); } // Destruct before checking
{ AssertVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("assert", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+1 -1
View File
@@ -205,6 +205,6 @@ public:
void V3AssertPre::assertPreAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ AssertPreVisitor visitor(nodep); } // Destruct before checking
{ AssertPreVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("assertpre", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+40 -35
View File
@@ -74,9 +74,10 @@ AstNode::AstNode(AstType t, FileLine* fl)
m_clonep = nullptr;
m_cloneCnt = 0;
// Attributes
m_didWidth = false;
m_doingWidth = false;
m_protect = true;
m_flags.didWidth = false;
m_flags.doingWidth = false;
m_flags.protect = true;
m_flags.unused = 0; // Initializing this avoids a read-modify-write on construction
m_user1u = VNUser(0);
m_user1Cnt = 0;
m_user2u = VNUser(0);
@@ -229,7 +230,8 @@ string AstNode::prettyTypeName() const {
//######################################################################
// Insertion
inline void AstNode::debugTreeChange(const char* prefix, int lineno, bool next){
inline void AstNode::debugTreeChange(const AstNode* nodep, const char* prefix, int lineno,
bool next){
#ifdef VL_DEBUG
// Called on all major tree changers.
// Only for use for those really nasty bugs relating to internals
@@ -253,8 +255,8 @@ inline void AstNode::debugTreeChange(const char* prefix, int lineno, bool next){
AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) {
// Add to m_nextp, returns this
UDEBUGONLY(UASSERT_OBJ(newp, nodep, "Null item passed to addNext"););
nodep->debugTreeChange("-addNextThs: ", __LINE__, false);
newp->debugTreeChange("-addNextNew: ", __LINE__, true);
debugTreeChange(nodep, "-addNextThs: ", __LINE__, false);
debugTreeChange(newp, "-addNextNew: ", __LINE__, true);
if (!nodep) { // verilog.y and lots of other places assume this
return newp;
} else {
@@ -284,7 +286,7 @@ AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) {
newp->editCountInc();
if (oldtailp->m_iterpp) *(oldtailp->m_iterpp) = newp; // Iterate on new item
}
nodep->debugTreeChange("-addNextOut:", __LINE__, true);
debugTreeChange(nodep, "-addNextOut:", __LINE__, true);
return nodep;
}
@@ -299,8 +301,8 @@ void AstNode::addNextHere(AstNode* newp) {
// New could be head of single node, or list
UASSERT(newp, "Null item passed to addNext");
UASSERT(!newp->backp(), "New node (back) already assigned?");
this->debugTreeChange("-addHereThs: ", __LINE__, false);
newp->debugTreeChange("-addHereNew: ", __LINE__, true);
debugTreeChange(this, "-addHereThs: ", __LINE__, false);
debugTreeChange(newp, "-addHereNew: ", __LINE__, true);
newp->editCountInc();
AstNode* addlastp = newp->m_headtailp; // Last node in list to be added
@@ -338,7 +340,7 @@ void AstNode::addNextHere(AstNode* newp) {
}
if (this->m_iterpp) *(this->m_iterpp) = newp; // Iterate on new item
this->debugTreeChange("-addHereOut: ", __LINE__, true);
debugTreeChange(this, "-addHereOut: ", __LINE__, true);
}
void AstNode::setOp1p(AstNode* newp) {
@@ -346,12 +348,12 @@ void AstNode::setOp1p(AstNode* newp) {
UDEBUGONLY(UASSERT_OBJ(!m_op1p, this, "Adding to non-empty, non-list op1"););
UDEBUGONLY(UASSERT_OBJ(!newp->m_backp, newp, "Adding already linked node"););
UDEBUGONLY(UASSERT_OBJ(!newp->m_nextp, newp, "Adding list to non-list op1"););
this->debugTreeChange("-setOp1pThs: ", __LINE__, false);
newp->debugTreeChange("-setOp1pNew: ", __LINE__, true);
debugTreeChange(this, "-setOp1pThs: ", __LINE__, false);
debugTreeChange(newp, "-setOp1pNew: ", __LINE__, true);
m_op1p = newp;
newp->editCountInc();
newp->m_backp = this;
this->debugTreeChange("-setOp1pOut: ", __LINE__, false);
debugTreeChange(this, "-setOp1pOut: ", __LINE__, false);
}
void AstNode::setOp2p(AstNode* newp) {
@@ -359,12 +361,12 @@ void AstNode::setOp2p(AstNode* newp) {
UDEBUGONLY(UASSERT_OBJ(!m_op2p, this, "Adding to non-empty, non-list op2"););
UDEBUGONLY(UASSERT_OBJ(!newp->m_backp, newp, "Adding already linked node"););
UDEBUGONLY(UASSERT_OBJ(!newp->m_nextp, newp, "Adding list to non-list op2"););
this->debugTreeChange("-setOp2pThs: ", __LINE__, false);
newp->debugTreeChange("-setOp2pNew: ", __LINE__, true);
debugTreeChange(this, "-setOp2pThs: ", __LINE__, false);
debugTreeChange(newp, "-setOp2pNew: ", __LINE__, true);
m_op2p = newp;
newp->editCountInc();
newp->m_backp = this;
this->debugTreeChange("-setOp2pOut: ", __LINE__, false);
debugTreeChange(this, "-setOp2pOut: ", __LINE__, false);
}
void AstNode::setOp3p(AstNode* newp) {
@@ -372,12 +374,12 @@ void AstNode::setOp3p(AstNode* newp) {
UDEBUGONLY(UASSERT_OBJ(!m_op3p, this, "Adding to non-empty, non-list op3"););
UDEBUGONLY(UASSERT_OBJ(!newp->m_backp, newp, "Adding already linked node"););
UDEBUGONLY(UASSERT_OBJ(!newp->m_nextp, newp, "Adding list to non-list op3"););
this->debugTreeChange("-setOp3pThs: ", __LINE__, false);
newp->debugTreeChange("-setOp3pNew: ", __LINE__, true);
debugTreeChange(this, "-setOp3pThs: ", __LINE__, false);
debugTreeChange(newp, "-setOp3pNew: ", __LINE__, true);
m_op3p = newp;
newp->editCountInc();
newp->m_backp = this;
this->debugTreeChange("-setOp3pOut: ", __LINE__, false);
debugTreeChange(this, "-setOp3pOut: ", __LINE__, false);
}
void AstNode::setOp4p(AstNode* newp) {
@@ -385,12 +387,12 @@ void AstNode::setOp4p(AstNode* newp) {
UDEBUGONLY(UASSERT_OBJ(!m_op4p, this, "Adding to non-empty, non-list op4"););
UDEBUGONLY(UASSERT_OBJ(!newp->m_backp, newp, "Adding already linked node"););
UDEBUGONLY(UASSERT_OBJ(!newp->m_nextp, newp, "Adding list to non-list op4"););
this->debugTreeChange("-setOp4pThs: ", __LINE__, false);
newp->debugTreeChange("-setOp4pNew: ", __LINE__, true);
debugTreeChange(this, "-setOp4pThs: ", __LINE__, false);
debugTreeChange(newp, "-setOp4pNew: ", __LINE__, true);
m_op4p = newp;
newp->editCountInc();
newp->m_backp = this;
this->debugTreeChange("-setOp4pOut: ", __LINE__, false);
debugTreeChange(this, "-setOp4pOut: ", __LINE__, false);
}
void AstNode::addOp1p(AstNode* newp) {
@@ -452,7 +454,7 @@ void AstNRelinker::dump(std::ostream& str) const {
}
AstNode* AstNode::unlinkFrBackWithNext(AstNRelinker* linkerp) {
this->debugTreeChange("-unlinkWNextThs: ", __LINE__, true);
debugTreeChange(this, "-unlinkWNextThs: ", __LINE__, true);
AstNode* oldp = this;
UASSERT(oldp->m_backp, "Node has no back, already unlinked?");
oldp->editCountInc();
@@ -507,12 +509,12 @@ AstNode* AstNode::unlinkFrBackWithNext(AstNRelinker* linkerp) {
// Iterator fixup
if (oldp->m_iterpp) *(oldp->m_iterpp) = nullptr;
oldp->m_iterpp = nullptr;
oldp->debugTreeChange("-unlinkWNextOut: ", __LINE__, true);
debugTreeChange(oldp, "-unlinkWNextOut: ", __LINE__, true);
return oldp;
}
AstNode* AstNode::unlinkFrBack(AstNRelinker* linkerp) {
this->debugTreeChange("-unlinkFrBkThs: ", __LINE__, true);
debugTreeChange(this, "-unlinkFrBkThs: ", __LINE__, true);
AstNode* oldp = this;
UASSERT(oldp->m_backp, "Node has no back, already unlinked?");
oldp->editCountInc();
@@ -571,7 +573,7 @@ AstNode* AstNode::unlinkFrBack(AstNRelinker* linkerp) {
oldp->m_backp = nullptr;
oldp->m_headtailp = this;
oldp->m_iterpp = nullptr;
oldp->debugTreeChange("-unlinkFrBkOut: ", __LINE__, true);
debugTreeChange(oldp, "-unlinkFrBkOut: ", __LINE__, true);
return oldp;
}
@@ -591,8 +593,8 @@ void AstNode::relink(AstNRelinker* linkerp) {
}
AstNode* backp = linkerp->m_backp;
this->debugTreeChange("-relinkNew: ", __LINE__, true);
backp->debugTreeChange("-relinkTre: ", __LINE__, true);
debugTreeChange(this, "-relinkNew: ", __LINE__, true);
debugTreeChange(backp, "-relinkTre: ", __LINE__, true);
switch (linkerp->m_chg) {
case AstNRelinker::RELINK_NEXT: backp->addNextHere(newp); break;
@@ -617,7 +619,7 @@ void AstNode::relink(AstNRelinker* linkerp) {
}
// Empty the linker so not used twice accidentally
linkerp->m_backp = nullptr;
this->debugTreeChange("-relinkOut: ", __LINE__, true);
debugTreeChange(this, "-relinkOut: ", __LINE__, true);
}
void AstNode::relinkOneLink(AstNode*& pointpr, // Ref to pointer that gets set to newp
@@ -699,7 +701,7 @@ AstNode* AstNode::cloneTreeIterList() {
}
AstNode* AstNode::cloneTree(bool cloneNextLink) {
this->debugTreeChange("-cloneThs: ", __LINE__, cloneNextLink);
debugTreeChange(this, "-cloneThs: ", __LINE__, cloneNextLink);
cloneClearTree();
AstNode* newp;
if (cloneNextLink && this->m_nextp) {
@@ -711,7 +713,7 @@ AstNode* AstNode::cloneTree(bool cloneNextLink) {
}
newp->m_backp = nullptr;
newp->cloneRelinkTree();
newp->debugTreeChange("-cloneOut: ", __LINE__, true);
debugTreeChange(newp, "-cloneOut: ", __LINE__, true);
return newp;
}
@@ -763,7 +765,7 @@ void AstNode::deleteTree() {
// deleteTree always deletes the next link, because you must have called
// unlinkFromBack or unlinkFromBackWithNext as appropriate before calling this.
UASSERT(!m_backp, "Delete called on node with backlink still set");
this->debugTreeChange("-delTree: ", __LINE__, true);
debugTreeChange(this, "-delTree: ", __LINE__, true);
this->editCountInc();
// MUST be depth first!
deleteTreeIter();
@@ -1123,7 +1125,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo
if (doDump) {
{ // Write log & close
UINFO(2, "Dumping " << filename << endl);
const std::unique_ptr<std::ofstream> logsp(V3File::new_ofstream(filename, append));
const std::unique_ptr<std::ofstream> logsp{V3File::new_ofstream(filename, append)};
if (logsp->fail()) v3fatal("Can't write " << filename);
*logsp << "Verilator Tree Dump (format 0x3900) from <e" << std::dec << editCountLast();
*logsp << "> to <e" << std::dec << editCountGbl() << ">\n";
@@ -1263,12 +1265,15 @@ AstNodeDType* AstNode::findBitRangeDType(const VNumRange& range, int widthMin,
AstBasicDType* AstNode::findInsertSameDType(AstBasicDType* nodep) {
return v3Global.rootp()->typeTablep()->findInsertSameDType(nodep);
}
AstNodeDType* AstNode::findVoidDType() const {
return v3Global.rootp()->typeTablep()->findVoidDType(fileline());
AstNodeDType* AstNode::findEmptyQueueDType() const {
return v3Global.rootp()->typeTablep()->findEmptyQueueDType(fileline());
}
AstNodeDType* AstNode::findQueueIndexDType() const {
return v3Global.rootp()->typeTablep()->findQueueIndexDType(fileline());
}
AstNodeDType* AstNode::findVoidDType() const {
return v3Global.rootp()->typeTablep()->findVoidDType(fileline());
}
//######################################################################
// AstNVisitor
+129 -95
View File
@@ -24,6 +24,7 @@
#include "V3FileLine.h"
#include "V3Number.h"
#include "V3Global.h"
#include "V3Broken.h"
#include <cmath>
#include <type_traits>
@@ -50,7 +51,7 @@ using MTaskIdSet = std::set<int>; // Set of mtaskIds for Var sorting
// For broken() function, return error string if have a match
#define BROKEN_RTN(test) \
do { \
if (VL_UNCOVERABLE(test)) return #test; \
if (VL_UNCOVERABLE(test)) return "'" #test "' @ " __FILE__ ":" VL_STRINGIFY(__LINE__); \
} while (false)
// For broken() function, return error string if a base of this class has a match
#define BROKEN_BASE_RTN(test) \
@@ -59,15 +60,21 @@ using MTaskIdSet = std::set<int>; // Set of mtaskIds for Var sorting
if (VL_UNCOVERABLE(reasonp)) return reasonp; \
} while (false)
// (V)erilator (N)ode is: True if AstNode is of a a given AstType
// (V)erilator (N)ode is: Returns true iff AstNode is of the given AstNode subtype, and not
// nullptr.
#define VN_IS(nodep, nodetypename) (AstNode::privateIs<Ast##nodetypename>(nodep))
// (V)erilator (N)ode cast: Cast to given type if can; effectively
// dynamic_cast<nodetypename>(nodep)
// (V)erilator (N)ode cast: More efficient but otherwise same as dynamic_cast, use this instead.
// Cast to given type if node is of such type, otherwise returns nullptr.
#define VN_CAST(nodep, nodetypename) (AstNode::privateCast<Ast##nodetypename>(nodep))
#define VN_CAST_CONST(nodep, nodetypename) (AstNode::privateConstCast<Ast##nodetypename>(nodep))
#define VN_CAST_CONST(nodep, nodetypename) (AstNode::privateCastConst<Ast##nodetypename>(nodep))
// (V)erilator (N)ode deleted: Reference to deleted child (for assertions only)
// (V)erilator (N)ode as: Assert node is of given type then cast to that type. Node can be nullptr.
// Use this to downcast instead of VN_CAST when you know the true type of the node.
#define VN_AS(nodep, nodetypename) (AstNode::privateAs<Ast##nodetypename>(nodep))
#define VN_AS_CONST(nodep, nodetypename) (AstNode::privateAsConst<Ast##nodetypename>(nodep))
// (V)erilator (N)ode deleted: Pointer to deleted AstNode (for assertions only)
#define VN_DELETED(nodep) VL_UNLIKELY((vluint64_t)(nodep) == 0x1)
//######################################################################
@@ -238,39 +245,6 @@ inline bool operator==(AstPragmaType::en lhs, const AstPragmaType& rhs) { return
//######################################################################
class AstCFuncType final {
public:
enum en : uint8_t {
FT_NORMAL,
TRACE_REGISTER,
TRACE_INIT,
TRACE_INIT_SUB,
TRACE_FULL,
TRACE_FULL_SUB,
TRACE_CHANGE,
TRACE_CHANGE_SUB,
TRACE_CLEANUP
};
enum en m_e;
inline AstCFuncType()
: m_e{FT_NORMAL} {}
// cppcheck-suppress noExplicitConstructor
inline AstCFuncType(en _e)
: m_e{_e} {}
explicit inline AstCFuncType(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
operator en() const { return m_e; }
// METHODS
bool isTrace() const { return m_e != FT_NORMAL; }
};
inline bool operator==(const AstCFuncType& lhs, const AstCFuncType& rhs) {
return lhs.m_e == rhs.m_e;
}
inline bool operator==(const AstCFuncType& lhs, AstCFuncType::en rhs) { return lhs.m_e == rhs; }
inline bool operator==(AstCFuncType::en lhs, const AstCFuncType& rhs) { return lhs == rhs.m_e; }
//######################################################################
class VEdgeType final {
public:
// REMEMBER to edit the strings below too
@@ -561,6 +535,25 @@ public:
bool isEventValue() const { return m_e == EVENTVALUE; }
bool isString() const { return m_e == STRING; }
bool isMTaskState() const { return m_e == MTASKSTATE; }
// Does this represent a C++ LiteralType? (can be constexpr)
bool isLiteralType() const {
switch (m_e) {
case BIT:
case BYTE:
case CHANDLE:
case INT:
case INTEGER:
case LOGIC:
case LONGINT:
case DOUBLE:
case SHORTINT:
case SCOPEPTR:
case CHARPTR:
case UINT32:
case UINT64: return true;
default: return false;
}
}
};
inline bool operator==(const AstBasicDTypeKwd& lhs, const AstBasicDTypeKwd& rhs) {
return lhs.m_e == rhs.m_e;
@@ -1367,8 +1360,32 @@ class AstNode VL_NOT_FINAL {
const AstType m_type; // Node sub-type identifier
// ^ ASTNODE_PREFETCH depends on above ordering of members
// padding - 2 extra bytes here after m_type
int m_cloneCnt; // Mark of when userp was set
// AstType is 2 bytes, so we can stick another 6 bytes after it to utilize what would
// otherwise be padding (on a 64-bit system). We stick the attribute flags, broken state,
// and the clone count here.
struct {
bool didWidth : 1; // Did V3Width computation
bool doingWidth : 1; // Inside V3Width
bool protect : 1; // Protect name if protection is on
// Space for more flags here (there must be 8 bits in total)
uint8_t unused : 5;
} m_flags; // Attribute flags
// State variable used by V3Broken for consistency checking. The top bit of this is byte is a
// flag, representing V3Broken is currently proceeding under this node. The bottom 7 bits are
// a generation number. This is hot when --debug-checks so we access as a whole to avoid bit
// field masking resulting in unnecessary read-modify-write ops.
uint8_t m_brokenState = 0;
int m_cloneCnt; // Sequence number for when last clone was made
#if defined(__x86_64__) && defined(__gnu_linux__)
// Only assert this on known platforms, as it only affects performance, not correctness
static_assert(sizeof(m_type) + sizeof(m_flags) + sizeof(m_brokenState) + sizeof(m_cloneCnt)
<= sizeof(void*),
"packing requires padding");
#endif
AstNodeDType* m_dtypep; // Data type of output or assignment (etc)
AstNode* m_headtailp; // When at begin/end of list, the opposite end of the list
@@ -1381,12 +1398,6 @@ class AstNode VL_NOT_FINAL {
AstNode* m_clonep; // Pointer to clone of/ source of this module (for *LAST* cloneTree() ONLY)
static int s_cloneCntGbl; // Count of which userp is set
// Attributes
bool m_didWidth : 1; // Did V3Width computation
bool m_doingWidth : 1; // Inside V3Width
bool m_protect : 1; // Protect name if protection is on
// // Space for more bools here
// This member ordering both allows 64 bit alignment and puts associated data together
VNUser m_user1u; // Contains any information the user iteration routine wants
uint32_t m_user1Cnt; // Mark of when userp was set
@@ -1432,7 +1443,7 @@ private:
public:
static void relinkOneLink(AstNode*& pointpr, AstNode* newp);
// cppcheck-suppress functionConst
void debugTreeChange(const char* prefix, int lineno, bool next);
static void debugTreeChange(const AstNode* nodep, const char* prefix, int lineno, bool next);
protected:
// CONSTRUCTORS
@@ -1489,9 +1500,14 @@ public:
AstNode* firstAbovep() const { // Returns nullptr when second or later in list
return ((backp() && backp()->nextp() != this) ? backp() : nullptr);
}
bool brokeExists() const;
bool brokeExistsAbove() const;
bool brokeExistsBelow() const;
uint8_t brokenState() const { return m_brokenState; }
void brokenState(uint8_t value) { m_brokenState = value; }
// Used by AstNode::broken()
bool brokeExists() const { return V3Broken::isLinkable(this); }
bool brokeExistsAbove() const { return brokeExists() && (m_brokenState >> 7); }
bool brokeExistsBelow() const { return brokeExists() && !(m_brokenState >> 7); }
// Note: brokeExistsBelow is not quite precise, as it is true for sibling nodes as well
// CONSTRUCTORS
virtual ~AstNode() = default;
@@ -1500,21 +1516,20 @@ public:
static void operator delete(void* obj, size_t size);
#endif
// CONSTANT ACCESSORS
static int instrCountBranch() { return 4; } ///< Instruction cycles to branch
static int instrCountDiv() { return 10; } ///< Instruction cycles to divide
static int instrCountDpi() { return 1000; } ///< Instruction cycles to call user function
static int instrCountLd() { return 2; } ///< Instruction cycles to load memory
static int instrCountMul() { return 3; } ///< Instruction cycles to multiply integers
static int instrCountPli() { return 20; } ///< Instruction cycles to call pli routines
static int instrCountDouble() { return 8; } ///< Instruction cycles to convert or do floats
static int instrCountDoubleDiv() { return 40; } ///< Instruction cycles to divide floats
static int instrCountDoubleTrig() { return 200; } ///< Instruction cycles to do trigonomics
static int instrCountString() { return 100; } ///< Instruction cycles to do string ops
/// Instruction cycles to call subroutine
static int instrCountCall() { return instrCountBranch() + 10; }
/// Instruction cycles to determine simulation time
static int instrCountTime() { return instrCountCall() + 5; }
// CONSTANTS
// The following are relative dynamic costs (~ execution cycle count) of various operations.
// They are used by V3InstCount to estimate the relative execution time of code fragments.
static constexpr int INSTR_COUNT_BRANCH = 4; // Branch
static constexpr int INSTR_COUNT_CALL = INSTR_COUNT_BRANCH + 10; // Subroutine call
static constexpr int INSTR_COUNT_LD = 2; // Load memory
static constexpr int INSTR_COUNT_INT_MUL = 3; // Integer multiply
static constexpr int INSTR_COUNT_INT_DIV = 10; // Integer divide
static constexpr int INSTR_COUNT_DBL = 8; // Convert or do float ops
static constexpr int INSTR_COUNT_DBL_DIV = 40; // Double divide
static constexpr int INSTR_COUNT_DBL_TRIG = 200; // Double trigonometric ops
static constexpr int INSTR_COUNT_STR = 100; // String ops
static constexpr int INSTR_COUNT_TIME = INSTR_COUNT_CALL + 5; // Determine simulation time
static constexpr int INSTR_COUNT_PLI = 20; // PLI routines
// ACCESSORS
virtual string name() const { return ""; }
@@ -1545,17 +1560,17 @@ public:
void fileline(FileLine* fl) { m_fileline = fl; }
bool width1() const;
int widthInstrs() const;
void didWidth(bool flag) { m_didWidth = flag; }
bool didWidth() const { return m_didWidth; }
void didWidth(bool flag) { m_flags.didWidth = flag; }
bool didWidth() const { return m_flags.didWidth; }
bool didWidthAndSet() {
if (didWidth()) return true;
didWidth(true);
return false;
}
bool doingWidth() const { return m_doingWidth; }
void doingWidth(bool flag) { m_doingWidth = flag; }
bool protect() const { return m_protect; }
void protect(bool flag) { m_protect = flag; }
bool doingWidth() const { return m_flags.doingWidth; }
void doingWidth(bool flag) { m_flags.doingWidth = flag; }
bool protect() const { return m_flags.protect; }
void protect(bool flag) { m_flags.protect = flag; }
// TODO stomp these width functions out, and call via dtypep() instead
int width() const;
@@ -1689,6 +1704,7 @@ public:
void dtypeSetSigned32() { dtypep(findSigned32DType()); }
void dtypeSetUInt32() { dtypep(findUInt32DType()); } // Twostate
void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate
void dtypeSetEmptyQueue() { dtypep(findEmptyQueueDType()); }
void dtypeSetVoid() { dtypep(findVoidDType()); }
// Data type locators
@@ -1699,6 +1715,7 @@ public:
AstNodeDType* findUInt32DType() { return findBasicDType(AstBasicDTypeKwd::UINT32); }
AstNodeDType* findUInt64DType() { return findBasicDType(AstBasicDTypeKwd::UINT64); }
AstNodeDType* findCHandleDType() { return findBasicDType(AstBasicDTypeKwd::CHANDLE); }
AstNodeDType* findEmptyQueueDType() const;
AstNodeDType* findVoidDType() const;
AstNodeDType* findQueueIndexDType() const;
AstNodeDType* findBitDType(int width, int widthMin, VSigning numeric) const;
@@ -1707,7 +1724,7 @@ public:
VSigning numeric) const;
AstNodeDType* findBitRangeDType(const VNumRange& range, int widthMin, VSigning numeric) const;
AstNodeDType* findBasicDType(AstBasicDTypeKwd kwd) const;
AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
static AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
// METHODS - dump and error
void v3errorEnd(std::ostringstream& str) const;
@@ -1782,8 +1799,6 @@ public:
virtual bool isGateOptimizable() const { return true; }
// GateDedupable is a slightly larger superset of GateOptimzable (eg, AstNodeIf)
virtual bool isGateDedupable() const { return isGateOptimizable(); }
// Needs verilated_heavy.h (uses std::string or some others)
virtual bool isHeavy() const { return false; }
// Else creates output or exits, etc, not unconsumed
virtual bool isOutputter() const { return false; }
// Else a AstTime etc which output can't be predicted from input
@@ -1829,16 +1844,36 @@ protected:
private:
void iterateListBackwards(AstNVisitor& v);
// CONVERSION
// For internal use only.
template <typename T> inline static bool privateTypeTest(const AstNode* nodep);
public:
// These for use by VN_IS macro only
template <class T> static bool privateIs(const AstNode* nodep);
// These for use by VN_CAST macro only
template <class T> static T* privateCast(AstNode* nodep);
// These for use by VN_CAST_CONST macro only
template <class T> static const T* privateConstCast(const AstNode* nodep);
// For use via the VN_IS macro only
template <typename T> inline static bool privateIs(const AstNode* nodep) {
return nodep && privateTypeTest<T>(nodep);
}
// For use via the VN_CAST macro only
template <typename T> inline static T* privateCast(AstNode* nodep) {
return privateIs<T>(nodep) ? reinterpret_cast<T*>(nodep) : nullptr;
}
// For use via the VN_CAST_CONST macro only
template <typename T> inline static const T* privateCastConst(const AstNode* nodep) {
return privateIs<T>(nodep) ? reinterpret_cast<const T*>(nodep) : nullptr;
}
// For use via the VN_AS macro only
template <typename T> inline static T* privateAs(AstNode* nodep) {
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
<< "'");
return reinterpret_cast<T*>(nodep);
}
// For use via the VN_AS_CONST macro only
template <typename T> inline static const T* privateAsConst(const AstNode* nodep) {
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
<< "'");
return reinterpret_cast<const T*>(nodep);
}
};
// Specialisations of privateIs/privateCast
@@ -2086,7 +2121,7 @@ public:
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual bool sizeMattersThs() const override { return false; }
virtual int instrCount() const override { return instrCountBranch(); }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual AstNode* cloneType(AstNode* condp, AstNode* expr1p, AstNode* expr2p) = 0;
};
@@ -2221,7 +2256,7 @@ public:
AstNode* incsp() const { return op3p(); } // op3 = increment statements
AstNode* bodysp() const { return op4p(); } // op4 = body of loop
virtual bool isGateOptimizable() const override { return false; }
virtual int instrCount() const override { return instrCountBranch(); }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual bool same(const AstNode* samep) const override { return true; }
};
@@ -2248,7 +2283,7 @@ public:
void addElsesp(AstNode* newp) { addOp3p(newp); }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isGateDedupable() const override { return true; }
virtual int instrCount() const override { return instrCountBranch(); }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual bool same(const AstNode* samep) const override { return true; }
void branchPred(VBranchPred flag) { m_branchPred = flag; }
VBranchPred branchPred() const { return m_branchPred; }
@@ -2266,7 +2301,7 @@ protected:
public:
ASTNODE_BASE_FUNCS(NodeCase)
virtual int instrCount() const override { return instrCountBranch(); }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
AstNode* exprp() const { return op1p(); } // op1 = case condition <expression>
AstCaseItem* itemsp() const {
return VN_CAST(op2p(), CaseItem);
@@ -2445,6 +2480,7 @@ public:
return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I");
}
string cType(const string& name, bool forFunc, bool isRef) const;
bool isLiteralType() const; // Does this represent a C++ LiteralType? (can be constexpr)
private:
class CTypeRecursed;
@@ -2621,7 +2657,6 @@ class AstNodeCCall VL_NOT_FINAL : public AstNodeStmt {
// A call of a C++ function, perhaps a AstCFunc or perhaps globally named
// Functions are not statements, while tasks are. AstNodeStmt needs isStatement() to deal.
AstCFunc* m_funcp;
string m_selfPointer; // Output code object pointer (e.g.: 'this')
string m_argTypes;
protected:
@@ -2636,7 +2671,7 @@ public:
virtual void dump(std::ostream& str = std::cout) const override;
virtual void cloneRelink() override;
virtual const char* broken() const override;
virtual int instrCount() const override { return instrCountCall(); }
virtual int instrCount() const override { return INSTR_COUNT_CALL; }
virtual bool same(const AstNode* samep) const override {
const AstNodeCCall* asamep = static_cast<const AstNodeCCall*>(samep);
return (funcp() == asamep->funcp() && argTypes() == asamep->argTypes());
@@ -2647,9 +2682,6 @@ public:
virtual bool isPure() const override;
virtual bool isOutputter() const override { return !isPure(); }
AstCFunc* funcp() const { return m_funcp; }
string selfPointer() const { return m_selfPointer; }
void selfPointer(const string& value) { m_selfPointer = value; }
string selfPointerProtect(bool useSelfForThis) const;
void argTypes(const string& str) { m_argTypes = str; }
string argTypes() const { return m_argTypes; }
// op1p reserved for AstCMethodCall
@@ -2678,6 +2710,7 @@ private:
bool m_isHideProtected : 1; // Verilog protected
bool m_pure : 1; // DPI import pure (vs. virtual pure)
bool m_pureVirtual : 1; // Pure virtual
bool m_underGenerate : 1; // Under generate (for warning)
bool m_virtual : 1; // Virtual method in class
VLifetime m_lifetime; // Lifetime
protected:
@@ -2700,6 +2733,7 @@ protected:
, m_isHideProtected{false}
, m_pure{false}
, m_pureVirtual{false}
, m_underGenerate{false}
, m_virtual{false} {
addNOp3p(stmtsp);
cname(name); // Might be overridden by dpi import/export
@@ -2766,6 +2800,8 @@ public:
bool pure() const { return m_pure; }
void pureVirtual(bool flag) { m_pureVirtual = flag; }
bool pureVirtual() const { return m_pureVirtual; }
void underGenerate(bool flag) { m_underGenerate = flag; }
bool underGenerate() const { return m_underGenerate; }
void isVirtual(bool flag) { m_virtual = flag; }
bool isVirtual() const { return m_virtual; }
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
@@ -2845,7 +2881,6 @@ private:
bool m_recursive : 1; // Recursive module
bool m_recursiveClone : 1; // If recursive, what module it clones, otherwise nullptr
int m_level = 0; // 1=top module, 2=cell off top module, ...
int m_varNum = 0; // Incrementing variable number
int m_typeNum = 0; // Incrementing implicit type number
VLifetime m_lifetime; // Lifetime
VTimescale m_timeunit; // Global time unit
@@ -2886,7 +2921,6 @@ public:
void level(int level) { m_level = level; }
int level() const { return m_level; }
bool isTop() const { return level() == 1; }
int varNumGetInc() { return ++m_varNum; }
int typeNumGetInc() { return ++m_typeNum; }
void modPublic(bool flag) { m_modPublic = flag; }
bool modPublic() const { return m_modPublic; }
+41 -12
View File
@@ -121,7 +121,8 @@ const char* AstNodeCCall::broken() const {
return nullptr;
}
bool AstNodeCCall::isPure() const { return funcp()->pure(); }
string AstNodeCCall::selfPointerProtect(bool useSelfForThis) const {
string AstCCall::selfPointerProtect(bool useSelfForThis) const {
const string& sp
= useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer();
return VIdProtect::protectWordsIf(sp, protect());
@@ -267,7 +268,7 @@ AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) {
char* endp;
int v = strtol(literal.c_str(), &endp, 0);
if ((v != 0) && (endp[0] == 0)) { // C literal
return new AstConst(fl, AstConst::WidthedValue(), 32, v);
return new AstConst(fl, AstConst::Signed32(), v);
} else { // Try a Verilog literal (fatals if not)
return new AstConst(fl, AstConst::StringToParse(), literal.c_str());
}
@@ -775,6 +776,20 @@ int AstNodeDType::widthPow2() const {
return 1;
}
bool AstNodeDType::isLiteralType() const {
if (auto* const dtypep = VN_CAST_CONST(skipRefp(), BasicDType)) {
return dtypep->keyword().isLiteralType();
} else if (auto* const dtypep = VN_CAST_CONST(skipRefp(), UnpackArrayDType)) {
return dtypep->basicp()->isLiteralType();
} else if (auto* const dtypep = VN_CAST_CONST(skipRefp(), StructDType)) {
// Currently all structs are packed, later this can be expanded to
// 'forall members _.isLiteralType()'
return dtypep->packed();
} else {
return false;
}
}
/// What is the base variable (or const) this dereferences?
AstNode* AstArraySel::baseFromp(AstNode* nodep, bool overMembers) {
// Else AstArraySel etc; search for the base
@@ -917,9 +932,18 @@ void AstTypeTable::repairCache() {
}
}
AstEmptyQueueDType* AstTypeTable::findEmptyQueueDType(FileLine* fl) {
if (VL_UNLIKELY(!m_emptyQueuep)) {
AstEmptyQueueDType* newp = new AstEmptyQueueDType{fl};
addTypesp(newp);
m_emptyQueuep = newp;
}
return m_emptyQueuep;
}
AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) {
if (VL_UNLIKELY(!m_voidp)) {
AstVoidDType* newp = new AstVoidDType(fl);
AstVoidDType* newp = new AstVoidDType{fl};
addTypesp(newp);
m_voidp = newp;
}
@@ -1050,8 +1074,8 @@ AstVarScope* AstConstPool::findTable(AstInitArray* initp) {
UASSERT_OBJ(VN_IS(valuep, Const), valuep, "Const pool table entry must be Const");
}
// Try to find an existing table with the same content
const uint32_t hash = V3Hasher::uncachedHash(initp).value();
const auto& er = m_tables.equal_range(hash);
const V3Hash hash = V3Hasher::uncachedHash(initp);
const auto& er = m_tables.equal_range(hash.value());
for (auto it = er.first; it != er.second; ++it) {
AstVarScope* const varScopep = it->second;
const AstInitArray* const init2p = VN_CAST(varScopep->varp()->valuep(), InitArray);
@@ -1061,11 +1085,11 @@ AstVarScope* AstConstPool::findTable(AstInitArray* initp) {
}
// No such table yet, create it.
string name = "TABLE_";
name += cvtToHex(hash);
name += hash.toString();
name += "_";
name += cvtToStr(std::distance(er.first, er.second));
AstVarScope* const varScopep = createNewEntry(name, initp);
m_tables.emplace(hash, varScopep);
m_tables.emplace(hash.value(), varScopep);
return varScopep;
}
@@ -1079,8 +1103,8 @@ static bool sameInit(const AstConst* ap, const AstConst* bp) {
AstVarScope* AstConstPool::findConst(AstConst* initp, bool mergeDType) {
// Try to find an existing constant with the same value
const uint32_t hash = initp->num().toHash().value();
const auto& er = m_consts.equal_range(hash);
const V3Hash hash = initp->num().toHash();
const auto& er = m_consts.equal_range(hash.value());
for (auto it = er.first; it != er.second; ++it) {
AstVarScope* const varScopep = it->second;
const AstConst* const init2p = VN_CAST(varScopep->varp()->valuep(), Const);
@@ -1091,11 +1115,11 @@ AstVarScope* AstConstPool::findConst(AstConst* initp, bool mergeDType) {
}
// No such constant yet, create it.
string name = "CONST_";
name += cvtToHex(hash);
name += hash.toString();
name += "_";
name += cvtToStr(std::distance(er.first, er.second));
AstVarScope* const varScopep = createNewEntry(name, initp);
m_consts.emplace(hash, varScopep);
m_consts.emplace(hash.value(), varScopep);
return varScopep;
}
@@ -1314,7 +1338,7 @@ void AstNodeCoverOrAssert::dump(std::ostream& str) const {
}
void AstDisplay::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
// str<<" "<<displayType().ascii();
// str << " " << displayType().ascii();
}
void AstEnumItemRef::dump(std::ostream& str) const {
this->AstNodeMath::dump(str);
@@ -1627,6 +1651,10 @@ void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "[]";
}
void AstEmptyQueueDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "emptyq";
}
void AstVoidDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "void";
@@ -1827,6 +1855,7 @@ void AstCFunc::dump(std::ostream& str) const {
if (dpiExportImpl()) str << " [DPIEI]";
if (dpiImportPrototype()) str << " [DPIIP]";
if (dpiImportWrapper()) str << " [DPIIW]";
if (dpiContext()) str << " [DPICTX]";
if (isConstructor()) str << " [CTOR]";
if (isDestructor()) str << " [DTOR]";
if (isVirtual()) str << " [VIRT]";
+159 -100
View File
@@ -183,6 +183,17 @@ public:
static AstConst* parseParamLiteral(FileLine* fl, const string& literal);
};
class AstEmptyQueue final : public AstNodeMath {
public:
AstEmptyQueue(FileLine* fl)
: ASTGEN_SUPER_EmptyQueue(fl) {}
ASTNODE_NODE_FUNCS(EmptyQueue)
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
virtual string emitVerilog() override { return "{}"; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool cleanOut() const override { return true; }
};
class AstRange final : public AstNodeRange {
// Range specification, for use under variables and cells
public:
@@ -307,7 +318,6 @@ public:
: ASTGEN_SUPER_Class(fl, name) {}
ASTNODE_NODE_FUNCS(Class)
virtual string verilogKwd() const override { return "class"; }
virtual bool isHeavy() const override { return true; }
virtual bool maybePointedTo() const override { return true; }
virtual void dump(std::ostream& str) const override;
virtual const char* broken() const override {
@@ -556,7 +566,6 @@ public:
virtual void dumpSmall(std::ostream& str) const override;
virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); }
virtual AstNodeDType* getChild2DTypep() const override { return keyChildDTypep(); }
virtual bool isHeavy() const override { return true; }
// op1 = Range of variable
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); }
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
@@ -653,7 +662,6 @@ public:
}
virtual string prettyDTypeName() const override;
virtual void dumpSmall(std::ostream& str) const override;
virtual bool isHeavy() const override { return true; }
virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); }
// op1 = Range of variable
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); }
@@ -881,7 +889,6 @@ public:
BROKEN_RTN(dtypep() != this);
return nullptr;
}
virtual bool isHeavy() const override { return keyword() == AstBasicDTypeKwd::STRING; }
AstRange* rangep() const { return VN_CAST(op1p(), Range); } // op1 = Range of variable
void rangep(AstRange* nodep) { setNOp1p(nodep); }
void setSignedState(const VSigning& signst) {
@@ -1141,7 +1148,6 @@ public:
}
virtual void dumpSmall(std::ostream& str) const override;
virtual string prettyDTypeName() const override;
virtual bool isHeavy() const override { return true; }
virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); }
// op1 = Range of variable
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); }
@@ -1359,6 +1365,33 @@ public:
}
};
class AstEmptyQueueDType final : public AstNodeDType {
// For EmptyQueue
public:
explicit AstEmptyQueueDType(FileLine* fl)
: ASTGEN_SUPER_EmptyQueueDType(fl) {
dtypep(this);
}
ASTNODE_NODE_FUNCS(EmptyQueueDType)
virtual void dumpSmall(std::ostream& str) const override;
virtual bool hasDType() const override { return true; }
virtual bool maybePointedTo() const override { return true; }
virtual AstNodeDType* subDTypep() const override { return nullptr; }
virtual AstNodeDType* virtRefDTypep() const override { return nullptr; }
virtual void virtRefDTypep(AstNodeDType* nodep) override {}
virtual bool similarDType(AstNodeDType* samep) const override { return this == samep; }
virtual AstBasicDType* basicp() const override { return nullptr; }
// cppcheck-suppress csyleCast
virtual AstNodeDType* skipRefp() const override { return (AstNodeDType*)this; }
// cppcheck-suppress csyleCast
virtual AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
// cppcheck-suppress csyleCast
virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
virtual int widthAlignBytes() const override { return 1; }
virtual int widthTotalBytes() const override { return 1; }
virtual bool isCompound() const override { return false; }
};
class AstVoidDType final : public AstNodeDType {
// For e.g. a function returning void
public:
@@ -2405,7 +2438,7 @@ public:
}
}
virtual int instrCount() const override {
return widthInstrs() * (access().isReadOrRW() ? instrCountLd() : 1);
return widthInstrs() * (access().isReadOrRW() ? INSTR_COUNT_LD : 1);
}
virtual string emitVerilog() override { V3ERROR_NA_RETURN(""); }
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
@@ -3484,6 +3517,17 @@ public:
virtual bool brokeLhsMustBeLvalue() const override { return true; }
};
class AstDpiExportUpdated final : public AstNodeStmt {
// Denotes that the referenced variable may have been updated via a DPI Export
public:
AstDpiExportUpdated(FileLine* fl, AstVarScope* varScopep)
: ASTGEN_SUPER_DpiExportUpdated(fl) {
addOp1p(new AstVarRef{fl, varScopep, VAccess::WRITE});
}
ASTNODE_NODE_FUNCS(DpiExportUpdated)
AstVarScope* varScopep() const { return VN_CAST(op1p(), VarRef)->varScopep(); }
};
class AstExprStmt final : public AstNodeMath {
// Perform a statement, often assignment inside an expression/math node,
// the parent gets passed the 'resultp()'.
@@ -3589,7 +3633,7 @@ public:
if (m_dataDeclp && m_dataDeclp->clonep()) m_dataDeclp = m_dataDeclp->clonep();
}
virtual void dump(std::ostream& str) const override;
virtual int instrCount() const override { return 1 + 2 * instrCountLd(); }
virtual int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; }
virtual bool maybePointedTo() const override { return true; }
void binNum(int flag) { m_binNum = flag; }
int binNum() const { return m_binNum; }
@@ -3632,7 +3676,7 @@ public:
if (m_declp->clonep()) m_declp = m_declp->clonep();
}
virtual void dump(std::ostream& str) const override;
virtual int instrCount() const override { return 1 + 2 * instrCountLd(); }
virtual int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; }
virtual bool same(const AstNode* samep) const override {
return declp() == static_cast<const AstCoverInc*>(samep)->declp();
}
@@ -3655,7 +3699,7 @@ public:
setOp3p(changep);
}
ASTNODE_NODE_FUNCS(CoverToggle)
virtual int instrCount() const override { return 3 + instrCountBranch() + instrCountLd(); }
virtual int instrCount() const override { return 3 + INSTR_COUNT_BRANCH + INSTR_COUNT_LD; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return true; }
@@ -3748,7 +3792,7 @@ public:
addNOp2p(bodysp);
}
ASTNODE_NODE_FUNCS(CaseItem)
virtual int instrCount() const override { return widthInstrs() + instrCountBranch(); }
virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
AstNode* condsp() const { return op1p(); } // op1 = list of possible matching expressions
AstNode* bodysp() const { return op2p(); } // op2 = what to do
void condsp(AstNode* nodep) { setOp1p(nodep); }
@@ -3792,7 +3836,7 @@ public:
}
ASTNODE_NODE_FUNCS(SFormatF)
virtual string name() const override { return m_text; }
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
virtual bool hasDType() const override { return true; }
virtual bool same(const AstNode* samep) const override {
return text() == static_cast<const AstSFormatF*>(samep)->text();
@@ -3857,7 +3901,7 @@ public:
virtual bool same(const AstNode* samep) const override {
return displayType() == static_cast<const AstDisplay*>(samep)->displayType();
}
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
AstDisplayType displayType() const { return m_displayType; }
void displayType(AstDisplayType type) { m_displayType = type; }
// * = Add a newline for $display
@@ -3882,7 +3926,6 @@ public:
ASTNODE_NODE_FUNCS(DumpCtl)
virtual string verilogKwd() const override { return ctlType().ascii(); }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isHeavy() const override { return true; }
virtual bool isPredictOptimizable() const override { return false; }
virtual bool isOutputter() const override { return true; }
virtual bool cleanOut() const { return true; }
@@ -3922,7 +3965,7 @@ public:
virtual bool same(const AstNode* samep) const override {
return displayType() == static_cast<const AstElabDisplay*>(samep)->displayType();
}
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
AstDisplayType displayType() const { return m_displayType; }
void displayType(AstDisplayType type) { m_displayType = type; }
void fmtp(AstSFormatF* nodep) { addOp1p(nodep); } // op1 = To-String formatter
@@ -3956,7 +3999,7 @@ public:
virtual bool isPure() const override { return true; }
virtual bool isOutputter() const override { return false; }
virtual bool cleanOut() const { return false; }
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
virtual bool same(const AstNode* samep) const override { return true; }
void fmtp(AstSFormatF* nodep) { addOp1p(nodep); } // op1 = To-String formatter
AstSFormatF* fmtp() const { return VN_CAST(op1p(), SFormatF); }
@@ -4001,7 +4044,7 @@ public:
} // Though deleted before opt
virtual bool isPure() const override { return false; } // Though deleted before opt
virtual bool isOutputter() const override { return true; } // Though deleted before opt
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
AstNode* exprsp() const { return op1p(); } // op1 = Expressions to output
void exprsp(AstNode* nodep) { addOp1p(nodep); } // op1 = Expressions to output
};
@@ -4038,7 +4081,6 @@ public:
ASTNODE_NODE_FUNCS(FOpen)
virtual string verilogKwd() const override { return "$fopen"; }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isHeavy() const override { return true; }
virtual bool isPredictOptimizable() const override { return false; }
virtual bool isPure() const override { return false; }
virtual bool isOutputter() const override { return true; }
@@ -4060,7 +4102,6 @@ public:
ASTNODE_NODE_FUNCS(FOpenMcd)
virtual string verilogKwd() const override { return "$fopen"; }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isHeavy() const override { return true; }
virtual bool isPredictOptimizable() const override { return false; }
virtual bool isPure() const override { return false; }
virtual bool isOutputter() const override { return true; }
@@ -4284,7 +4325,6 @@ public:
setNOp4p(msbp);
}
virtual bool isGateOptimizable() const override { return false; }
virtual bool isHeavy() const override { return true; }
virtual bool isPredictOptimizable() const override { return false; }
virtual bool isPure() const override { return false; }
virtual bool isOutputter() const override { return true; }
@@ -4335,7 +4375,7 @@ public:
} // Though deleted before opt
virtual bool isPure() const override { return false; } // Though deleted before opt
virtual bool isOutputter() const override { return true; } // Though deleted before opt
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
virtual bool same(const AstNode* samep) const override {
return m_off == static_cast<const AstMonitorOff*>(samep)->m_off;
}
@@ -4395,8 +4435,8 @@ public:
virtual string emitVerilog() override { return "%f$value$plusargs(%l, %k%r)"; }
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isHeavy() const override { return true; }
virtual bool isPredictOptimizable() const override { return false; }
virtual bool isPure() const override { return !outp(); }
virtual bool cleanOut() const override { return true; }
virtual bool same(const AstNode* samep) const override { return true; }
AstNode* searchp() const { return op1p(); } // op1 = Search expression
@@ -4448,7 +4488,7 @@ public:
AstNode* arrayp() const { return op1p(); } // op1 = array and index vars
AstNode* bodysp() const { return op4p(); } // op4 = body of loop
virtual bool isGateOptimizable() const override { return false; }
virtual int instrCount() const override { return instrCountBranch(); }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual bool same(const AstNode* samep) const override { return true; }
};
@@ -4465,7 +4505,7 @@ public:
virtual bool isGateOptimizable() const override {
return false;
} // Not relevant - converted to FOR
virtual int instrCount() const override { return instrCountBranch(); }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual bool same(const AstNode* samep) const override { return true; }
};
@@ -4498,7 +4538,7 @@ public:
void addBodysp(AstNode* newp) { addOp3p(newp); }
void addIncsp(AstNode* newp) { addOp4p(newp); }
virtual bool isGateOptimizable() const override { return false; }
virtual int instrCount() const override { return instrCountBranch(); }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual bool same(const AstNode* samep) const override { return true; }
// Stop statement searchback here
virtual void addBeforeStmt(AstNode* newp, AstNode* belowp) override;
@@ -4679,7 +4719,7 @@ public:
if (m_labelp->clonep()) m_labelp = m_labelp->clonep();
}
virtual void dump(std::ostream& str) const override;
virtual int instrCount() const override { return instrCountBranch(); }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual bool same(const AstNode* samep) const override {
return labelp() == static_cast<const AstJumpGo*>(samep)->labelp();
}
@@ -4736,7 +4776,7 @@ public:
bool isClockReq() const { return m_clockReq; }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
virtual int instrCount() const override { return widthInstrs(); }
virtual int instrCount() const override { return widthInstrs() * 2; } // xor, or/logor
virtual bool same(const AstNode* samep) const override { return true; }
};
@@ -5075,7 +5115,7 @@ public:
virtual bool isPredictOptimizable() const override { return false; }
virtual bool isPure() const override { return false; }
virtual bool isOutputter() const override { return true; }
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
VTimescale timeunit() const { return m_timeunit; }
};
@@ -5174,7 +5214,7 @@ public:
virtual bool isPredictOptimizable() const override { return false; }
virtual bool isPure() const override { return false; }
virtual bool isOutputter() const override { return true; }
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
AstNode* unitsp() const { return op1p(); }
AstNode* precisionp() const { return op2p(); }
AstNode* suffixp() const { return op3p(); }
@@ -5244,11 +5284,14 @@ class AstTraceInc final : public AstNodeStmt {
private:
AstTraceDecl* m_declp; // Pointer to declaration
const bool m_full; // Is this a full vs incremental dump
const uint32_t m_baseCode; // Trace code base value in function containing this AstTraceInc
public:
AstTraceInc(FileLine* fl, AstTraceDecl* declp, bool full)
AstTraceInc(FileLine* fl, AstTraceDecl* declp, bool full, uint32_t baseCode = 0)
: ASTGEN_SUPER_TraceInc(fl)
, m_declp{declp}
, m_full{full} {
, m_full{full}
, m_baseCode{baseCode} {
dtypeFrom(declp);
addOp2p(declp->valuep()->cloneTree(true));
}
@@ -5261,7 +5304,7 @@ public:
if (m_declp->clonep()) m_declp = m_declp->clonep();
}
virtual void dump(std::ostream& str) const override;
virtual int instrCount() const override { return 10 + 2 * instrCountLd(); }
virtual int instrCount() const override { return 10 + 2 * INSTR_COUNT_LD; }
virtual bool hasDType() const override { return true; }
virtual bool same(const AstNode* samep) const override {
return declp() == static_cast<const AstTraceInc*>(samep)->declp();
@@ -5276,6 +5319,7 @@ public:
AstNode* valuep() const { return op2p(); }
AstTraceDecl* declp() const { return m_declp; }
bool full() const { return m_full; }
uint32_t baseCode() const { return m_baseCode; }
};
class AstActive final : public AstNode {
@@ -5438,7 +5482,7 @@ public:
virtual bool cleanOut() const override { return true; }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
virtual bool same(const AstNode* samep) const override { return true; }
AstNode* seedp() const { return op1p(); }
bool reset() const { return m_reset; }
@@ -5468,7 +5512,7 @@ public:
virtual bool sizeMattersRhs() const override { return false; }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
};
class AstTime final : public AstNodeTermop {
@@ -5485,7 +5529,7 @@ public:
virtual bool cleanOut() const override { return true; }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
virtual int instrCount() const override { return instrCountTime(); }
virtual int instrCount() const override { return INSTR_COUNT_TIME; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual void dump(std::ostream& str = std::cout) const override;
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
@@ -5506,7 +5550,7 @@ public:
virtual bool cleanOut() const override { return true; }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
virtual int instrCount() const override { return instrCountTime(); }
virtual int instrCount() const override { return INSTR_COUNT_TIME; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual void dump(std::ostream& str = std::cout) const override;
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
@@ -5531,7 +5575,7 @@ public:
virtual bool isGateOptimizable() const override { return false; }
virtual bool isSubstOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
virtual bool same(const AstNode* samep) const override { return true; }
};
@@ -5567,7 +5611,7 @@ public:
virtual bool cleanOut() const override { return true; }
virtual bool cleanLhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
virtual bool doubleFlavor() const override { return true; }
};
class AstRedAnd final : public AstNodeUniop {
@@ -5760,7 +5804,7 @@ public:
virtual bool cleanOut() const override { return false; }
virtual bool cleanLhs() const override { return false; } // Eliminated before matters
virtual bool sizeMattersLhs() const override { return false; } // Eliminated before matters
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
};
class AstRToIRoundS final : public AstNodeUniop {
// Convert real to integer, with arbitrary sized output (not just "integer" format)
@@ -5778,7 +5822,7 @@ public:
virtual bool cleanOut() const override { return false; }
virtual bool cleanLhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
};
class AstIToRD final : public AstNodeUniop {
// $itor where lhs is unsigned
@@ -5794,7 +5838,7 @@ public:
virtual bool cleanOut() const override { return false; }
virtual bool cleanLhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
};
class AstISToRD final : public AstNodeUniop {
// $itor where lhs is signed
@@ -5811,7 +5855,7 @@ public:
virtual bool cleanOut() const override { return false; }
virtual bool cleanLhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
};
class AstRealToBits final : public AstNodeUniop {
public:
@@ -5828,7 +5872,7 @@ public:
virtual bool cleanOut() const override { return false; }
virtual bool cleanLhs() const override { return false; } // Eliminated before matters
virtual bool sizeMattersLhs() const override { return false; } // Eliminated before matters
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
};
class AstBitsToRealD final : public AstNodeUniop {
public:
@@ -5845,7 +5889,7 @@ public:
virtual bool cleanOut() const override { return false; }
virtual bool cleanLhs() const override { return false; } // Eliminated before matters
virtual bool sizeMattersLhs() const override { return false; } // Eliminated before matters
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
};
class AstCLog2 final : public AstNodeUniop {
@@ -6219,7 +6263,7 @@ public:
virtual bool cleanOut() const override { return true; }
virtual bool cleanLhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual int instrCount() const override { return instrCountDoubleTrig(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL_TRIG; }
virtual bool doubleFlavor() const override { return true; }
};
@@ -6530,7 +6574,6 @@ public:
virtual bool cleanOut() const override { return true; }
virtual bool cleanLhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool isHeavy() const override { return true; }
FmtType format() const { return m_fmt; }
};
@@ -6558,7 +6601,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return widthInstrs() + instrCountBranch(); }
virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
};
class AstLogAnd final : public AstNodeBiop {
public:
@@ -6581,7 +6624,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return widthInstrs() + instrCountBranch(); }
virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
};
class AstLogEq final : public AstNodeBiCom {
public:
@@ -6604,7 +6647,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return widthInstrs() + instrCountBranch(); }
virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
};
class AstLogIf final : public AstNodeBiop {
public:
@@ -6627,7 +6670,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return widthInstrs() + instrCountBranch(); }
virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
};
class AstOr final : public AstNodeBiComAsv {
public:
@@ -6740,7 +6783,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
virtual bool doubleFlavor() const override { return true; }
};
class AstEqN final : public AstNodeBiCom {
@@ -6764,7 +6807,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountString(); }
virtual int instrCount() const override { return INSTR_COUNT_STR; }
virtual bool stringFlavor() const override { return true; }
};
class AstNeq final : public AstNodeBiCom {
@@ -6810,7 +6853,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
virtual bool doubleFlavor() const override { return true; }
};
class AstNeqN final : public AstNodeBiCom {
@@ -6834,7 +6877,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountString(); }
virtual int instrCount() const override { return INSTR_COUNT_STR; }
virtual bool stringFlavor() const override { return true; }
};
class AstLt final : public AstNodeBiop {
@@ -6880,7 +6923,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
virtual bool doubleFlavor() const override { return true; }
};
class AstLtS final : public AstNodeBiop {
@@ -6927,7 +6970,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountString(); }
virtual int instrCount() const override { return INSTR_COUNT_STR; }
virtual bool stringFlavor() const override { return true; }
};
class AstGt final : public AstNodeBiop {
@@ -6973,7 +7016,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
virtual bool doubleFlavor() const override { return true; }
};
class AstGtS final : public AstNodeBiop {
@@ -7020,7 +7063,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountString(); }
virtual int instrCount() const override { return INSTR_COUNT_STR; }
virtual bool stringFlavor() const override { return true; }
};
class AstGte final : public AstNodeBiop {
@@ -7068,7 +7111,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
virtual bool doubleFlavor() const override { return true; }
};
class AstGteS final : public AstNodeBiop {
@@ -7115,7 +7158,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountString(); }
virtual int instrCount() const override { return INSTR_COUNT_STR; }
virtual bool stringFlavor() const override { return true; }
};
class AstLte final : public AstNodeBiop {
@@ -7163,7 +7206,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
virtual bool doubleFlavor() const override { return true; }
};
class AstLteS final : public AstNodeBiop {
@@ -7210,7 +7253,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountString(); }
virtual int instrCount() const override { return INSTR_COUNT_STR; }
virtual bool stringFlavor() const override { return true; }
};
class AstShiftL final : public AstNodeBiop {
@@ -7331,7 +7374,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
virtual bool doubleFlavor() const override { return true; }
};
class AstSub final : public AstNodeBiop {
@@ -7377,7 +7420,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
virtual bool doubleFlavor() const override { return true; }
};
class AstMul final : public AstNodeBiComAsv {
@@ -7401,7 +7444,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return true; }
virtual int instrCount() const override { return widthInstrs() * instrCountMul(); }
virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL; }
};
class AstMulD final : public AstNodeBiComAsv {
public:
@@ -7424,7 +7467,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return true; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
virtual bool doubleFlavor() const override { return true; }
};
class AstMulS final : public AstNodeBiComAsv {
@@ -7449,7 +7492,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return true; }
virtual int instrCount() const override { return widthInstrs() * instrCountMul(); }
virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL; }
virtual bool signedFlavor() const override { return true; }
};
class AstDiv final : public AstNodeBiop {
@@ -7472,7 +7515,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return true; }
virtual int instrCount() const override { return widthInstrs() * instrCountDiv(); }
virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_DIV; }
};
class AstDivD final : public AstNodeBiop {
public:
@@ -7495,7 +7538,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDoubleDiv(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL_DIV; }
virtual bool doubleFlavor() const override { return true; }
};
class AstDivS final : public AstNodeBiop {
@@ -7518,7 +7561,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return true; }
virtual int instrCount() const override { return widthInstrs() * instrCountDiv(); }
virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_DIV; }
virtual bool signedFlavor() const override { return true; }
};
class AstModDiv final : public AstNodeBiop {
@@ -7541,7 +7584,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return true; }
virtual int instrCount() const override { return widthInstrs() * instrCountDiv(); }
virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_DIV; }
};
class AstModDivS final : public AstNodeBiop {
public:
@@ -7563,7 +7606,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return true; }
virtual int instrCount() const override { return widthInstrs() * instrCountDiv(); }
virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_DIV; }
virtual bool signedFlavor() const override { return true; }
};
class AstPow final : public AstNodeBiop {
@@ -7587,7 +7630,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return widthInstrs() * instrCountMul() * 10; }
virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL * 10; }
};
class AstPowD final : public AstNodeBiop {
public:
@@ -7609,7 +7652,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDoubleDiv() * 5; }
virtual int instrCount() const override { return INSTR_COUNT_DBL_DIV * 5; }
virtual bool doubleFlavor() const override { return true; }
};
class AstPowSU final : public AstNodeBiop {
@@ -7635,7 +7678,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return widthInstrs() * instrCountMul() * 10; }
virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL * 10; }
virtual bool signedFlavor() const override { return true; }
};
class AstPowSS final : public AstNodeBiop {
@@ -7661,7 +7704,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return widthInstrs() * instrCountMul() * 10; }
virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL * 10; }
virtual bool signedFlavor() const override { return true; }
};
class AstPowUS final : public AstNodeBiop {
@@ -7687,7 +7730,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return widthInstrs() * instrCountMul() * 10; }
virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL * 10; }
virtual bool signedFlavor() const override { return true; }
};
class AstPreAdd final : public AstNodeTriop {
@@ -7928,7 +7971,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountString(); }
virtual int instrCount() const override { return INSTR_COUNT_STR; }
virtual bool stringFlavor() const override { return true; }
};
class AstReplicate final : public AstNodeBiop {
@@ -8091,7 +8134,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDoubleTrig(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL_TRIG; }
virtual bool doubleFlavor() const override { return true; }
};
@@ -8148,7 +8191,6 @@ public:
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual bool sizeMattersThs() const override { return false; }
virtual bool isHeavy() const override { return true; }
};
class AstGetcN final : public AstNodeBiop {
@@ -8174,7 +8216,6 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual bool isHeavy() const override { return true; }
};
class AstGetcRefN final : public AstNodeBiop {
@@ -8200,7 +8241,6 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual bool isHeavy() const override { return true; }
};
class AstSubstrN final : public AstNodeTriop {
@@ -8226,7 +8266,6 @@ public:
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual bool sizeMattersThs() const override { return false; }
virtual bool isHeavy() const override { return true; }
};
class AstCompareNN final : public AstNodeBiop {
@@ -8259,7 +8298,6 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual bool isHeavy() const override { return true; }
};
class AstFell final : public AstNodeMath {
@@ -8493,9 +8531,9 @@ private:
public:
AstNodeCoverOrAssert(AstType t, FileLine* fl, AstNode* propp, AstNode* passsp, bool immediate,
const string& name = "")
: AstNodeStmt(t, fl)
, m_immediate(immediate)
, m_name(name) {
: AstNodeStmt{t, fl}
, m_immediate{immediate}
, m_name{name} {
addOp1p(propp);
addNOp4p(passsp);
}
@@ -8728,7 +8766,6 @@ class AstCFunc final : public AstNode {
// Parents: MODULE/SCOPE
// Children: VAR/statements
private:
AstCFuncType m_funcType;
AstScope* m_scopep;
string m_name;
string m_cname; // C name, for dpiExports
@@ -8738,6 +8775,7 @@ private:
string m_ifdef; // #ifdef symbol around this function
VBoolOrUnknown m_isConst; // Function is declared const (*this not changed)
bool m_isStatic : 1; // Function is static (no need for a 'this' pointer)
bool m_isTrace : 1; // Function is related to tracing
bool m_dontCombine : 1; // V3Combine shouldn't compare this func tree, it's special
bool m_declPrivate : 1; // Declare it private
bool m_formCallTree : 1; // Make a global function to call entire tree of functions
@@ -8756,15 +8794,16 @@ private:
bool m_dpiExportImpl : 1; // DPI export implementation (called from DPI dispatcher via lookup)
bool m_dpiImportPrototype : 1; // This is the DPI import prototype (i.e.: provided by user)
bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code
bool m_dpiContext : 1; // Declared as 'context' DPI import/export function
public:
AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "")
: ASTGEN_SUPER_CFunc(fl) {
m_funcType = AstCFuncType::FT_NORMAL;
m_isConst = VBoolOrUnknown::BU_UNKNOWN; // Unknown until analyzed
m_scopep = scopep;
m_name = name;
m_rtnType = rtnType;
m_isStatic = false;
m_isTrace = false;
m_dontCombine = false;
m_declPrivate = false;
m_formCallTree = false;
@@ -8782,6 +8821,7 @@ public:
m_dpiExportImpl = false;
m_dpiImportPrototype = false;
m_dpiImportWrapper = false;
m_dpiContext = false;
}
ASTNODE_NODE_FUNCS(CFunc)
virtual string name() const override { return m_name; }
@@ -8793,25 +8833,29 @@ public:
virtual void dump(std::ostream& str = std::cout) const override;
virtual bool same(const AstNode* samep) const override {
const AstCFunc* asamep = static_cast<const AstCFunc*>(samep);
return ((funcType() == asamep->funcType()) && (rtnTypeVoid() == asamep->rtnTypeVoid())
return ((isTrace() == asamep->isTrace()) && (rtnTypeVoid() == asamep->rtnTypeVoid())
&& (argTypes() == asamep->argTypes()) && (ctorInits() == asamep->ctorInits())
&& isLoose() == asamep->isLoose()
&& (!(dpiImportPrototype() || dpiExportImpl()) || name() == asamep->name()));
}
//
virtual void name(const string& name) override { m_name = name; }
virtual int instrCount() const override { return dpiImportPrototype() ? instrCountDpi() : 0; }
virtual int instrCount() const override {
return dpiImportPrototype() ? v3Global.opt.instrCountDpi() : 0;
}
VBoolOrUnknown isConst() const { return m_isConst; }
void isConst(bool flag) { m_isConst.setTrueOrFalse(flag); }
void isConst(VBoolOrUnknown flag) { m_isConst = flag; }
bool isStatic() const { return m_isStatic; }
void isStatic(bool flag) { m_isStatic = flag; }
bool isTrace() const { return m_isTrace; }
void isTrace(bool flag) { m_isTrace = flag; }
void cname(const string& name) { m_cname = name; }
string cname() const { return m_cname; }
AstScope* scopep() const { return m_scopep; }
void scopep(AstScope* nodep) { m_scopep = nodep; }
string rtnTypeVoid() const { return ((m_rtnType == "") ? "void" : m_rtnType); }
bool dontCombine() const { return m_dontCombine || funcType() != AstCFuncType::FT_NORMAL; }
bool dontCombine() const { return m_dontCombine || isTrace(); }
void dontCombine(bool flag) { m_dontCombine = flag; }
bool dontInline() const { return dontCombine() || slow() || funcPublic(); }
bool declPrivate() const { return m_declPrivate; }
@@ -8828,8 +8872,6 @@ public:
string ctorInits() const { return m_ctorInits; }
void ifdef(const string& str) { m_ifdef = str; }
string ifdef() const { return m_ifdef; }
void funcType(AstCFuncType flag) { m_funcType = flag; }
AstCFuncType funcType() const { return m_funcType; }
bool isConstructor() const { return m_isConstructor; }
void isConstructor(bool flag) { m_isConstructor = flag; }
bool isDestructor() const { return m_isDestructor; }
@@ -8855,6 +8897,8 @@ public:
void dpiImportPrototype(bool flag) { m_dpiImportPrototype = flag; }
bool dpiImportWrapper() const { return m_dpiImportWrapper; }
void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; }
bool dpiContext() const { return m_dpiContext; }
void dpiContext(bool flag) { m_dpiContext = flag; }
//
// If adding node accessors, see below emptyBody
AstNode* argsp() const { return op1p(); }
@@ -8876,10 +8920,17 @@ class AstCCall final : public AstNodeCCall {
// C++ function call
// Parents: Anything above a statement
// Children: Args to the function
string m_selfPointer; // Output code object pointer (e.g.: 'this')
public:
AstCCall(FileLine* fl, AstCFunc* funcp, AstNode* argsp = nullptr)
: ASTGEN_SUPER_CCall(fl, funcp, argsp) {}
ASTNODE_NODE_FUNCS(CCall)
string selfPointer() const { return m_selfPointer; }
void selfPointer(const string& value) { m_selfPointer = value; }
string selfPointerProtect(bool useSelfForThis) const;
};
class AstCMethodCall final : public AstNodeCCall {
@@ -9002,8 +9053,8 @@ class AstCUse final : public AstNode {
// C++ use of a class or #include; indicates need of forward declaration
// Parents: NODEMODULE
private:
VUseType m_useType; // What sort of use this is
string m_name;
const VUseType m_useType; // What sort of use this is
const string m_name;
public:
AstCUse(FileLine* fl, VUseType useType, const string& name)
@@ -9011,10 +9062,9 @@ public:
, m_useType{useType}
, m_name{name} {}
ASTNODE_NODE_FUNCS(CUse)
virtual string name() const override { return m_name; }
virtual void dump(std::ostream& str = std::cout) const override;
virtual string name() const override { return m_name; }
VUseType useType() const { return m_useType; }
void useType(VUseType useType) { m_useType = useType; }
};
class AstMTaskBody final : public AstNode {
@@ -9077,8 +9127,9 @@ public:
class AstTypeTable final : public AstNode {
// Container for hash of standard data types
// Children: NODEDTYPEs
AstVoidDType* m_voidp = nullptr;
AstEmptyQueueDType* m_emptyQueuep = nullptr;
AstQueueDType* m_queueIndexp = nullptr;
AstVoidDType* m_voidp = nullptr;
AstBasicDType* m_basicps[AstBasicDTypeKwd::_ENUM_MAX];
//
using DetailedMap = std::map<VBasicTypeKey, AstBasicDType*>;
@@ -9089,14 +9140,15 @@ public:
ASTNODE_NODE_FUNCS(TypeTable)
AstNodeDType* typesp() const { return VN_CAST(op1p(), NodeDType); } // op1 = List of dtypes
void addTypesp(AstNodeDType* nodep) { addOp1p(nodep); }
AstVoidDType* findVoidDType(FileLine* fl);
AstQueueDType* findQueueIndexDType(FileLine* fl);
AstBasicDType* findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd);
AstBasicDType* findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd, int width, int widthMin,
VSigning numeric);
AstBasicDType* findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd, const VNumRange& range,
int widthMin, VSigning numeric);
AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
AstEmptyQueueDType* findEmptyQueueDType(FileLine* fl);
AstQueueDType* findQueueIndexDType(FileLine* fl);
AstVoidDType* findVoidDType(FileLine* fl);
void clearCache();
void repairCache();
virtual void dump(std::ostream& str = std::cout) const override;
@@ -9143,8 +9195,10 @@ private:
AstPackage* m_dollarUnitPkgp = nullptr; // $unit
AstCFunc* m_evalp = nullptr; // The '_eval' function
AstExecGraph* m_execGraphp = nullptr; // Execution MTask graph for threads>1 mode
AstVarScope* m_dpiExportTriggerp = nullptr; // The DPI export trigger variable
VTimescale m_timeunit; // Global time unit
VTimescale m_timeprecision; // Global time precision
bool m_changeRequest = false; // Have _change_request method
bool m_timescaleSpecified = false; // Input HDL specified timescale
public:
AstNetlist();
@@ -9152,6 +9206,7 @@ public:
virtual const char* broken() const override {
BROKEN_RTN(m_dollarUnitPkgp && !m_dollarUnitPkgp->brokeExists());
BROKEN_RTN(m_evalp && !m_evalp->brokeExists());
BROKEN_RTN(m_dpiExportTriggerp && !m_dpiExportTriggerp->brokeExists());
return nullptr;
}
virtual string name() const override { return "$root"; }
@@ -9168,6 +9223,8 @@ public:
AstNode* miscsp() const { return op3p(); } // op3 = List of dtypes etc
void addMiscsp(AstNode* nodep) { addOp3p(nodep); }
AstTypeTable* typeTablep() { return m_typeTablep; }
void changeRequest(bool specified) { m_changeRequest = specified; }
bool changeRequest() const { return m_changeRequest; }
AstConstPool* constPoolp() { return m_constPoolp; }
AstPackage* dollarUnitPkgp() const { return m_dollarUnitPkgp; }
AstPackage* dollarUnitPkgAddp() {
@@ -9185,6 +9242,8 @@ public:
void evalp(AstCFunc* evalp) { m_evalp = evalp; }
AstExecGraph* execGraphp() const { return m_execGraphp; }
void execGraphp(AstExecGraph* graphp) { m_execGraphp = graphp; }
AstVarScope* dpiExportTriggerp() const { return m_dpiExportTriggerp; }
void dpiExportTriggerp(AstVarScope* varScopep) { m_dpiExportTriggerp = varScopep; }
VTimescale timeunit() const { return m_timeunit; }
void timeunit(const VTimescale& value) { m_timeunit = value; }
VTimescale timeprecision() const { return m_timeprecision; }
+8 -1
View File
@@ -33,7 +33,7 @@ template <class T_Node, class T_Data, int T_UserN> class AstUserAllocatorBase VL
private:
std::vector<T_Data*> m_allocated;
inline T_Data* getUserp(T_Node* nodep) const {
inline T_Data* getUserp(const T_Node* nodep) const {
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
if (T_UserN == 1) {
const VNUser user = nodep->user1u();
@@ -100,6 +100,13 @@ public:
}
return *userp;
}
// Get a reference to the user data
T_Data& operator()(const T_Node* nodep) {
T_Data* userp = getUserp(nodep);
UASSERT_OBJ(userp, nodep, "Missing User data on const AstNode");
return *userp;
}
};
// User pointer allocator classes. T_Node is the type of node the allocator should be applied to
+31 -16
View File
@@ -62,6 +62,7 @@ private:
BeginState* m_statep; // Current global state
AstNodeModule* m_modp = nullptr; // Current module
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
AstNode* m_liftedp = nullptr; // Local nodes we are lifting into m_ftaskp
string m_namedScope; // Name of begin blocks above us
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
int m_ifDepth = 0; // Current if depth
@@ -74,6 +75,21 @@ private:
return a + "__DOT__" + b;
}
void liftNode(AstNode* nodep) {
nodep->unlinkFrBack();
if (m_ftaskp) {
// AstBegin under ftask, just move into the ftask
if (!m_liftedp) {
m_liftedp = nodep;
} else {
m_liftedp->addNext(nodep);
}
} else {
// Move to module
m_modp->addStmtp(nodep);
}
}
// VISITORS
virtual void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
@@ -101,7 +117,17 @@ private:
m_namedScope = "";
m_unnamedScope = "";
m_ftaskp = nodep;
m_liftedp = nullptr;
iterateChildren(nodep);
if (m_liftedp) {
// Place lifted nodes at beginning of stmtsp, so Var nodes appear before referenced
if (AstNode* const stmtsp = nodep->stmtsp()) {
stmtsp->unlinkFrBackWithNext();
m_liftedp->addNext(stmtsp);
}
nodep->addStmtsp(m_liftedp);
m_liftedp = nullptr;
}
m_ftaskp = nullptr;
}
}
@@ -157,13 +183,8 @@ private:
// Rename it
nodep->name(dot(m_unnamedScope, nodep->name()));
m_statep->userMarkChanged(nodep);
// Move to module
nodep->unlinkFrBack();
if (m_ftaskp) {
m_ftaskp->addStmtsp(nodep); // Begins under funcs just move into the func
} else {
m_modp->addStmtp(nodep);
}
// Move it under enclosing tree
liftNode(nodep);
}
}
virtual void visit(AstTypedef* nodep) override {
@@ -171,14 +192,8 @@ private:
// Rename it
nodep->name(dot(m_unnamedScope, nodep->name()));
m_statep->userMarkChanged(nodep);
// Move to module
nodep->unlinkFrBack();
// Begins under funcs just move into the func
if (m_ftaskp) {
m_ftaskp->addStmtsp(nodep);
} else {
m_modp->addStmtp(nodep);
}
// Move it under enclosing tree
liftNode(nodep);
}
}
virtual void visit(AstCell* nodep) override {
@@ -293,7 +308,7 @@ void V3Begin::debeginAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{
BeginState state;
{ BeginVisitor bvisitor(nodep, &state); }
{ BeginVisitor bvisitor{nodep, &state}; }
if (state.anyFuncInBegin()) { BeginRelinkVisitor brvisitor(nodep, &state); }
} // Destruct before checking
V3Global::dumpCheckGlobalTree("begin", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
+3 -3
View File
@@ -43,8 +43,8 @@ private:
AstUser1InUse m_inuser1;
// STATE
int m_likely; // Excuses for branch likely taken
int m_unlikely; // Excuses for branch likely not taken
int m_likely = false; // Excuses for branch likely taken
int m_unlikely = false; // Excuses for branch likely not taken
std::vector<AstCFunc*> m_cfuncsp; // List of all tasks
// METHODS
@@ -123,5 +123,5 @@ public:
void V3Branch::branchAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
BranchVisitor visitor(nodep);
BranchVisitor visitor{nodep};
}
+205 -182
View File
@@ -18,6 +18,7 @@
// Entire netlist
// Mark all nodes
// Check all links point to marked nodes
// Check local variables in CFuncs appear before they are referenced
//
//*************************************************************************
@@ -31,201 +32,129 @@
// This visitor does not edit nodes, and is called at error-exit, so should use constant iterators
#include "V3AstConstOnly.h"
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
//######################################################################
// Generation counter for AstNode::m_brokenState
class BrokenTable VL_NOT_FINAL : public AstNVisitor {
// Table of brokenExists node pointers
static class BrokenCntGlobal {
// This is a 7 bit generation counter, stored in the bottom 7 bits of AstNode::m_brokenState,
// used to mark a node as being present under the root AstNetlist in the current traversal. A
// value 0 is invalid, as the AstNode constructor uses that to initialize m_brokenState
static constexpr uint8_t MIN_VALUE = 1;
static constexpr uint8_t MAX_VALUE = 127;
uint8_t m_count = MIN_VALUE;
public:
uint8_t get() {
UASSERT(MIN_VALUE <= m_count && m_count <= MAX_VALUE, "Invalid generation number");
return m_count;
}
void inc() {
++m_count;
if (m_count > MAX_VALUE) m_count = MIN_VALUE;
}
} s_brokenCntGlobal;
//######################################################################
// Table of allocated AstNode pointers
static class AllocTable final {
private:
// MEMBERS
// For each node, we keep if it exists or not.
using NodeMap = std::unordered_map<const AstNode*, int>; // Performance matters (when --debug)
static NodeMap s_nodes; // Set of all nodes that exist
// BITMASK
enum { FLAG_ALLOCATED = 0x01 }; // new() and not delete()ed
enum { FLAG_IN_TREE = 0x02 }; // Is in netlist tree
enum { FLAG_LINKABLE = 0x04 }; // Is in netlist tree, can be linked to
enum { FLAG_LEAKED = 0x08 }; // Known to have been leaked
enum { FLAG_UNDER_NOW = 0x10 }; // Is in tree as parent of current node
std::unordered_set<const AstNode*> m_allocated; // Set of all nodes allocated but not freed
public:
// METHODS
static void deleted(const AstNode* nodep) {
// Called by operator delete on any node - only if VL_LEAK_CHECKS
if (debug() >= 9) cout << "-nodeDel: " << cvtToHex(nodep) << endl;
const auto iter = s_nodes.find(nodep);
UASSERT_OBJ(!(iter == s_nodes.end() || !(iter->second & FLAG_ALLOCATED)),
reinterpret_cast<const AstNode*>(nodep),
"Deleting AstNode object that was never tracked or already deleted");
if (iter != s_nodes.end()) s_nodes.erase(iter);
}
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 4
// GCC 4.4.* compiler warning bug, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39390
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#endif
static void addNewed(const AstNode* nodep) {
void addNewed(const AstNode* nodep) {
// Called by operator new on any node - only if VL_LEAK_CHECKS
if (debug() >= 9) cout << "-nodeNew: " << cvtToHex(nodep) << endl;
const auto iter = s_nodes.find(nodep);
UASSERT_OBJ(!(iter != s_nodes.end() && (iter->second & FLAG_ALLOCATED)), nodep,
"Newing AstNode object that is already allocated");
if (iter == s_nodes.end()) {
const int flags = FLAG_ALLOCATED; // This int needed to appease GCC 4.1.2
s_nodes.emplace(nodep, flags);
// LCOV_EXCL_START
if (VL_UNCOVERABLE(!m_allocated.emplace(nodep).second)) {
nodep->v3fatalSrc("Newing AstNode object that is already allocated");
}
// LCOV_EXCL_STOP
}
static void setUnder(const AstNode* nodep, bool flag) {
// Called by BrokenCheckVisitor when each node entered/exited
if (!okIfLinkedTo(nodep)) return;
const auto iter = s_nodes.find(nodep);
if (iter != s_nodes.end()) {
iter->second &= ~FLAG_UNDER_NOW;
if (flag) iter->second |= FLAG_UNDER_NOW;
void deleted(const AstNode* nodep) {
// Called by operator delete on any node - only if VL_LEAK_CHECKS
// LCOV_EXCL_START
if (VL_UNCOVERABLE(m_allocated.erase(nodep) == 0)) {
nodep->v3fatalSrc("Deleting AstNode object that was not allocated or already freed");
}
// LCOV_EXCL_STOP
}
static void addInTree(AstNode* nodep, bool linkable) {
#ifndef VL_LEAK_CHECKS
// cppcheck-suppress knownConditionTrueFalse
if (!linkable) return; // save some time, else the map will get huge!
#endif
const auto iter = s_nodes.find(nodep);
if (VL_UNCOVERABLE(iter == s_nodes.end())) {
#ifdef VL_LEAK_CHECKS
nodep->v3fatalSrc("AstNode is in tree, but not allocated");
#endif
} else {
#ifdef VL_LEAK_CHECKS
UASSERT_OBJ(iter->second & FLAG_ALLOCATED, nodep,
"AstNode is in tree, but not allocated");
#endif
UASSERT_OBJ(!(iter->second & FLAG_IN_TREE), nodep,
"AstNode is already in tree at another location");
}
const int or_flags = FLAG_IN_TREE | (linkable ? FLAG_LINKABLE : 0);
if (iter == s_nodes.end()) {
s_nodes.emplace(nodep, or_flags);
} else {
iter->second |= or_flags;
}
}
static bool isAllocated(const AstNode* nodep) {
// Some generic node has a pointer to this node. Is it allocated?
// Use this when might not be in tree; otherwise use okIfLinkedTo().
#ifdef VL_LEAK_CHECKS
const auto iter = s_nodes.find(nodep);
if (iter == s_nodes.end()) return false;
if (!(iter->second & FLAG_ALLOCATED)) return false;
#endif
return true;
}
static bool okIfLinkedTo(const AstNode* nodep) {
// Some node in tree has a pointer to this node. Is it kosher?
const auto iter = s_nodes.find(nodep);
if (iter == s_nodes.end()) return false;
#ifdef VL_LEAK_CHECKS
if (!(iter->second & FLAG_ALLOCATED)) return false;
#endif
if (!(iter->second & FLAG_IN_TREE)) return false;
if (!(iter->second & FLAG_LINKABLE)) return false;
return true;
}
static bool okIfAbove(const AstNode* nodep) {
// Must be linked to and below current node
if (!okIfLinkedTo(nodep)) return false;
const auto iter = s_nodes.find(nodep);
if (iter == s_nodes.end()) return false;
if ((iter->second & FLAG_UNDER_NOW)) return false;
return true;
}
static bool okIfBelow(const AstNode* nodep) {
// Must be linked to and below current node
if (!okIfLinkedTo(nodep)) return false;
const auto iter = s_nodes.find(nodep);
if (iter == s_nodes.end()) return false;
if (!(iter->second & FLAG_UNDER_NOW)) return false;
return true;
}
static void prepForTree() {
#ifndef VL_LEAK_CHECKS
s_nodes.clear();
#else
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
it->second &= ~FLAG_IN_TREE;
it->second &= ~FLAG_LINKABLE;
}
#endif
}
static void doneWithTree() {
for (int backs = 0; backs < 2;
backs++) { // Those with backp() are probably under one leaking without
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
// LCOV_EXCL_START
if (VL_UNCOVERABLE((it->second & FLAG_ALLOCATED) && !(it->second & FLAG_IN_TREE)
&& !(it->second & FLAG_LEAKED)
&& (it->first->backp() ? backs == 1 : backs == 0))) {
bool isAllocated(const AstNode* nodep) const { return m_allocated.count(nodep) != 0; }
void checkForLeaks() {
if (!v3Global.opt.debugCheck()) return;
const uint8_t brokenCntCurrent = s_brokenCntGlobal.get();
// Those with backp() are probably under a parent that was leaked and has no backp()
for (const bool withBack : {false, true}) {
for (const AstNode* const nodep : m_allocated) {
// LCOV_EXCL_START
// Most likely not leaked, so check that first
if (VL_UNCOVERABLE(nodep->brokenState() != brokenCntCurrent)) {
const bool hasBack = nodep->backp() != nullptr;
if (hasBack != withBack) continue;
// Use only AstNode::dump instead of the virtual one, as there
// may be varp() and other cross links that are bad.
if (v3Global.opt.debugCheck()) {
// When get this message, find what forgot to delete the
// node by running GDB, where for node "<e###>" use:
// watch AstNode::s_editCntGbl==####
// run
// bt
std::cerr << "%Error: LeakedNode"
<< (it->first->backp() ? "Back: " : ": ");
AstNode* rawp
= const_cast<AstNode*>(static_cast<const AstNode*>(it->first));
rawp->AstNode::dump(std::cerr);
std::cerr << endl;
V3Error::incErrors();
}
it->second |= FLAG_LEAKED;
// When get this message, find what forgot to delete the
// node by running GDB, where for node "<e###>" use:
// watch AstNode::s_editCntGbl==####
// run
// bt
std::cerr << "%Error: LeakedNode" << (withBack ? "with back pointer: " : ": ");
nodep->AstNode::dump(std::cerr);
std::cerr << endl;
V3Error::incErrors();
}
// LCOV_EXCL_STOP
}
}
}
} s_allocTable;
// CONSTRUCTORS
BrokenTable() = default;
virtual ~BrokenTable() override = default;
};
BrokenTable::NodeMap BrokenTable::s_nodes;
bool AstNode::brokeExists() const {
// Called by node->broken() routines to do table lookup
return BrokenTable::okIfLinkedTo(this);
}
bool AstNode::brokeExistsAbove() const {
// Called by node->broken() routines to do table lookup
return BrokenTable::okIfBelow(this);
}
bool AstNode::brokeExistsBelow() const {
// Called by node->broken() routines to do table lookup
return BrokenTable::okIfAbove(this);
}
void V3Broken::addNewed(const AstNode* nodep) { s_allocTable.addNewed(nodep); }
void V3Broken::deleted(const AstNode* nodep) { s_allocTable.deleted(nodep); }
//######################################################################
// Table of AstNode pointers that can be linked to via member pointers
static class LinkableTable final {
private:
// MEMBERS
std::unordered_set<const AstNode*> m_linkable; // Set of all nodes allocated but not freed
public:
// METHODS
void clear() { m_linkable.clear(); }
inline void addLinkable(const AstNode* nodep) { m_linkable.emplace(nodep); }
inline bool isLinkable(const AstNode* nodep) const { return m_linkable.count(nodep) != 0; }
} s_linkableTable;
bool V3Broken::isLinkable(const AstNode* nodep) { return s_linkableTable.isLinkable(nodep); }
//######################################################################
// Mark every node in the tree
class BrokenMarkVisitor final : public AstNVisitor {
// Mark every node in the tree
private:
// NODE STATE
// Nothing! // This may be called deep inside other routines
// // so userp and friends may not be used
// METHODS
void processAndIterate(AstNode* nodep) {
BrokenTable::addInTree(nodep, nodep->maybePointedTo());
iterateChildrenConst(nodep);
}
const uint8_t m_brokenCntCurrent = s_brokenCntGlobal.get();
// VISITORS
virtual void visit(AstNode* nodep) override {
// Process not just iterate
processAndIterate(nodep);
#ifdef VL_LEAK_CHECKS
UASSERT_OBJ(s_allocTable.isAllocated(nodep), nodep,
"AstNode is in tree, but not allocated");
#endif
UASSERT_OBJ(nodep->brokenState() != m_brokenCntCurrent, nodep,
"AstNode is already in tree at another location");
if (nodep->maybePointedTo()) s_linkableTable.addLinkable(nodep);
nodep->brokenState(m_brokenCntCurrent);
iterateChildrenConst(nodep);
}
public:
@@ -235,19 +164,33 @@ public:
};
//######################################################################
// Broken state, as a visitor of each AstNode
// Check every node in tree
class BrokenCheckVisitor final : public AstNVisitor {
bool m_inScope = false; // Under AstScope
// Constants for marking we are under/not under a node
const uint8_t m_brokenCntCurrentNotUnder = s_brokenCntGlobal.get(); // Top bit is clear
const uint8_t m_brokenCntCurrentUnder = m_brokenCntCurrentNotUnder | 0x80; // Top bit is set
// Current CFunc, if any
const AstCFunc* m_cfuncp = nullptr;
// All local variables declared in current function
std::unordered_set<const AstVar*> m_localVars;
// Variable references in current function that do not reference an in-scope local
std::unordered_map<const AstVar*, const AstNodeVarRef*> m_suspectRefs;
// Local variables declared in the scope of the current statement
std::vector<std::unordered_set<const AstVar*>> m_localsStack;
private:
static void checkWidthMin(const AstNode* nodep) {
UASSERT_OBJ(nodep->width() == nodep->widthMin()
|| v3Global.widthMinUsage() != VWidthMinUsage::MATCHES_WIDTH,
nodep, "Width != WidthMin");
}
void processAndIterate(AstNode* nodep) {
BrokenTable::setUnder(nodep, true);
void processEnter(AstNode* nodep) {
nodep->brokenState(m_brokenCntCurrentUnder);
const char* whyp = nodep->broken();
UASSERT_OBJ(!whyp, nodep,
"Broken link in node (or something without maybePointedTo): " << whyp);
@@ -270,8 +213,30 @@ private:
if (const AstNodeDType* dnodep = VN_CAST(nodep, NodeDType)) checkWidthMin(dnodep);
}
checkWidthMin(nodep);
}
void processExit(AstNode* nodep) { nodep->brokenState(m_brokenCntCurrentNotUnder); }
void processAndIterate(AstNode* nodep) {
processEnter(nodep);
iterateChildrenConst(nodep);
BrokenTable::setUnder(nodep, false);
processExit(nodep);
}
void processAndIterateList(AstNode* nodep) {
while (nodep) {
processAndIterate(nodep);
nodep = nodep->nextp();
};
}
void pushLocalScope() {
if (m_cfuncp) m_localsStack.emplace_back();
}
void popLocalScope() {
if (m_cfuncp) m_localsStack.pop_back();
}
bool isInScopeLocal(const AstVar* varp) const {
for (const auto& set : m_localsStack) {
if (set.count(varp)) return true;
}
return false;
}
virtual void visit(AstNodeAssign* nodep) override {
processAndIterate(nodep);
@@ -295,6 +260,65 @@ private:
UASSERT_OBJ(
!(v3Global.assertScoped() && m_inScope && nodep->varp() && !nodep->varScopep()), nodep,
"VarRef missing VarScope pointer");
if (m_cfuncp) {
// Check if variable is an in-scope local, otherwise mark as suspect
if (const AstVar* const varp = nodep->varp()) {
if (!isInScopeLocal(varp)) {
// This only stores the first ref for each Var, which is what we want
m_suspectRefs.emplace(varp, nodep);
}
}
}
}
virtual void visit(AstCFunc* nodep) override {
UASSERT_OBJ(!m_cfuncp, nodep, "Nested AstCFunc");
m_cfuncp = nodep;
m_localVars.clear();
m_suspectRefs.clear();
m_localsStack.clear();
pushLocalScope();
processAndIterate(nodep);
// Check suspect references are all to non-locals
for (const auto& pair : m_suspectRefs) {
UASSERT_OBJ(m_localVars.count(pair.first) == 0, pair.second,
"Local variable not in scope where referenced: " << pair.first);
}
m_cfuncp = nullptr;
}
virtual void visit(AstNodeIf* nodep) override {
// Each branch is a separate local variable scope
pushLocalScope();
processEnter(nodep);
processAndIterate(nodep->condp());
if (AstNode* const ifsp = nodep->ifsp()) {
pushLocalScope();
processAndIterateList(ifsp);
popLocalScope();
}
if (AstNode* const elsesp = nodep->elsesp()) {
pushLocalScope();
processAndIterateList(elsesp);
popLocalScope();
}
processExit(nodep);
popLocalScope();
}
virtual void visit(AstNodeStmt* nodep) override {
// For local variable checking act as if any statement introduces a new scope.
// This is aggressive but conservatively correct.
pushLocalScope();
processAndIterate(nodep);
popLocalScope();
}
virtual void visit(AstVar* nodep) override {
processAndIterate(nodep);
if (m_cfuncp) {
m_localVars.insert(nodep);
m_localsStack.back().insert(nodep);
}
}
virtual void visit(AstNode* nodep) override {
// Process not just iterate
@@ -308,7 +332,7 @@ public:
};
//######################################################################
// Broken class functions
// Broken check entry point
void V3Broken::brokenAll(AstNetlist* nodep) {
// UINFO(9, __FUNCTION__ << ": " << endl);
@@ -318,24 +342,23 @@ void V3Broken::brokenAll(AstNetlist* nodep) {
UINFO(1, "Broken called under broken, skipping recursion.\n"); // LCOV_EXCL_LINE
} else {
inBroken = true;
BrokenTable::prepForTree();
BrokenMarkVisitor mvisitor(nodep);
BrokenCheckVisitor cvisitor(nodep);
BrokenTable::doneWithTree();
BrokenMarkVisitor mvisitor{nodep};
BrokenCheckVisitor cvisitor{nodep};
s_allocTable.checkForLeaks();
s_linkableTable.clear();
s_brokenCntGlobal.inc();
inBroken = false;
}
}
void V3Broken::addNewed(AstNode* nodep) { BrokenTable::addNewed(nodep); }
void V3Broken::deleted(AstNode* nodep) { BrokenTable::deleted(nodep); }
bool V3Broken::isAllocated(AstNode* nodep) { return BrokenTable::isAllocated(nodep); }
//######################################################################
// Self test
void V3Broken::selfTest() {
// Warmup addNewed and deleted for coverage, as otherwise only with VL_LEAK_CHECKS
FileLine* fl = new FileLine(FileLine::commandLineFilename());
auto* newp = new AstBegin(fl, "[EditWrapper]", nullptr);
#ifndef VL_LEAK_CHECKS
// Exercise addNewed and deleted for coverage, as otherwise only used with VL_LEAK_CHECKS
FileLine* const fl = new FileLine(FileLine::commandLineFilename());
const AstNode* const newp = new AstBegin(fl, "[EditWrapper]", nullptr);
addNewed(newp);
deleted(newp);
#endif
VL_DO_DANGLING(delete newp, newp);
}
+7 -6
View File
@@ -20,17 +20,18 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
// Forward declare so we can include this in V3Ast.h
class AstNode;
class AstNetlist;
class V3Broken final {
public:
static void brokenAll(AstNetlist* nodep);
static void addNewed(AstNode* nodep);
static void deleted(AstNode* nodep);
static bool isAllocated(AstNode* nodep);
static bool isLinkable(const AstNode* nodep);
static void addNewed(const AstNode* nodep);
static void deleted(const AstNode* nodep);
static void selfTest();
};
+27 -25
View File
@@ -60,7 +60,7 @@ private:
AstCFunc* makeNewFunc() {
const int funcNum = m_newFunctions.size();
const string funcName = m_basename + "_" + cvtToStr(funcNum);
AstCFunc* const funcp = new AstCFunc(m_modp->fileline(), funcName, nullptr, "void");
AstCFunc* const funcp = new AstCFunc{m_modp->fileline(), funcName, nullptr, "void"};
funcp->isStatic(false);
funcp->isLoose(!m_type.isClass());
funcp->declPrivate(true);
@@ -74,7 +74,7 @@ private:
preventUnusedStmt = "if (false && first) {} // Prevent unused\n";
}
if (!preventUnusedStmt.empty()) {
funcp->addStmtsp(new AstCStmt(m_modp->fileline(), preventUnusedStmt));
funcp->addStmtsp(new AstCStmt{m_modp->fileline(), preventUnusedStmt});
}
m_modp->addStmtp(funcp);
m_numStmts = 0;
@@ -91,9 +91,9 @@ public:
}
V3CCtorsBuilder(AstNodeModule* nodep, const string& basename, VCtorType type)
: m_modp(nodep)
: m_modp{nodep}
, m_basename{basename}
, m_type(type) {
, m_type{type} {
// Note: The constructor is always called, even if empty, so we must always create at least
// one.
m_newFunctions.push_back(makeNewFunc());
@@ -108,7 +108,7 @@ public:
AstCFunc* const rootFuncp = makeNewFunc();
rootFuncp->name(m_basename);
for (AstCFunc* const funcp : m_newFunctions) {
AstCCall* const callp = new AstCCall(m_modp->fileline(), funcp);
AstCCall* const callp = new AstCCall{m_modp->fileline(), funcp};
if (m_type.isClass()) {
callp->argTypes("vlSymsp");
} else {
@@ -127,8 +127,9 @@ private:
//######################################################################
void V3CCtors::evalAsserts() {
AstNodeModule* modp = v3Global.rootp()->modulesp(); // Top module wrapper
AstCFunc* funcp = new AstCFunc(modp->fileline(), "_eval_debug_assertions", nullptr, "void");
AstNodeModule* const modp = v3Global.rootp()->modulesp(); // Top module wrapper
AstCFunc* const funcp
= new AstCFunc{modp->fileline(), "_eval_debug_assertions", nullptr, "void"};
funcp->declPrivate(true);
funcp->isStatic(false);
funcp->isLoose(true);
@@ -136,7 +137,7 @@ void V3CCtors::evalAsserts() {
funcp->ifdef("VL_DEBUG");
modp->addStmtp(funcp);
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
if (AstVar* varp = VN_CAST(np, Var)) {
if (AstVar* const varp = VN_CAST(np, Var)) {
if (varp->isPrimaryInish() && !varp->isSc()) {
if (AstBasicDType* basicp = VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
const int storedWidth = basicp->widthAlignBytes() * 8;
@@ -144,22 +145,22 @@ void V3CCtors::evalAsserts() {
if (lastWordWidth != 0) {
// if (signal & CONST(upper_non_clean_mask)) { fail; }
AstVarRef* const vrefp
= new AstVarRef(varp->fileline(), varp, VAccess::READ);
= new AstVarRef{varp->fileline(), varp, VAccess::READ};
vrefp->selfPointer("this");
AstNode* newp = vrefp;
if (varp->isWide()) {
newp = new AstWordSel(
newp = new AstWordSel{
varp->fileline(), newp,
new AstConst(varp->fileline(), varp->widthWords() - 1));
new AstConst(varp->fileline(), varp->widthWords() - 1)};
}
uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth);
newp = new AstAnd(varp->fileline(), newp,
const uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth);
newp = new AstAnd{varp->fileline(), newp,
new AstConst(varp->fileline(), AstConst::WidthedValue(),
storedWidth, value));
AstNodeIf* ifp = new AstIf(
storedWidth, value)};
AstNodeIf* const ifp = new AstIf{
varp->fileline(), newp,
new AstCStmt(varp->fileline(), "Verilated::overWidthError(\""
+ varp->prettyName() + "\");"));
new AstCStmt{varp->fileline(), "Verilated::overWidthError(\""
+ varp->prettyName() + "\");"}};
ifp->branchPred(VBranchPred::BP_UNLIKELY);
newp = ifp;
funcp->addStmtsp(newp);
@@ -177,20 +178,21 @@ void V3CCtors::cctorsAll() {
modp = VN_CAST(modp->nextp(), NodeModule)) {
// Process each module in turn
{
V3CCtorsBuilder var_reset(modp, "_ctor_var_reset",
VN_IS(modp, Class) ? VCtorType::CLASS : VCtorType::MODULE);
V3CCtorsBuilder var_reset{modp, "_ctor_var_reset",
VN_IS(modp, Class) ? VCtorType::CLASS : VCtorType::MODULE};
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
if (AstVar* const varp = VN_CAST(np, Var)) {
if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset()) {
const auto vrefp = new AstVarRef(varp->fileline(), varp, VAccess::WRITE);
var_reset.add(new AstCReset(varp->fileline(), vrefp));
if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset()
&& !varp->isParam()) {
const auto vrefp = new AstVarRef{varp->fileline(), varp, VAccess::WRITE};
var_reset.add(new AstCReset{varp->fileline(), vrefp});
}
}
}
}
if (v3Global.opt.coverage()) {
V3CCtorsBuilder configure_coverage(modp, "_configure_coverage", VCtorType::COVERAGE);
V3CCtorsBuilder configure_coverage{modp, "_configure_coverage", VCtorType::COVERAGE};
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
if (AstCoverDecl* const coverp = VN_CAST(np, CoverDecl)) {
np = coverp->backp();
@@ -198,8 +200,8 @@ void V3CCtors::cctorsAll() {
}
}
}
if (AstClass* classp = VN_CAST(modp, Class)) {
AstCFunc* funcp = new AstCFunc(modp->fileline(), "~", nullptr, "");
if (AstClass* const classp = VN_CAST(modp, Class)) {
AstCFunc* const funcp = new AstCFunc{modp->fileline(), "~", nullptr, ""};
funcp->isDestructor(true);
funcp->isStatic(false);
// If can be referred to by base pointer, need virtual delete
+35 -165
View File
@@ -13,14 +13,12 @@
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
// V3Class's Transformations:
// V3CUse's Transformations:
//
// Each module:
// Each cell:
// Create CUse for cell forward declaration
// Each class:
// Create string access functions
// Search for dtypes referencing class, and create CUse for forward declaraion
// Search for dtypes referencing class, and create CUse for forward declaration
//
//*************************************************************************
@@ -30,61 +28,37 @@
#include "V3Global.h"
#include "V3CUse.h"
#include "V3Ast.h"
#include "V3EmitCBase.h"
#include <unordered_map>
#include <set>
//######################################################################
class CUseState final {
private:
// MEMBERS
AstNodeModule* m_modInsertp; // Current module to insert AstCUse under
using UseString = std::pair<VUseType, std::string>;
std::map<const UseString, AstCUse*> m_didUse; // What we already used
// NODE STATE
// Entire netlist:
// AstClass::user1() -> bool. True if class needs to_string dumper
AstUser1InUse m_inuser1;
// AstClass::user2() -> bool. True if iterated
AstUser2InUse m_inuser2;
// METHODS
VL_DEBUG_FUNC; // Declare debug()
public:
AstCUse* newUse(AstNode* nodep, VUseType useType, const string& name) {
UseString key(useType, name);
if (m_didUse.find(key) == m_didUse.end()) {
AstCUse* newp = new AstCUse(nodep->fileline(), useType, name);
m_modInsertp->addStmtp(newp);
UINFO(8, "Insert " << newp << endl);
m_didUse[key] = newp;
}
return m_didUse[key];
}
// CONSTRUCTORS
explicit CUseState(AstNodeModule* nodep)
: m_modInsertp{nodep} {}
virtual ~CUseState() = default;
VL_UNCOPYABLE(CUseState);
};
// Visit within a module all nodes and data types they reference, finding
// any classes so we can make sure they are defined when Verilated code
// compiles
class CUseDTypeVisitor final : public AstNVisitor {
class CUseVisitor final : public AstNVisitor {
// NODE STATE
// AstNode::user1() -> bool. True if already visited
AstUser1InUse m_inuser1;
// MEMBERS
CUseState& m_stater; // State for inserter
bool m_impOnly = false; // In details needed only for implementation
AstNodeModule* const m_modp; // Current module
std::set<std::pair<VUseType, std::string>> m_didUse; // What we already used
// METHODS
void addNewUse(AstNode* nodep, VUseType useType, const string& name) {
if (m_didUse.emplace(useType, name).second) {
AstCUse* const newp = new AstCUse{nodep->fileline(), useType, name};
m_modp->addStmtp(newp);
UINFO(8, "Insert " << newp << endl);
}
}
// VISITORS
virtual void visit(AstClassRefDType* nodep) override {
if (nodep->user2SetOnce()) return; // Process once
if (!m_impOnly) m_stater.newUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
// No class.h, it's inside the class package's h file
m_stater.newUse(nodep, VUseType::IMP_INCLUDE, nodep->classp()->classOrPackagep()->name());
if (nodep->user1SetOnce()) return; // Process once
if (!m_impOnly) addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
// Need to include extends() when we implement, but no need for pointers to know
VL_RESTORER(m_impOnly);
{
@@ -93,131 +67,27 @@ class CUseDTypeVisitor final : public AstNVisitor {
}
}
virtual void visit(AstNodeDType* nodep) override {
if (nodep->user2SetOnce()) return; // Process once
if (nodep->user1SetOnce()) return; // Process once
if (nodep->virtRefDTypep()) iterate(nodep->virtRefDTypep());
if (nodep->virtRefDType2p()) iterate(nodep->virtRefDType2p());
}
virtual void visit(AstNode* nodep) override {
if (nodep->user2SetOnce()) return; // Process once
if (nodep->dtypep() && !nodep->dtypep()->user2()) iterate(nodep->dtypep());
if (nodep->user1SetOnce()) return; // Process once
if (nodep->dtypep() && !nodep->dtypep()->user1()) iterate(nodep->dtypep());
iterateChildren(nodep);
}
virtual void visit(AstCell* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
// Currently no IMP_INCLUDE because we include __Syms which has them all
addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->modp()->name());
iterateChildren(nodep);
}
public:
// CONSTRUCTORS
explicit CUseDTypeVisitor(AstNodeModule* nodep, CUseState& stater)
: m_stater(stater) { // Need () or GCC 4.8 false warning
iterate(nodep);
}
virtual ~CUseDTypeVisitor() override = default;
VL_UNCOPYABLE(CUseDTypeVisitor);
};
class CUseVisitor final : public AstNVisitor {
// MEMBERS
CUseState m_state; // Inserter state
// METHODS
VL_DEBUG_FUNC; // Declare debug()
// Module use builders
void makeUseCells(AstNodeModule* nodep) {
for (AstNode* itemp = nodep->stmtsp(); itemp; itemp = itemp->nextp()) {
if (AstCell* cellp = VN_CAST(itemp, Cell)) {
// Currently no include because we include __Syms which has them all
m_state.newUse(nodep, VUseType::INT_FWD_CLASS, cellp->modp()->name());
}
}
}
void makeVlToString(AstClass* nodep) {
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "VL_TO_STRING", nullptr, "std::string");
funcp->argTypes("const VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(nodep)
+ ">& obj");
funcp->isMethod(false);
funcp->isConst(false);
funcp->isStatic(false);
funcp->protect(false);
AstNode* exprp = new AstCMath(nodep->fileline(), "obj ? obj->to_string() : \"null\"", 0);
exprp->dtypeSetString();
funcp->addStmtsp(new AstCReturn(nodep->fileline(), exprp));
nodep->addStmtp(funcp);
}
void makeToString(AstClass* nodep) {
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "to_string", nullptr, "std::string");
funcp->isConst(true);
funcp->isStatic(false);
funcp->protect(false);
AstNode* exprp = new AstCMath(nodep->fileline(),
R"(std::string("'{") + to_string_middle() + "}")", 0);
exprp->dtypeSetString();
funcp->addStmtsp(new AstCReturn(nodep->fileline(), exprp));
nodep->addStmtp(funcp);
}
void makeToStringMiddle(AstClass* nodep) {
AstCFunc* funcp
= new AstCFunc(nodep->fileline(), "to_string_middle", nullptr, "std::string");
funcp->isConst(true);
funcp->isStatic(false);
funcp->protect(false);
funcp->addStmtsp(new AstCStmt(nodep->fileline(), "std::string out;\n"));
std::string comma;
for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) {
if (auto* varp = VN_CAST(itemp, Var)) {
if (!varp->isParam()) {
string stmt = "out += \"";
stmt += comma;
comma = ", ";
stmt += itemp->origNameProtect();
stmt += ":\" + ";
if (itemp->isWide()) {
stmt += "VL_TO_STRING_W(";
stmt += cvtToStr(itemp->widthWords());
stmt += ", ";
} else {
stmt += "VL_TO_STRING(";
}
stmt += itemp->nameProtect();
stmt += ");\n";
nodep->user1(true); // So what we extend dumps this
funcp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
}
}
}
if (nodep->extendsp() && nodep->extendsp()->classp()->user1()) {
string stmt = "out += \"";
if (!comma.empty()) stmt += "\", \"+ ";
// comma = ", "; // Nothing further so not needed
stmt += nodep->extendsp()->dtypep()->nameProtect();
stmt += "::to_string_middle();\n";
nodep->user1(true); // So what we extend dumps this
funcp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
}
funcp->addStmtsp(new AstCStmt(nodep->fileline(), "return out;\n"));
nodep->addStmtp(funcp);
}
// VISITORS
virtual void visit(AstNodeModule* nodep) override {
if (v3Global.opt.trace()) {
AstCUse* usep
= m_state.newUse(nodep, VUseType::INT_FWD_CLASS, v3Global.opt.traceClassBase());
usep->protect(false);
}
makeUseCells(nodep);
{ CUseDTypeVisitor dtypeVisitor(nodep, m_state); }
if (AstClass* classp = VN_CAST(nodep, Class)) {
makeVlToString(classp);
makeToString(classp);
makeToStringMiddle(classp);
}
}
virtual void visit(AstNode*) override {} // All in AstNodeModule
public:
// CONSTRUCTORS
explicit CUseVisitor(AstNodeModule* nodep)
: m_state{nodep} {
iterate(nodep);
explicit CUseVisitor(AstNodeModule* modp)
: m_modp(modp) {
iterate(modp);
}
virtual ~CUseVisitor() override = default;
VL_UNCOPYABLE(CUseVisitor);
@@ -233,7 +103,7 @@ void V3CUse::cUseAll() {
modp = VN_CAST(modp->nextp(), NodeModule)) {
// Insert under this module; someday we should e.g. make Ast
// for each output file and put under that
CUseVisitor visitor(modp);
CUseVisitor{modp};
}
V3Global::dumpCheckGlobalTree("cuse", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+2 -2
View File
@@ -530,10 +530,10 @@ public:
void V3Case::caseAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CaseVisitor visitor(nodep); } // Destruct before checking
{ CaseVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("case", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
void V3Case::caseLint(AstNodeCase* nodep) {
UINFO(4, __FUNCTION__ << ": " << endl);
CaseLintVisitor visitor(nodep);
CaseLintVisitor visitor{nodep};
}
+8 -6
View File
@@ -66,7 +66,8 @@ private:
AstNRelinker relinkHandle;
nodep->unlinkFrBack(&relinkHandle);
//
AstCCast* castp = new AstCCast(nodep->fileline(), nodep, needsize, nodep->widthMin());
AstCCast* const castp
= new AstCCast{nodep->fileline(), nodep, needsize, nodep->widthMin()};
relinkHandle.relink(castp);
// if (debug() > 8) castp->dumpTree(cout, "-castins: ");
//
@@ -103,7 +104,7 @@ private:
if (!VN_IS(nodep->backp(), NullCheck)) {
AstNRelinker relinkHandle;
nodep->unlinkFrBack(&relinkHandle);
AstNode* newp = new AstNullCheck(nodep->fileline(), nodep);
AstNode* const newp = new AstNullCheck{nodep->fileline(), nodep};
relinkHandle.relink(newp);
}
}
@@ -154,9 +155,10 @@ private:
}
}
virtual void visit(AstVarRef* nodep) override {
if (nodep->access().isReadOnly() && !VN_IS(nodep->backp(), CCast)
&& VN_IS(nodep->backp(), NodeMath) && !VN_IS(nodep->backp(), ArraySel)
&& nodep->backp()->width() && castSize(nodep) != castSize(nodep->varp())) {
AstNode* const backp = nodep->backp();
if (nodep->access().isReadOnly() && !VN_IS(backp, CCast) && VN_IS(backp, NodeMath)
&& !VN_IS(backp, ArraySel) && !VN_IS(backp, RedXor) && backp->width()
&& castSize(nodep) != castSize(nodep->varp())) {
// Cast vars to IData first, else below has upper bits wrongly set
// CData x=3; out = (QData)(x<<30);
insertCast(nodep, castSize(nodep));
@@ -201,6 +203,6 @@ public:
void V3Cast::castAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CastVisitor visitor(nodep); } // Destruct before checking
{ CastVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("cast", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+4 -4
View File
@@ -337,7 +337,7 @@ private:
int filelineWidth() {
if (!m_filelineWidth) {
CdcWidthVisitor visitor(v3Global.rootp());
CdcWidthVisitor visitor{v3Global.rootp()};
m_filelineWidth = visitor.maxWidth();
}
return m_filelineWidth;
@@ -495,7 +495,7 @@ private:
UINFO(3, __FUNCTION__ << ": " << endl);
// Trace all sources and sinks
for (int traceDests = 0; traceDests < 2; ++traceDests) {
for (const bool& traceDests : {false, true}) {
UINFO(9, " Trace Direction " << (traceDests ? "dst" : "src") << endl);
m_graph.userClearVertices(); // user1: bool - was analyzed
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
@@ -508,7 +508,7 @@ private:
const string filename
= v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc_edges.txt";
const std::unique_ptr<std::ofstream> ofp(V3File::new_ofstream(filename));
const std::unique_ptr<std::ofstream> ofp{V3File::new_ofstream(filename)};
if (ofp->fail()) v3fatal("Can't write " << filename);
*ofp << "Edge Report for " << v3Global.opt.prefix() << '\n';
@@ -758,5 +758,5 @@ public:
void V3Cdc::cdcAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
CdcVisitor visitor(nodep);
CdcVisitor visitor{nodep};
}
+58 -42
View File
@@ -47,11 +47,33 @@ public:
AstCFunc* m_tlChgFuncp = nullptr; // Top level change function we're building
int m_numStmts = 0; // Number of statements added to m_chgFuncp
int m_funcNum = 0; // Number of change functions emitted
bool m_madeTopChg = false;
ChangedState() = default;
~ChangedState() = default;
void maybeCreateChgFuncp() {
maybeCreateTopChg();
maybeCreateMidChg();
}
void maybeCreateTopChg() {
if (m_madeTopChg) return;
m_madeTopChg = true;
v3Global.rootp()->changeRequest(true);
// Create a wrapper change detection function that calls each change detection function
m_tlChgFuncp
= new AstCFunc{m_scopetopp->fileline(), "_change_request", m_scopetopp, "QData"};
m_tlChgFuncp->isStatic(false);
m_tlChgFuncp->isLoose(true);
m_tlChgFuncp->declPrivate(true);
m_scopetopp->addActivep(m_tlChgFuncp);
// Each change detection function needs at least one AstChangeDet
// to ensure that V3EmitC outputs the necessary code.
maybeCreateMidChg();
m_chgFuncp->addStmtsp(new AstChangeDet{m_scopetopp->fileline(), nullptr, nullptr, false});
}
void maybeCreateMidChg() {
// Don't create an extra function call if splitting is disabled
if (!v3Global.opt.outputSplitCFuncs()) {
m_chgFuncp = m_tlChgFuncp;
@@ -59,29 +81,29 @@ public:
}
if (!m_chgFuncp || v3Global.opt.outputSplitCFuncs() < m_numStmts) {
m_chgFuncp
= new AstCFunc(m_scopetopp->fileline(), "_change_request_" + cvtToStr(++m_funcNum),
m_scopetopp, "QData");
= new AstCFunc{m_scopetopp->fileline(), "_change_request_" + cvtToStr(++m_funcNum),
m_scopetopp, "QData"};
m_chgFuncp->isStatic(false);
m_chgFuncp->isLoose(true);
m_chgFuncp->declPrivate(true);
m_scopetopp->addActivep(m_chgFuncp);
// Add a top call to it
AstCCall* callp = new AstCCall(m_scopetopp->fileline(), m_chgFuncp);
AstCCall* const callp = new AstCCall{m_scopetopp->fileline(), m_chgFuncp};
if (!m_tlChgFuncp->stmtsp()) {
m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp));
m_tlChgFuncp->addStmtsp(new AstCReturn{m_scopetopp->fileline(), callp});
} else {
AstCReturn* returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn);
AstCReturn* const returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn);
UASSERT_OBJ(returnp, m_scopetopp, "Lost CReturn in top change function");
// This is currently using AstLogOr which will shortcut the
// evaluation if any function returns true. This is likely what
// we want and is similar to the logic already in use inside
// V3EmitC, however, it also means that verbose logging may
// miss to print change detect variables.
AstNode* newp = new AstCReturn(
AstNode* const newp = new AstCReturn{
m_scopetopp->fileline(),
new AstLogOr(m_scopetopp->fileline(), callp, returnp->lhsp()->unlinkFrBack()));
new AstLogOr{m_scopetopp->fileline(), callp, returnp->lhsp()->unlinkFrBack()}};
returnp->replaceWith(newp);
VL_DO_DANGLING(returnp->deleteTree(), returnp);
}
@@ -123,14 +145,17 @@ private:
}
m_statep->maybeCreateChgFuncp();
AstChangeDet* changep = new AstChangeDet(m_vscp->fileline(), m_varEqnp->cloneTree(true),
m_newRvEqnp->cloneTree(true), false);
AstChangeDet* const changep = new AstChangeDet{
m_vscp->fileline(), m_varEqnp->cloneTree(true), m_newRvEqnp->cloneTree(true), false};
m_statep->m_chgFuncp->addStmtsp(changep);
AstAssign* initp = new AstAssign(m_vscp->fileline(), m_newLvEqnp->cloneTree(true),
m_varEqnp->cloneTree(true));
AstAssign* const initp = new AstAssign{m_vscp->fileline(), m_newLvEqnp->cloneTree(true),
m_varEqnp->cloneTree(true)};
m_statep->m_chgFuncp->addFinalsp(initp);
EmitCBaseCounterVisitor visitor(initp);
m_statep->m_numStmts += visitor.count();
// Later code will expand words which adds to GCC compile time,
// so add penalty based on word width also
EmitCBaseCounterVisitor visitor{initp};
m_statep->m_numStmts += visitor.count() + m_varEqnp->widthWords();
}
virtual void visit(AstBasicDType*) override { //
@@ -145,11 +170,11 @@ private:
VL_RESTORER(m_newLvEqnp);
VL_RESTORER(m_newRvEqnp);
{
m_varEqnp = new AstArraySel(nodep->fileline(), m_varEqnp->cloneTree(true), index);
m_varEqnp = new AstArraySel{nodep->fileline(), m_varEqnp->cloneTree(true), index};
m_newLvEqnp
= new AstArraySel(nodep->fileline(), m_newLvEqnp->cloneTree(true), index);
= new AstArraySel{nodep->fileline(), m_newLvEqnp->cloneTree(true), index};
m_newRvEqnp
= new AstArraySel(nodep->fileline(), m_newRvEqnp->cloneTree(true), index);
= new AstArraySel{nodep->fileline(), m_newRvEqnp->cloneTree(true), index};
iterate(nodep->subDTypep()->skipRefp());
@@ -180,26 +205,30 @@ private:
public:
// CONSTRUCTORS
ChangedInsertVisitor(AstVarScope* vscp, ChangedState* statep) {
// DPI export trigger should never need change detect. See similar assertions in V3Order
// (OrderVisitor::nodeMarkCircular), and V3GenClk (GenClkRenameVisitor::genInpClk).
UASSERT_OBJ(vscp != v3Global.rootp()->dpiExportTriggerp(), vscp,
"DPI export trigger should not need change detect");
m_statep = statep;
m_vscp = vscp;
m_detects = 0;
{
AstVar* varp = m_vscp->varp();
string newvarname
= ("__Vchglast__" + m_vscp->scopep()->nameDotless() + "__" + varp->shortName());
AstVar* const varp = m_vscp->varp();
string newvarname{"__Vchglast__" + m_vscp->scopep()->nameDotless() + "__"
+ varp->shortName()};
// Create: VARREF(_last)
// ASSIGN(VARREF(_last), VARREF(var))
// ...
// CHANGEDET(VARREF(_last), VARREF(var))
AstVar* newvarp
= new AstVar(varp->fileline(), AstVarType::MODULETEMP, newvarname, varp);
AstVar* const newvarp
= new AstVar{varp->fileline(), AstVarType::MODULETEMP, newvarname, varp};
m_statep->m_topModp->addStmtp(newvarp);
m_newvscp = new AstVarScope(m_vscp->fileline(), m_statep->m_scopetopp, newvarp);
m_newvscp = new AstVarScope{m_vscp->fileline(), m_statep->m_scopetopp, newvarp};
m_statep->m_scopetopp->addVarp(m_newvscp);
m_varEqnp = new AstVarRef(m_vscp->fileline(), m_vscp, VAccess::READ);
m_newLvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, VAccess::WRITE);
m_newRvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, VAccess::READ);
m_varEqnp = new AstVarRef{m_vscp->fileline(), m_vscp, VAccess::READ};
m_newLvEqnp = new AstVarRef{m_vscp->fileline(), m_newvscp, VAccess::WRITE};
m_newRvEqnp = new AstVarRef{m_vscp->fileline(), m_newvscp, VAccess::READ};
}
iterate(vscp->dtypep()->skipRefp());
m_varEqnp->deleteTree();
@@ -228,7 +257,7 @@ private:
void genChangeDet(AstVarScope* vscp) {
vscp->v3warn(IMPERFECTSCH, "Imperfect scheduling of variable: " << vscp->prettyNameQ());
ChangedInsertVisitor visitor(vscp, m_statep);
ChangedInsertVisitor visitor{vscp, m_statep};
}
// VISITORS
@@ -242,24 +271,11 @@ private:
UINFO(4, " TS " << nodep << endl);
// Clearing
AstNode::user1ClearTree();
// Create the change detection function
AstScope* scopep = nodep->scopep();
// Prep for if make change detection function
AstScope* const scopep = nodep->scopep();
UASSERT_OBJ(scopep, nodep, "No scope found on top level, perhaps you have no statements?");
m_statep->m_scopetopp = scopep;
// Create a wrapper change detection function that calls each change detection function
m_statep->m_tlChgFuncp
= new AstCFunc(nodep->fileline(), "_change_request", scopep, "QData");
m_statep->m_tlChgFuncp->isStatic(false);
m_statep->m_tlChgFuncp->isLoose(true);
m_statep->m_tlChgFuncp->declPrivate(true);
m_statep->m_scopetopp->addActivep(m_statep->m_tlChgFuncp);
// Each change detection function needs at least one AstChangeDet
// to ensure that V3EmitC outputs the necessary code.
m_statep->maybeCreateChgFuncp();
m_statep->m_chgFuncp->addStmtsp(
new AstChangeDet(nodep->fileline(), nullptr, nullptr, false));
iterateChildren(nodep);
}
virtual void visit(AstVarScope* nodep) override {
@@ -288,7 +304,7 @@ void V3Changed::changedAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{
ChangedState state;
ChangedVisitor visitor(nodep, &state);
ChangedVisitor visitor{nodep, &state};
} // Destruct before checking
V3Global::dumpCheckGlobalTree("changed", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+1 -1
View File
@@ -153,6 +153,6 @@ public:
void V3Class::classAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ClassVisitor visitor(nodep); } // Destruct before checking
{ ClassVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("class", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+1 -1
View File
@@ -314,6 +314,6 @@ public:
void V3Clean::cleanAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CleanVisitor visitor(nodep); } // Destruct before checking
{ CleanVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("clean", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+10 -1
View File
@@ -136,6 +136,7 @@ private:
"Unsupported: Complicated event expression in sensitive activity list");
return nullptr;
}
UASSERT_OBJ(nodep->varrefp(), nodep, "No clock found on sense item");
AstVarScope* clkvscp = nodep->varrefp()->varScopep();
if (nodep->edgeType() == VEdgeType::ET_POSEDGE) {
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
@@ -257,6 +258,14 @@ private:
// Process the activates
iterateChildren(nodep);
UINFO(4, " TOPSCOPE iter done " << nodep << endl);
// Clear the DPI export trigger flag at the end of eval
if (AstVarScope* const dpiExportTriggerp = v3Global.rootp()->dpiExportTriggerp()) {
FileLine* const fl = dpiExportTriggerp->fileline();
AstAssign* const assignp
= new AstAssign{fl, new AstVarRef{fl, dpiExportTriggerp, VAccess::WRITE},
new AstConst{fl, AstConst::BitFalse{}}};
m_evalFuncp->addFinalsp(assignp);
}
// Split large functions
splitCheck(m_evalFuncp);
splitCheck(m_initFuncp);
@@ -435,6 +444,6 @@ public:
void V3Clock::clockAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ClockVisitor visitor(nodep); } // Destruct before checking
{ ClockVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("clock", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+1 -1
View File
@@ -223,6 +223,6 @@ public:
void V3Combine::combineAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CombineVisitor visitor(nodep); } // Destruct before checking
{ CombineVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("combine", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+120
View File
@@ -0,0 +1,120 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Add common contents to modules
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
// V3Common's Transformations:
//
// Each class:
// Create string access functions
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include "V3Common.h"
#include "V3Ast.h"
#include "V3Global.h"
#include "V3EmitCBase.h"
//######################################################################
// Common component builders
static void makeVlToString(AstClass* nodep) {
AstCFunc* const funcp
= new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"};
funcp->argTypes("const VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(nodep) + ">& obj");
funcp->isMethod(false);
funcp->isConst(false);
funcp->isStatic(false);
funcp->protect(false);
AstNode* const exprp = new AstCMath{nodep->fileline(), "obj ? obj->to_string() : \"null\"", 0};
exprp->dtypeSetString();
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
nodep->addStmtp(funcp);
}
static void makeToString(AstClass* nodep) {
AstCFunc* const funcp = new AstCFunc{nodep->fileline(), "to_string", nullptr, "std::string"};
funcp->isConst(true);
funcp->isStatic(false);
funcp->protect(false);
AstNode* const exprp
= new AstCMath{nodep->fileline(), R"(std::string("'{") + to_string_middle() + "}")", 0};
exprp->dtypeSetString();
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
nodep->addStmtp(funcp);
}
static void makeToStringMiddle(AstClass* nodep) {
AstCFunc* const funcp
= new AstCFunc{nodep->fileline(), "to_string_middle", nullptr, "std::string"};
funcp->isConst(true);
funcp->isStatic(false);
funcp->protect(false);
funcp->addStmtsp(new AstCStmt{nodep->fileline(), "std::string out;\n"});
std::string comma;
for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) {
if (auto* const varp = VN_CAST(itemp, Var)) {
if (!varp->isParam()) {
string stmt = "out += \"";
stmt += comma;
comma = ", ";
stmt += itemp->origNameProtect();
stmt += ":\" + ";
if (itemp->isWide()) {
stmt += "VL_TO_STRING_W(";
stmt += cvtToStr(itemp->widthWords());
stmt += ", ";
} else {
stmt += "VL_TO_STRING(";
}
stmt += itemp->nameProtect();
stmt += ");\n";
nodep->user1(true); // So what we extend dumps this
funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt});
}
}
}
if (nodep->extendsp() && nodep->extendsp()->classp()->user1()) {
string stmt = "out += \"";
if (!comma.empty()) stmt += "\", \"+ ";
// comma = ", "; // Nothing further so not needed
stmt += nodep->extendsp()->dtypep()->nameProtect();
stmt += "::to_string_middle();\n";
nodep->user1(true); // So what we extend dumps this
funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt});
}
funcp->addStmtsp(new AstCStmt{nodep->fileline(), "return out;\n"});
nodep->addStmtp(funcp);
}
//######################################################################
// V3Common class functions
void V3Common::commonAll() {
UINFO(2, __FUNCTION__ << ": " << endl);
// Create common contents for each module
for (AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) {
if (AstClass* const classp = VN_CAST(nodep, Class)) {
// NODE STATE
// Entire netlist:
// AstClass::user1() -> bool. True if class needs to_string dumper
AstUser1InUse m_inuser1;
// Create ToString methods
makeVlToString(classp);
makeToString(classp);
makeToStringMiddle(classp);
}
}
V3Global::dumpCheckGlobalTree("common", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+30
View File
@@ -0,0 +1,30 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Add common contents to modules
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef VERILATOR_V3COMMON_H_
#define VERILATOR_V3COMMON_H_
#include "config_build.h"
#include "verilatedos.h"
//============================================================================
class V3Common final {
public:
static void commonAll();
};
#endif // Guard
+34 -4
View File
@@ -24,6 +24,7 @@
#include <map>
#include <set>
#include <string>
#include <unordered_map>
//######################################################################
// Resolve wildcards in files, modules, ftasks or variables
@@ -346,6 +347,9 @@ using V3ConfigFileResolver = V3ConfigWildcardResolver<V3ConfigFile>;
class V3ConfigResolver final {
V3ConfigModuleResolver m_modules; // Access to module names (with wildcards)
V3ConfigFileResolver m_files; // Access to file names (with wildcards)
std::unordered_map<string, std::unordered_map<string, vluint64_t>>
m_profileData; // Access to profile_data records
FileLine* m_profileFileLine = nullptr;
static V3ConfigResolver s_singleton; // Singleton (not via local static, as that's slow)
V3ConfigResolver() = default;
@@ -356,6 +360,20 @@ public:
V3ConfigModuleResolver& modules() { return m_modules; }
V3ConfigFileResolver& files() { return m_files; }
void addProfileData(FileLine* fl, const string& model, const string& key, vluint64_t cost) {
if (!m_profileFileLine) m_profileFileLine = fl;
if (cost == 0) cost = 1; // Cost 0 means delete (or no data)
m_profileData[model][key] += cost;
}
vluint64_t getProfileData(const string& model, const string& key) const {
const auto mit = m_profileData.find(model);
if (mit == m_profileData.cend()) return 0;
const auto it = mit->second.find(key);
if (it == mit->second.cend()) return 0;
return it->second;
}
FileLine* getProfileDataFileLine() const { return m_profileFileLine; } // Maybe null
};
V3ConfigResolver V3ConfigResolver::s_singleton;
@@ -392,10 +410,6 @@ void V3Config::addIgnore(V3ErrorCode code, bool on, const string& filename, int
}
}
void V3Config::addModulePragma(const string& module, AstPragmaType pragma) {
V3ConfigResolver::s().modules().at(module).addModulePragma(pragma);
}
void V3Config::addInline(FileLine* fl, const string& module, const string& ftask, bool on) {
if (ftask.empty()) {
V3ConfigResolver::s().modules().at(module).setInline(on);
@@ -408,6 +422,15 @@ void V3Config::addInline(FileLine* fl, const string& module, const string& ftask
}
}
void V3Config::addModulePragma(const string& module, AstPragmaType pragma) {
V3ConfigResolver::s().modules().at(module).addModulePragma(pragma);
}
void V3Config::addProfileData(FileLine* fl, const string& model, const string& key,
vluint64_t cost) {
V3ConfigResolver::s().addProfileData(fl, model, key, cost);
}
void V3Config::addVarAttr(FileLine* fl, const string& module, const string& ftask,
const string& var, AstAttrType attr, AstSenTree* sensep) {
// Semantics: sensep only if public_flat_rw
@@ -497,6 +520,13 @@ void V3Config::applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar
if (vp) vp->apply(varp);
}
vluint64_t V3Config::getProfileData(const string& model, const string& key) {
return V3ConfigResolver::s().getProfileData(model, key);
}
FileLine* V3Config::getProfileDataFileLine() {
return V3ConfigResolver::s().getProfileDataFileLine();
}
bool V3Config::waive(FileLine* filelinep, V3ErrorCode code, const string& message) {
V3ConfigFile* filep = V3ConfigResolver::s().files().resolve(filelinep->filename());
if (!filep) return false;
+8 -2
View File
@@ -33,17 +33,23 @@ public:
static void addCoverageBlockOff(const string& file, int lineno);
static void addCoverageBlockOff(const string& module, const string& blockname);
static void addIgnore(V3ErrorCode code, bool on, const string& filename, int min, int max);
static void addWaiver(V3ErrorCode code, const string& filename, const string& message);
static void addModulePragma(const string& module, AstPragmaType pragma);
static void addInline(FileLine* fl, const string& module, const string& ftask, bool on);
static void addModulePragma(const string& module, AstPragmaType pragma);
static void addProfileData(FileLine* fl, const string& model, const string& key,
vluint64_t cost);
static void addWaiver(V3ErrorCode code, const string& filename, const string& message);
static void addVarAttr(FileLine* fl, const string& module, const string& ftask,
const string& signal, AstAttrType type, AstSenTree* nodep);
static void applyCase(AstCase* nodep);
static void applyCoverageBlock(AstNodeModule* modulep, AstBegin* nodep);
static void applyIgnores(FileLine* filelinep);
static void applyModule(AstNodeModule* modulep);
static void applyFTask(AstNodeModule* modulep, AstNodeFTask* ftaskp);
static void applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar* varp);
static uint64_t getProfileData(const string& model, const string& key);
static FileLine* getProfileDataFileLine();
static bool waive(FileLine* filelinep, V3ErrorCode code, const string& message);
};
+560 -288
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -543,6 +543,6 @@ public:
void V3Coverage::coverage(AstNetlist* rootp) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CoverageVisitor visitor(rootp); } // Destruct before checking
{ CoverageVisitor visitor{rootp}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("coverage", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+1 -1
View File
@@ -114,6 +114,6 @@ public:
void V3CoverageJoin::coverageJoin(AstNetlist* rootp) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CoverageJoinVisitor visitor(rootp); } // Destruct before checking
{ CoverageJoinVisitor visitor{rootp}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("coveragejoin", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+6 -6
View File
@@ -318,7 +318,7 @@ private:
// And its children may now be killable too; correct counts
// Recurse, as cells may not be directly under the module but in a generate
if (!modp->dead()) { // If was dead didn't increment user1's
DeadModVisitor visitor(modp);
DeadModVisitor visitor{modp};
}
VL_DO_DANGLING(modp->unlinkFrBack()->deleteTree(), modp);
retry = true;
@@ -463,31 +463,31 @@ public:
void V3Dead::deadifyModules(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor visitor(nodep, false, false, false, false); } // Destruct before checking
{ DeadVisitor visitor{nodep, false, false, false, false}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadModules", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
}
void V3Dead::deadifyDTypes(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor visitor(nodep, false, true, false, false); } // Destruct before checking
{ DeadVisitor visitor{nodep, false, true, false, false}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadDtypes", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
void V3Dead::deadifyDTypesScoped(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor visitor(nodep, false, true, true, false); } // Destruct before checking
{ DeadVisitor visitor{nodep, false, true, true, false}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0,
v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
void V3Dead::deadifyAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor visitor(nodep, true, true, false, true); } // Destruct before checking
{ DeadVisitor visitor{nodep, true, true, false, true}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadAll", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
void V3Dead::deadifyAllScoped(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor visitor(nodep, true, true, true, true); } // Destruct before checking
{ DeadVisitor visitor{nodep, true, true, true, true}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadAllScoped", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+1 -1
View File
@@ -510,6 +510,6 @@ public:
void V3Delayed::delayedAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DelayedVisitor visitor(nodep); } // Destruct before checking
{ DelayedVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("delayed", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+13 -19
View File
@@ -29,6 +29,7 @@
#include "V3Global.h"
#include "V3Depth.h"
#include "V3Ast.h"
#include "V3UniqueNames.h"
#include <algorithm>
@@ -39,11 +40,11 @@ private:
// NODE STATE
// STATE
AstNodeModule* m_modp = nullptr; // Current module
AstCFunc* m_cfuncp = nullptr; // Current block
AstNode* m_stmtp = nullptr; // Current statement
int m_depth = 0; // How deep in an expression
int m_maxdepth = 0; // Maximum depth in an expression
V3UniqueNames m_tempNames; // For generating unique temporary variable names
// METHODS
VL_DEBUG_FUNC; // Declare debug()
@@ -51,18 +52,16 @@ private:
void createDeepTemp(AstNode* nodep) {
UINFO(6, " Deep " << nodep << endl);
// if (debug() >= 9) nodep->dumpTree(cout, "deep:");
const string newvarname = (string("__Vdeeptemp") + cvtToStr(m_modp->varNumGetInc()));
AstVar* varp
= new AstVar(nodep->fileline(), AstVarType::STMTTEMP, newvarname, nodep->dtypep());
AstVar* const varp = new AstVar{nodep->fileline(), AstVarType::STMTTEMP,
m_tempNames.get(nodep), nodep->dtypep()};
UASSERT_OBJ(m_cfuncp, nodep, "Deep expression not under a function");
m_cfuncp->addInitsp(varp);
// Replace node tree with reference to var
AstVarRef* newp = new AstVarRef(nodep->fileline(), varp, VAccess::READ);
AstVarRef* const newp = new AstVarRef{nodep->fileline(), varp, VAccess::READ};
nodep->replaceWith(newp);
// Put assignment before the referencing statement
AstAssign* assp = new AstAssign(
nodep->fileline(), new AstVarRef(nodep->fileline(), varp, VAccess::WRITE), nodep);
AstAssign* const assp = new AstAssign{
nodep->fileline(), new AstVarRef{nodep->fileline(), varp, VAccess::WRITE}, nodep};
AstNRelinker linker2;
m_stmtp->unlinkFrBack(&linker2);
assp->addNext(m_stmtp);
@@ -70,21 +69,13 @@ private:
}
// VISITORS
virtual void visit(AstNodeModule* nodep) override {
UINFO(4, " MOD " << nodep << endl);
VL_RESTORER(m_modp);
{
m_modp = nodep;
m_cfuncp = nullptr;
iterateChildren(nodep);
}
}
virtual void visit(AstCFunc* nodep) override {
VL_RESTORER(m_cfuncp);
{
m_cfuncp = nodep;
m_depth = 0;
m_maxdepth = 0;
m_tempNames.reset();
iterateChildren(nodep);
}
}
@@ -149,7 +140,10 @@ private:
public:
// CONSTRUCTORS
explicit DepthVisitor(AstNetlist* nodep) { iterate(nodep); }
explicit DepthVisitor(AstNetlist* nodep)
: m_tempNames{"__Vdeeptemp"} {
iterate(nodep);
}
virtual ~DepthVisitor() override = default;
};
@@ -158,6 +152,6 @@ public:
void V3Depth::depthAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DepthVisitor visitor(nodep); } // Destruct before checking
{ DepthVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("depth", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
}
+1 -1
View File
@@ -129,6 +129,6 @@ public:
void V3DepthBlock::depthBlockAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DepthBlockVisitor visitor(nodep); } // Destruct before checking
{ DepthBlockVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deepblock", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+30 -7
View File
@@ -73,6 +73,7 @@ private:
// module.
string descopedSelfPointer(const AstScope* scopep) {
UASSERT(scopep, "Var/Func not scoped");
UASSERT(!VN_IS(scopep->modp(), Class), "References to classes handled elsewhere");
// Static functions can't use relative references via 'this->'
const bool relativeRefOk = !m_funcp->isStatic();
@@ -83,9 +84,7 @@ private:
if (relativeRefOk && scopep == m_scopep) {
return "this";
} else if (VN_IS(scopep->modp(), Class)) {
return "this";
} else if (!m_modSingleton && relativeRefOk && scopep->aboveScopep() == m_scopep
} else if (relativeRefOk && !m_modSingleton && scopep->aboveScopep() == m_scopep
&& VN_IS(scopep->modp(), Module)) {
// Reference to scope of instance directly under this module, can just "this->cell",
// which can potentially be V3Combined, but note this requires one extra pointer
@@ -183,6 +182,10 @@ private:
}
// VISITORS
virtual void visit(AstNetlist* nodep) override {
nodep->dpiExportTriggerp(nullptr);
iterateChildren(nodep);
}
virtual void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
{
@@ -215,20 +218,40 @@ private:
UASSERT_OBJ(m_scopep, nodep, "Node not under scope");
const AstVar* const varp = nodep->varScopep()->varp();
const AstScope* const scopep = nodep->varScopep()->scopep();
if (!varp->isFuncLocal()) { nodep->selfPointer(descopedSelfPointer(scopep)); }
if (varp->isFuncLocal()) {
// Reference to function locals need no self pointer
nodep->selfPointer("");
} else if (scopep->modp() == v3Global.rootp()->constPoolp()->modp()) {
// Reference to constant pool value need no self pointer
nodep->selfPointer("");
} else if (VN_IS(scopep->modp(), Class)) {
// Direct reference to class members are from within the class itself, references from
// outside the class must go via AstMemberSel
nodep->selfPointer("this");
} else {
nodep->selfPointer(descopedSelfPointer(scopep));
}
nodep->varScopep(nullptr);
UINFO(9, " refout " << nodep << endl);
}
virtual void visit(AstNodeCCall* nodep) override {
virtual void visit(AstCCall* nodep) override {
// UINFO(9, " " << nodep << endl);
iterateChildren(nodep);
// Convert the hierch name
UASSERT_OBJ(m_scopep, nodep, "Node not under scope");
const AstScope* const scopep = nodep->funcp()->scopep();
nodep->selfPointer(descopedSelfPointer(scopep));
if (VN_IS(scopep->modp(), Class)) {
// Direct call to class methods are from within the class itself, method calls from
// outside the class must go via AstCMethodCall
nodep->selfPointer("this");
} else {
nodep->selfPointer(descopedSelfPointer(scopep));
}
// Can't do this, as we may have more calls later
// nodep->funcp()->scopep(nullptr);
}
virtual void visit(AstCMethodCall* nodep) override { iterateChildren(nodep); }
virtual void visit(AstCNew* nodep) override { iterateChildren(nodep); }
virtual void visit(AstCFunc* nodep) override {
VL_RESTORER(m_funcp);
if (!nodep->user1()) {
@@ -266,6 +289,6 @@ public:
void V3Descope::descopeAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DescopeVisitor visitor(nodep); } // Destruct before checking
{ DescopeVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("descope", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
+1 -1
View File
@@ -44,7 +44,7 @@ V3DupFinder::iterator V3DupFinder::findDuplicate(AstNode* nodep, V3DupFinderUser
}
void V3DupFinder::dumpFile(const string& filename, bool tree) {
const std::unique_ptr<std::ofstream> logp(V3File::new_ofstream(filename));
const std::unique_ptr<std::ofstream> logp{V3File::new_ofstream(filename)};
if (logp->fail()) v3fatal("Can't write " << filename);
std::unordered_map<int, int> dist;
-1133
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -27,12 +27,12 @@
class V3EmitC final {
public:
static void emitc();
static void emitcConstPool();
static void emitcHeaders();
static void emitcImp();
static void emitcInlines();
static void emitcModel();
static void emitcSyms(bool dpiHdrOnly = false);
static void emitcTrace();
static void emitcFiles();
};
+42 -7
View File
@@ -20,11 +20,26 @@
#include "V3EmitCBase.h"
#include "V3Task.h"
//######################################################################
// EmitCParentModule implementation
EmitCParentModule::EmitCParentModule() {
const auto setAll = [](AstNodeModule* modp) -> void {
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (VN_IS(nodep, CFunc) || VN_IS(nodep, Var)) { nodep->user4p(modp); }
}
};
for (AstNode* modp = v3Global.rootp()->modulesp(); modp; modp = modp->nextp()) {
setAll(VN_CAST(modp, NodeModule));
}
setAll(v3Global.rootp()->constPoolp()->modp());
}
//######################################################################
// EmitCBaseVisitor implementation
string EmitCBaseVisitor::funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp) {
modp = modp ? modp : VN_CAST(nodep->user4p(), NodeModule);
modp = modp ? modp : EmitCParentModule::get(nodep);
string name;
if (nodep->isConstructor()) {
name += prefixNameProtect(modp);
@@ -54,8 +69,7 @@ string EmitCBaseVisitor::cFuncArgs(const AstCFunc* nodep) {
string args;
if (nodep->isLoose() && !nodep->isStatic()) {
if (nodep->isConst().trueKnown()) args += "const ";
AstNodeModule* modp = VN_CAST(nodep->user4p(), NodeModule);
args += prefixNameProtect(modp);
args += prefixNameProtect(EmitCParentModule::get(nodep));
args += "* vlSelf";
}
if (!nodep->argTypes().empty()) {
@@ -82,6 +96,7 @@ string EmitCBaseVisitor::cFuncArgs(const AstCFunc* nodep) {
void EmitCBaseVisitor::emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp,
bool withScope) {
if (funcp->slow()) puts("VL_ATTR_COLD ");
if (!funcp->isConstructor() && !funcp->isDestructor()) {
puts(funcp->rtnTypeVoid());
puts(" ");
@@ -109,12 +124,11 @@ void EmitCBaseVisitor::emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule*
puts("virtual ");
}
emitCFuncHeader(funcp, modp, /* withScope: */ false);
if (funcp->slow()) puts(" VL_ATTR_COLD");
puts(";\n");
if (!funcp->ifdef().empty()) puts("#endif // " + funcp->ifdef() + "\n");
}
void EmitCBaseVisitor::emitVarDecl(const AstVar* nodep, const string& prefixIfImp, bool asRef) {
void EmitCBaseVisitor::emitVarDecl(const AstVar* nodep, bool asRef) {
const AstBasicDType* const basicp = nodep->basicp();
bool refNeedParens = VN_IS(nodep->dtypeSkipRefp(), UnpackArrayDType);
@@ -199,12 +213,12 @@ void EmitCBaseVisitor::emitVarDecl(const AstVar* nodep, const string& prefixIfIm
&& name.substr(name.size() - suffix.size()) == suffix;
if (beStatic) puts("static VL_THREAD_LOCAL ");
}
puts(nodep->vlArgType(true, false, false, prefixIfImp, asRef));
puts(nodep->vlArgType(true, false, false, "", asRef));
puts(";\n");
}
}
void EmitCBaseVisitor::emitModCUse(AstNodeModule* modp, VUseType useType) {
void EmitCBaseVisitor::emitModCUse(const AstNodeModule* modp, VUseType useType) {
string nl;
for (AstNode* itemp = modp->stmtsp(); itemp; itemp = itemp->nextp()) {
if (AstCUse* usep = VN_CAST(itemp, CUse)) {
@@ -221,3 +235,24 @@ void EmitCBaseVisitor::emitModCUse(AstNodeModule* modp, VUseType useType) {
}
puts(nl);
}
void EmitCBaseVisitor::emitTextSection(const AstNodeModule* modp, AstType type) {
int last_line = -999;
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstNodeText* textp = VN_CAST(nodep, NodeText)) {
if (nodep->type() == type) {
if (last_line != nodep->fileline()->lineno()) {
if (last_line < 0) {
puts("\n//*** Below code from `systemc in Verilog file\n");
}
putsDecoration(
ifNoProtect("// From `systemc at " + nodep->fileline()->ascii() + "\n"));
last_line = nodep->fileline()->lineno();
}
ofp()->putsNoTracking(textp->text());
last_line++;
}
}
}
if (last_line > 0) puts("//*** Above code from `systemc in Verilog file\n\n");
}

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