Compare commits

...
357 Commits
Author SHA1 Message Date
Wilson Snyder 57c816f906 Merge from master for release. (update) 2023-09-16 21:44:39 -04:00
Wilson Snyder 8f43e74288 Update CMakeList version 2023-09-16 20:54:54 -04:00
Wilson Snyder 82e1dbe162 Merge from master for release. 2023-09-16 20:43:53 -04:00
Wilson Snyder cef3960a4e Version bump 2023-09-16 18:36:39 -04:00
Wilson Snyder 783b7ecdab Fix clang override warning (last commit) 2023-09-16 17:52:02 -04:00
Wilson Snyder 8bd1c63b32 Support function non-constant default arguments (#4470). 2023-09-16 17:37:25 -04:00
Wilson Snyder e6fb7e970d Fix recursive display causing segfault (#4480). 2023-09-16 10:10:21 -04:00
Wilson Snyder 891cc0f9b6 Fix t_dist_cppstyle Perl performance issue (#4085). 2023-09-16 09:22:12 -04:00
Wilson Snyder 10fbe74cef Commentary: Changes update 2023-09-15 23:25:28 -04:00
Wilson Snyder 05d04a3959 Internals: Fix misnamed member. No functional change. 2023-09-15 23:02:34 -04:00
Wilson Snyder 19f7279542 Fix false INFINITELOOP on forever..mailbox.get() (#4323). 2023-09-15 22:05:55 -04:00
Wilson Snyder aa608472ae Support recursive function calls (#3267). 2023-09-15 20:46:31 -04:00
Wilson Snyder 26ed2a06a0 Tests: Add UVM test that generates ok with known issues commented out. (#1538) (#3267) (#4125) (#4323) (#4349) (#4465) (#4467) (#4468) (#4470) (#4493) (#4494) (#4495) (#4496) (#4497) 2023-09-15 20:26:46 -04:00
Wilson Snyder 07013da13d Tests: Disabled test (#4493) 2023-09-15 19:56:05 -04:00
Wilson Snyder d840c612d4 Fix IGNOREDRETURN to not warn on void-cast static function calls. 2023-09-15 19:01:11 -04:00
Wilson Snyder 188cf85b3f Suppress some debug unknown hash/simulate warnings in UVM. 2023-09-15 18:33:02 -04:00
Wilson Snyder c52ba28dd0 Tests: Fix commentary to unify issue references. 2023-09-15 18:12:11 -04:00
Wilson Snyder 10c1653e72 Fix ZERODLY to not warn on 'wait(0)'. 2023-09-15 08:53:29 -04:00
Wilson Snyder 2fbd41e13b Tests: Add t_queue_persistence.pl as broken test (#3385) (#4489) 2023-09-15 07:59:06 -04:00
Ryszard Rozak 96857c5f95 Fix the error message when the type of ref argument is wrong (#4490) 2023-09-15 07:17:24 -04:00
Wilson Snyder 131eb319e0 Tests: Advance UVM through V3LinkDot Param (#1538 partial) 2023-09-14 20:09:43 -04:00
Wilson Snyder 6e589377f4 Fix reference to extended class in parameterized class (#4466). 2023-09-14 20:07:49 -04:00
Wilson Snyder 2c84b37bf7 Internals: Rename some V3LinkDot variables. No functional change. 2023-09-14 20:06:20 -04:00
Kamil Rakoczy 0d67caff77 Internals: V3Task: refactor common code to connectPort. No functional change intended (#4488)
Signed-off-by: Kamil Rakoczy <[email protected]>
2023-09-14 07:24:49 -04:00
Anthony Donlon 3dde57d539 Fix lint of case statements with enum and wildcard bits (#4464) (#4487) 2023-09-14 07:22:49 -04:00
Kamil Rakoczy ec2e3ec0e4 Update clang_check_attributes to take into account MT_DISABLED (#4479) 2023-09-13 16:32:18 -04:00
Kamil Rakoczy 9fe459c820 Internals: V3LockGuard: Add constructor for adopting already locked mutex. (#4476) 2023-09-13 13:52:59 -04:00
Wilson Snyder 823e0723fb Internals: Remove legacy define. No functional change. 2023-09-13 08:46:51 -04:00
Kamil Rakoczy 8bd6d7c5b1 Internals: Add V3ThreadSafety (#4477) 2023-09-13 07:57:48 -04:00
Wilson Snyder 839a8fa4d9 Fix error on enum with VARHIDDEN of cell (#4482). 2023-09-12 17:47:57 -04:00
Krzysztof Boroński 24917a187a Fix V3CUse, do not consider implementations (.cpp) at all (#4386) 2023-09-12 17:59:57 +02:00
Ryszard Rozak 6c6f03cf7c Fix static cast from a stream type (#4469) (#4485) 2023-09-12 11:06:12 -04:00
Kamil RakoczyandMariusz Glebocki 5676443139 Internals: Rename and slightly change other threads stopping/resuming methods. (#4478)
Co-authored-by: Mariusz Glebocki <[email protected]>
2023-09-11 13:43:26 -04:00
Kamil RakoczyandMariusz Glebocki e77d847671 Check whether V3ERROR_NO_GLOBAL_ is not already defined. (#4475)
Co-authored-by: Mariusz Glebocki <[email protected]>
2023-09-11 09:01:34 -04:00
Kamil RakoczyandMariusz Glebocki 0baee84e96 Move dpiTemporaryVarSuffix() to header. (#4474)
Co-authored-by: Mariusz Glebocki <[email protected]>
2023-09-11 09:01:09 -04:00
Ryszard Rozak c446cc2596 Internals: Remove unused constructor. No functional change. (#4473)
Signed-off-by: Ryszard Rozak <[email protected]>
2023-09-11 07:10:33 -04:00
Ryszard Rozak b66c4153b1 Compute purity of AstCMethodHard (#4460) 2023-09-11 13:06:15 +02:00
Wilson Snyder d72f1b89fc Internals: Minor internal code coverage cleanups 2023-09-10 18:53:51 -04:00
Wilson Snyder b5b278d072 Fix fork crash with no init, test for (#4471) 2023-09-10 08:53:21 -04:00
Wilson Snyder cb5466a90b Tests: UVM passes V3Param stage 2023-09-09 11:54:55 -04:00
Wilson Snyder 4de25369da Tests: Add t_uvm_all 2023-09-08 22:29:23 -04:00
Wilson Snyder cdd8953085 Disable Subst when not pure 2023-09-08 21:52:33 -04:00
Wilson Snyder 2f2f0164ff Internals: Use specific types on some cloneTrees. No functional change. 2023-09-08 21:51:59 -04:00
Kamil Rakoczy 6e977e1024 Generate compile_commands.json using bear (#4463) 2023-09-08 17:26:11 -04:00
Krzysztof Boroński 70b11f91b4 Support block_item_declaration in forks (#4455) 2023-09-08 16:40:14 +02:00
Wilson Snyder 014301587f Unsupported instead of syntax error on parameter var initial values 2023-09-08 08:37:17 -04:00
Krzysztof Bieganski 139e93d371 Internals: Reduce the number of typechecks in graphs (#4398). No functional change intended. 2023-09-08 07:35:52 -04:00
Krzysztof Bieganski fb1fc46b06 Internals: Rework self pointers (#4396) 2023-09-08 07:34:35 -04:00
Wilson Snyder 9882ab6c67 Tests: Remove unstable new test 2023-09-08 07:22:52 -04:00
Ryszard Rozak 1a1f919882 Check the output in t_class_method_str_literal.v (#4459) 2023-09-08 08:51:54 +02:00
Ryszard Rozak 91227d26bb Internals: Rename pure to dpiPure. No functional change. (#4461) 2023-09-08 08:51:19 +02:00
Wilson Snyder 11b5dae88d Support let 2023-09-07 21:45:51 -04:00
Wilson Snyder 0c7d485417 Fix error printing internal name() 2023-09-06 21:07:06 -04:00
Ryszard Rozak c40e34b134 Support assignments of stream expressions on queues to packed values (#4458) 2023-09-06 15:25:48 +02:00
Wilson Snyder 6cdf8da3f7 Fix documentation line wrap (#4456) 2023-09-05 07:25:33 -04:00
Wilson Snyder c65265477f Tests: Enable cover named property 2023-09-03 21:02:42 -04:00
Wilson Snyder de77f1ee5c Improve error that assert-under-assert is unsupported 2023-09-03 09:08:43 -04:00
Wilson Snyder c8c980c49d Fix mis-warning on #() in classes' own functions. 2023-09-02 08:33:56 -04:00
Wilson Snyder ad329612fd Internals: Remove unnecessary cloneTree. 2023-09-01 20:13:20 -04:00
Wilson Snyder c324895cbc Internals: Remove unnecessary cloneTree. 2023-09-01 19:49:57 -04:00
Wilson Snyder 679a4314dc Tests: Avoid side effect on array index 2023-09-01 19:26:50 -04:00
Wilson Snyder 36932d31c4 Fix C++11 missing C++14's std::make_unique. 2023-09-01 18:07:15 -04:00
Wilson Snyder ed25aaaad9 Internals: Fix g++ std11 warning. No functional change. 2023-09-01 17:59:30 -04:00
Wilson Snyder 147aa41ba5 Internals: Fix virt call in constructor warning. No functional change. 2023-09-01 17:58:17 -04:00
Ryszard Rozak 5b7e32581b Internals: Cleanup V3Task. No function change intended. (#4454) 2023-09-01 08:03:16 -04:00
Wilson Snyder 2f90fb2bec Set lifetime for repeat variable (#4451 partial) 2023-08-31 18:34:37 -04:00
Wilson Snyder 8c480fd39e Internals: Fix cppcheck warnings 2023-08-31 18:29:58 -04:00
Krzysztof Bieganski ffbbd438ae Internals: Use runtime type info instead of dynamic_cast for faster graph type checks (#4397) 2023-08-31 18:00:53 -04:00
Anthony Donlon 96ee81fa3f Fix unstable output of VHashSha256 (#4453) 2023-08-31 13:42:37 -04:00
Wilson Snyder 3d2399ea0f Tests: Enable fixed-earlier (#2314) 2023-08-30 18:10:30 -04:00
Wilson Snyder 6d5dde8645 Fix duplicate Vfork functions (#4418) 2023-08-30 17:59:25 -04:00
Wilson Snyder 048c9f3fe2 Support extra commas before $display 2023-08-30 17:31:11 -04:00
Wilson Snyder 373265752f Fix display %x formatting of real. 2023-08-30 17:21:33 -04:00
Wilson Snyder 5e6519c1b0 Commentary 2023-08-30 07:46:39 -04:00
Wilson Snyder ccdb52c1d9 Fix lifetime unknown error on enum.name (#4448). 2023-08-30 07:20:25 -04:00
Yinan Xu b4b74d72f0 Add prepareClone and atClone APIs for Verilated models (#3503) (#4444)
This API is used if the user copies the process using `fork`
and similar OS-level mechanisms. The `at_clone` member function
ensures that all model-allocated resources are re-allocated, such
that the copied child process/model can simulate correctly.

A typical allocated resource is the thread pool, which every model
has its own pool.
2023-08-30 07:02:55 -04:00
Wilson Snyder ca6ab584d0 Internals: Remove cloneTreeNull. No functional change intended. 2023-08-29 20:29:11 -04:00
github action 799a502cac Apply 'make format' 2023-08-29 23:56:25 +00:00
Ethan Sifferman 8d1570db28 Add check for conflicting options e.g. binary+lintonly (#4409) 2023-08-29 19:55:37 -04:00
Wilson Snyder 2a30bbe493 Fix internal error on real conversion (#4447). 2023-08-29 18:07:23 -04:00
Anthony Donlon cf6566b9bc Internal: Optimize program size by refactoring error reporting routines (#4446) 2023-08-29 16:54:32 -04:00
Krzysztof Boroński c3e19f2821 Move variables mutated under fork..join_none/join_any blocks into anonymous objects (#4356) 2023-08-29 20:01:00 +02:00
Ryszard Rozak e24197fd16 Don't move function calls before the expression (#4413) 2023-08-28 15:44:41 +02:00
Anthony Donlon 63db60f646 Fix using type in parameterized classes without #() (#4281) (#4440)
* Check whether a class is parameterized or not with AstClass::isParameterized method

* Fix usage conflict of user2 pointer in V3Param.cpp
2023-08-26 12:06:26 -01:00
Wilson Snyder e66b28823d Internals: Cleanup some spacing. No functional change. 2023-08-26 05:59:56 -04:00
Wilson Snyder 77bd565ef6 Commentary: Changes update 2023-08-25 08:28:26 -04:00
Wilson Snyder 00d63883f8 Fix false MULTITOP on bound interfaces (#4438). 2023-08-25 08:21:15 -04:00
Wilson Snyder 10dd675371 Fix false UNUSEDPARAM on generate localparam (#4427). 2023-08-25 07:59:29 -04:00
Ryszard Rozak 2daa32b98b Support assignments of packed values to stream expressions on queues (#4401) 2023-08-25 11:24:12 +02:00
Ryszard Rozak e8e7912871 Tests: Fix t_net_delay test (#4436) 2023-08-23 13:27:17 -01:00
Ryszard Rozak 90079c2974 Fix nested assignments on the LHS (#4435) 2023-08-23 12:08:22 +02:00
Wilson Snyder 967a8530dd Fix errors on some $past cases (#4425) 2023-08-22 01:49:06 -04:00
Krzysztof Boroński ef4794e36d Support parenthesesless calls to static methods (#4432) 2023-08-22 06:06:34 +01:00
Aleksander Kiryk 7c7c92d2dd Fix coroutine handle movement during queue manipulation (#4431) 2023-08-21 10:22:09 -04:00
Wilson Snyder 95f3dd0535 Commentary (#4429) 2023-08-21 06:23:35 -04:00
Anthony Donlon 768b78e7d0 Fix checking for parameter and port connections in the wrong place (#4428) 2023-08-20 08:55:16 -04:00
Aleksander Kiryk 4370254e73 Fix ++/-- under statements (#4399) 2023-08-19 07:03:21 -04:00
Wilson Snyder 436f72582b Update GTKWave from upstream. 2023-08-19 06:57:43 -04:00
Wilson Snyder af3aba7821 Commentary: Changes update 2023-08-19 04:56:49 -04:00
github action 7479db6ffc Apply 'make format' 2023-08-19 08:52:38 +00:00
Frans Skarman e9cc2786b7 Add --no-trace-top option (#4422) 2023-08-19 04:51:29 -04:00
Anthony Donlon cbdee5a804 Fix Windows filename format, etc. (#3873) (#4421)
* Ignore CLion project files and CMake outputs
* Supporting stripping file path that contains backslash
* Set /bigobj flag and increase stack size for windows platform
* Fix MSVC warnings
2023-08-16 07:34:57 -04:00
Anthony Donlon 0f66262fa1 Internals: Refactor AstNode::checkTreeIter for better stack size (#4420) 2023-08-16 07:32:39 -04:00
Jose Loyola 6c80457262 CI: Refactor docker build action to define DOCKER_HUB_NAMESPACE as repo variable (#4419) 2023-08-15 06:45:07 +02:00
Wilson Snyder 16008acdf1 Internals: Add disabled duplicate function check (#4418) 2023-08-12 10:51:35 -04:00
Wilson Snyder 5447ed2629 Fix multple function definitions in V3Sched (#4416). 2023-08-12 10:41:44 -04:00
Wilson Snyder 9eba61018a Tests: Ignore debug message in error line 2023-08-12 09:39:02 -04:00
Wilson Snyder bc97319402 Internals: State commentary. No functional change. 2023-08-12 08:55:08 -04:00
Wilson Snyder 709db5c648 Internals: Avoid unordered_set 2023-08-12 08:46:25 -04:00
Krzysztof Boroński b752faa107 Fix jumping over object initialization (#4411) 2023-08-11 18:28:37 +02:00
Krzysztof Boroński 8d512c3187 Fix variable lifetimes in extern methods (#4414) 2023-08-11 16:52:59 +02:00
Ryszard Rozak 2d9bc73709 Fix dtype of condition operation on class objects (#4345) (#4352) 2023-08-07 05:54:30 -04:00
Ryszard Rozak b0942ed8c7 Fix detection of mixed blocking and nonblocking assignment in nested assignments (#4404) 2023-08-07 11:35:44 +02:00
Wilson Snyder e6b0bdd4df Merge from master for release. 2023-08-06 14:17:13 -04:00
Wilson Snyder b1c1aa53a9 devel release 2023-08-06 10:53:16 -04:00
Wilson Snyder f79749e7b2 Version bump 2023-08-06 10:39:35 -04:00
Krzysztof Bieganski 1a77030c9b Internals: Speedup to only typecheck node child getters in debug builds (#4394) 2023-08-05 03:24:00 -04:00
Wilson Snyder 817ade27fb Commentary: Changes update 2023-08-05 03:18:00 -04:00
Aleksander Kiryk 4afa14b612 Internals: Remove m_pkgp and m_classp from WidthVisitor (#4402) 2023-08-05 03:07:23 -04:00
Krzysztof Bieganski 9caa79a7ea Internals: Remove the name field from AstVarRef (#4395) 2023-08-03 02:52:52 -04:00
Wilson Snyder 446f21d2a0 Tests: Fix more missing internal code coverage 2023-07-27 06:53:43 -04:00
Wilson Snyder 06521f6cf1 Tests: Fix some missing coverage, and cleanup some error messages 2023-07-27 06:53:43 -04:00
github action 75336670d7 Apply 'make format' 2023-07-27 09:44:05 +00:00
Andrew Miloradovsky 106a9fe67f Fix config_build.h issues (#4380) (#4381) 2023-07-27 05:42:35 -04:00
Wilson Snyder a5eccc199b Commentary: Changes update 2023-07-27 04:01:28 -04:00
Vito Gamberini 837d9f9d77 Require CMake config minimum version 3.13 (#4389) (#4390) 2023-07-26 17:54:18 -04:00
Anthony Donlon 959387b69f Support property declaration with empty parentheses (#4313) (#4317) 2023-07-26 05:18:21 -04:00
Ryszard Rozak fd0703b8c8 Fix selects on unpacked structs (#4359) 2023-07-25 12:50:24 +02:00
Ryszard Rozak 3ff608b9cb Fix non-public methods with wide output (#4364) 2023-07-25 04:55:58 -04:00
Aleksander Kiryk edc0a0e982 Optimize V3Number::opAdd (#4385) 2023-07-24 17:09:27 -04:00
Aleksander Kiryk 1549ff9185 Support more types in wait (#4374) 2023-07-24 08:14:01 -04:00
Wilson Snyder 3454641244 Commentary: Add website badge 2023-07-21 04:11:15 -04:00
Wilson Snyder 5dedf25e3b Commentary: Add website badge 2023-07-21 04:07:29 -04:00
Wilson Snyder 7214c180ae Commentary: Add website badge 2023-07-21 04:06:22 -04:00
Ryszard Rozak aa0ef554b3 Fix order of evaluation of function calls in statements (#4375) 2023-07-21 03:29:11 -04:00
Ryszard Rozak a6015863d8 Support for static method calls as default values of function arguments (#4378) 2023-07-21 03:27:00 -04:00
Aleksander Kiryk 5a694ccb8a Fix assign to input var in methods (#4367) 2023-07-17 09:52:37 -04:00
Aleksander Kiryk f7d09c671a Fix VlProcess not found (#4368) 2023-07-17 09:52:07 -04:00
Ryszard Rozak 4de1b22672 Fix handling of super.new calls (#4366) 2023-07-17 13:30:42 +02:00
Krzysztof Boroński 4bdda3f240 Improve propagation of process requirement and decouple it from suspendability (#4321) 2023-07-14 17:12:02 +02:00
Ryszard Rozak ced1dfeffd Fix select operation on assoc array with wide keys (#4360) 2023-07-12 18:48:24 +02:00
Krzysztof Boroński 0774f825cf Internals: Use pointer to const nodes in V3MemberMap (#4353) 2023-07-12 11:36:24 -04:00
Ryszard Rozak 23fe5c1b93 Fix string concatenations (#4354) 2023-07-10 12:58:54 -04:00
Wilson Snyder bd4afeea69 Tests: Fix removing line numbers (#4352) 2023-07-10 08:17:49 -04:00
Julian Daube 3ee9b33f8a Fix cmake astgen for Rocky Linux 8.7 (#4343) 2023-07-09 06:02:21 +05:00
Wilson Snyder 5be0813893 Explicity reference std:: in header files (#4338). No functional change intended. 2023-07-08 13:20:40 -04:00
Wilson Snyder f94c1b32e5 Internals: Make V3MemberMap to remove member cache (#4350) 2023-07-08 12:40:27 -04:00
Wilson Snyder ff4923cf67 Internals: Make V3MemberMap towards removing member cache (#4350) 2023-07-08 12:27:50 -04:00
Krzysztof Bieganski 5788bc8048 Propagate class timescale to class package (#4348)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2023-07-07 17:27:33 +02:00
Wilson SnyderandAhmed El-Mahmoudy 5749662194 Documentation: Drop analytics_id unless VERILATOR_ANALYTICS_ID set (#4333)
Co-authored-by: Ahmed El-Mahmoudy <[email protected]>
2023-07-07 10:44:33 -04:00
Krzysztof Bieganski d5d1cc47e0 Fix multiple edge timing controls in class methods (#4318) (#4320) (#4344)
Multiple edge timing controls in class methods would cause compilation errors on
the generated C++ code. This is because the `SenExprBuilder` used for these
would get recreated per timing control, resulting in duplicate variable names.
The fix is to have a single `SenExprBuilder` per scope.
2023-07-07 08:19:49 -04:00
Ryszard Rozak da043ca16d Fix comparison of class objects (#4346) 2023-07-07 16:20:23 +05:00
Aleksander Kiryk 9aa90569bf Support string replication with variable (#4341) 2023-07-07 13:05:06 +05:00
Wilson Snyder 294d78e0e2 Backout unstable dbb35ab8 (#4318) (#4320) 2023-07-05 14:26:28 -04:00
Wilson Snyder 8182c3fe2c Tests: Fix t_vpi_unimpl exiting early with missed error 2023-07-05 14:12:22 -04:00
Wilson Snyder 6f7bc38088 Tests: Improve specparam verilog.y coverage 2023-07-05 14:11:55 -04:00
Wilson Snyder dcbc0ccfdc Commentary: Revert lost name. 2023-07-05 13:55:47 -04:00
Aleksander Kiryk 399319dc1c Fix wildcard referring types (#4336) (#4342) 2023-07-05 13:08:00 -04:00
Krzysztof Bieganski 35d6da391b Fix multiple edge timing controls in class methods (#4318) (#4320)
Multiple edge timing controls in class methods would cause compilation errors on the generated C++ code. This is because the `SenExprBuilder` used for these would get recreated per timing control, resulting in duplicate variable names. The fix is to have a single `SenExprBuilder` per scope.
2023-07-05 10:36:10 +00:00
Aleksander Kiryk fcbd7c87af Fix unexpected RefDType on assoc arrays (#4337) 2023-07-04 18:13:22 +05:00
Ryszard Rozak 97feba6898 Support locator methods with "with" on assoc arrays (#4335) 2023-07-04 18:11:15 +05:00
Aleksander Kiryk 9249ffdb84 Fix queue slicing (#4329) 2023-07-03 13:55:24 -04:00
أحمد المحمودي 71df9e3381 Commentary: Fix typo of concurrent (#4332) 2023-07-02 16:12:09 -04:00
Wilson Snyder 3c964147be Add MISINDENT lint warning for misleading indentation. 2023-07-01 10:45:25 -04:00
Wilson Snyder 54b4b6b378 Internals: Fix missing calls to cleanFileline. 2023-07-01 08:59:26 -04:00
Wilson Snyder cff37f0775 Add GENUNNAMED lint warning.
Also fix generate-for blocks with empty statements getting lost.
2023-07-01 08:31:53 -04:00
Wilson Snyder 8b1cc3b303 Commentary: Changes update 2023-07-01 07:35:58 -04:00
Mariusz Glebocki 65b6121186 Revert .gdbinit modified by accident. (#4330)
`test_regress/.gdbinit` has been modified in
d42f9c095b most likely by accident.
Restore its previous content.
2023-06-30 16:57:31 -04:00
Ryszard Rozak 3d30527860 Fix unique..with method on queues of class objects (#4328) 2023-06-30 17:04:09 +02:00
Ryszard Rozak 24a9f7c650 Fix references to members of results of static methods (#4327) 2023-06-30 13:23:34 +02:00
Ryszard Rozak 242f661644 Fix selects of static members (#4326) 2023-06-30 09:12:22 +02:00
Aleksander Kiryk 32019d2bc4 Fix state update for always processes (#4311) 2023-06-29 09:21:49 -04:00
Krzysztof Boroński b252bb048f Fix unhandled overloads in V3InstrCount (#4324) 2023-06-28 08:49:56 -04:00
Ryszard Rozak ba47c01a01 Fix implicit calls of base class constructors with optional arguments (#4319) 2023-06-27 16:08:38 +02:00
Ryszard Rozak 51266898ec Fix comparison of string parameters (#4308) 2023-06-27 10:40:45 +02:00
Wilson Snyder f9164ab0d2 Internals: Assert dtype comparison. No functional change intended. 2023-06-25 07:40:00 -04:00
Wilson Snyder c43e707f43 Internals: refactor sameTreeIter. No functional change. 2023-06-25 07:26:45 -04:00
Mariusz Glebocki 85b7f828b3 Internals: Use std::packaged_task as a job wrapper; add and use V3ThreadPool::ScopedExclusiveAccess (#4310).
* Add VL_ASSERT_CAPABILITY; add assumeLocked and pretendUnlock to V3Mutex.

* Pass jobs as template-arguments and use std::packaged_task.

* Add and use V3ThreadPool::ScopedExclusiveAccess.
2023-06-23 18:25:12 -04:00
Wilson Snyder 7005a65d32 Tests: Attempt 20.04 fix for (#b1aec9b) (#4307) 2023-06-23 07:09:07 -04:00
Wilson Snyder 776d4e6ac2 CI: Build 20.04 on pull requests 2023-06-23 07:08:04 -04:00
Jiamin Zhu 1c88c3083c Fix splitting coroutines (#4297) (#4307) 2023-06-23 06:01:38 -04:00
Ryszard Rozak 4522834f7a Fix duplicate fork names (#4295) 2023-06-22 06:51:53 -04:00
Ryszard Rozak 9ee5fd0585 Fix handling of ref types in initial values of type parameters (#4304) 2023-06-20 14:10:07 -04:00
Wilson Snyder 63b2dbb827 Fix error when multiple duplicate DPI exports (#4301). 2023-06-20 06:26:46 -04:00
Wilson Snyder 5bee8ef15b Remove long deprecated verilated_heavy.h 2023-06-17 08:45:13 -04:00
Wilson Snyder e7de3d368f Commentary: Deprecation planned for 32-bit pointer -m32 mode (#4268). 2023-06-17 08:44:13 -04:00
Wilson Snyder d45deccc0a Internals: Favot std::array. No functional change intended. 2023-06-16 19:44:40 -04:00
Wilson Snyder d5b03ab721 Tests (#3806) (#3807) 2023-06-16 18:30:25 -04:00
Ryszard Rozak d1ff81a41c Fix class reference assignment checking (#4296) 2023-06-15 14:00:47 +02:00
Krzysztof Bieganski 43fa6858f6 Fix VlForkSync redeclaration (#4277)
Given nested forks, if the inner fork had a `join` or `join_any` at the end,
`V3Sched::transformForks()` would decide that the fork's `VlForkSync` variable
should be passed in from the outside. This resulted in the `VlForkSync` getting
redeclared as a function argument. Ultimately, it led to C++ compilation errors
due to variable redeclaration.

Fixed by rearranging the `if`s that decide whether a variable should be passed
in or left as-is.
2023-06-15 07:49:50 -04:00
Wilson Snyder db7eb45433 Contributors: Add Krzysztof Boronski and re-sort 2023-06-14 18:53:44 -04:00
Krzysztof Boronski 21c01ba97b V3Fork - transform processes that can outlive their parents into separate tasks (#4253)
Signed-off-by: Krzysztof Boronski <[email protected]>
2023-06-14 20:44:53 +02:00
Ryszard Rozak b517fb5ee3 Support some stream operations on queues (#4292) 2023-06-13 22:46:42 -04:00
Wilson Snyder 9dbc669199 devel release 2023-06-13 19:39:50 -04:00
Wilson Snyder 4f13c4d1bb Merge from master for release. 2023-06-13 19:37:22 -04:00
Wilson Snyder 7d2d32420a Version bump 2023-06-13 19:31:57 -04:00
Wilson Snyder 48e98ea2bd Internals: Sort some error codes (#4253). No functional change intended. 2023-06-13 08:52:13 -04:00
Wilson Snyder 01d0f0327b Commentary (#4254) 2023-06-13 08:22:57 -04:00
Wilson Snyder 0dc887720d Tests: Fix tests, broke in commit 3a9eeabd 2023-06-12 22:38:45 -04:00
Wilson Snyder e08bbcdb07 Commentary 2023-06-12 21:59:52 -04:00
Wilson Snyder 70d26ec123 Commentary (#4211) (#4254) 2023-06-12 21:59:33 -04:00
Wilson Snyder a06f260f42 Fix C++11 compiler error with C++14 rbegin 2023-06-12 21:46:32 -04:00
Wilson Snyder d42f76c346 Tests: Fix tests, broke in commit 3a9eeabd 2023-06-12 21:36:23 -04:00
Wilson Snyder 66c3d6d2d1 Commentary 2023-06-12 20:52:29 -04:00
Wilson Snyder 81affad2dc Tests: Fix test, broke in previous commit 2023-06-12 20:41:33 -04:00
Wilson Snyder 2488af9066 Commentary: Changes update 2023-06-12 20:38:33 -04:00
Wilson Snyder 5cf9d78516 Commentary 2023-06-12 20:28:32 -04:00
Wilson Snyder 3a9eeabdb2 Tests: Auto detect and exit --timing with no coroutines 2023-06-12 20:09:12 -04:00
Julien Margetts ac4315e145 Fix LATCH warning on function local variables (#4221) (#4284) 2023-06-12 09:24:46 -04:00
John Wehle 5094e94df1 Fix tracing undefined alignment (#4201) (#4288) 2023-06-12 07:13:00 -04:00
Àlex Torregrosa 4b7b185d05 Fix VCD scope types (#4227) (#4282) 2023-06-08 11:43:04 -04:00
Aleksander Kiryk 6cb0335d28 Support array max (#4275) 2023-06-07 07:44:21 -04:00
Aleksander Kiryk c248318958 Fix struct redefinition (#4276) 2023-06-06 18:28:35 -04:00
Ryszard Rozak c3e5db5f04 Fix names of foreach blocks (#4264) 2023-06-06 16:24:42 +02:00
Krzysztof Bieganski 058233fc87 Set the global usesTiming flag if forks exist (#4274) 2023-06-06 09:35:12 -04:00
Mariusz Glebocki 186c851695 Internals: Avoid pessimistic mutex locking in waitIfStopRequested() (#4272). No functional change intended.
`stopRequested()` reads only atomic variables. It doesn't need a mutex
to do this.

This function is called in `waitIfStopRequested()`, which in turn
is called before execution of every job, and inside some jobs. With this
change the mutex inside `waitIfStopRequested` needs to be locked only in
very rare cases instead of every time.
2023-06-05 19:40:36 -04:00
Mariusz Glebocki 5aa36357f6 Internals: Use auto instead of SFINAE in return type (#4271). No functional change intended. 2023-06-05 18:53:19 -04:00
Wilson Snyder 191d30cf1c Update gtkwave from upstream (#4141) 2023-06-03 12:10:07 -04:00
Andrei Kostovski d8c2ecaaf8 Commentary: Update install.rst for help2man (#4260) (#4261) 2023-06-03 10:31:17 -04:00
github action a7b0551d23 Apply 'make format' 2023-06-03 14:11:23 +00:00
John Wehle dc34968fe7 Add class specific same methods for AstVarScope, AstVar, and AstScope (#4203) (#4250) 2023-06-03 10:07:39 -04:00
Jiamin Zhu 45f064f7cb Fix missing assignment for wide class members. (#4267) 2023-06-03 06:40:23 -07:00
Krzysztof Boroński 545caba720 Give iterated variables in foreach loops VAUTOM lifetimes (#4265)
Signed-off-by: Krzysztof Boronski <[email protected]>
2023-06-02 17:16:42 +02:00
Krzysztof Boroński 0e24f36fef Use STATIC lifetime for variables created from clocking items (#4262)
Signed-off-by: Krzysztof Boronski <[email protected]>
2023-06-01 17:49:27 +02:00
Aleksander Kiryk db7935faf3 Add std::process class (#4212) 2023-06-01 10:02:08 -04:00
Adrien Le Masle 9cc218db3e Fix incorrect multi-driven lint warning (#4231) (#4248) 2023-06-01 08:43:17 -04:00
Ryszard Rozak 15f8ebc562 Fix static methods with prototypes (#4220) 2023-05-31 21:18:32 -04:00
github action 205cae963b Apply 'make format' 2023-05-31 16:03:33 +00:00
Don Williamson df2746de71 Add --main-top-name option for C main TOP name (#4235) (#4249) 2023-05-31 09:02:26 -07:00
Risto Pejašinović 7f471d862e Fix operator == for unpacked struct, if elements are VlUnpacked arrays (#4247) 2023-05-31 08:34:34 -04:00
Risto Pejašinović 338acabe2b Fix AstStructSel clean when data type is structure (#4241) (#4244) 2023-05-30 23:44:48 -04:00
Ryszard Rozak 4f1f487ae4 Fix function calls in with statements (#4245) 2023-05-30 09:02:59 -04:00
Krzysztof Bieganski 77502aeb97 Add warning that timing controls in DPI exports are unsupported (#4238)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2023-05-30 09:00:10 -04:00
Ryszard Rozak ba82d43ca1 Support for condition operator on class objects (#4214) 2023-05-30 08:59:00 -04:00
Wilson Snyder e9135598b3 Fix method calls on function return values. 2023-05-29 18:51:27 -04:00
Risto Pejašinović 1c0739db10 Fix unpacked struct == and != operators (#4234) (#4240) 2023-05-29 12:08:39 -04:00
Ryszard Rozak dfd3907787 Mark AstMemberSel as clean (#4236) 2023-05-29 09:35:53 +02:00
Wilson Snyder 1069652701 Fix some AstExprStmt handling issues, towards side effect fixes. 2023-05-27 12:43:40 -04:00
Wilson Snyder 70b82f1aec Internals: V3Dead cleanups. No functional change. 2023-05-27 11:46:25 -04:00
Wilson Snyder 5573bdb219 Internals: Cleanup V3SplitAs VL_RESTORED. No functional change intended. 2023-05-27 10:43:04 -04:00
Wilson Snyder cf68578261 Internals: Cleanup V3SplitAs VL_RESTORED. No functional change intended. 2023-05-27 10:17:51 -04:00
Wilson Snyder f407d442d4 Commentary 2023-05-27 09:43:23 -04:00
Wilson Snyder 7f12ad9d91 Commentary: Changes update 2023-05-27 07:17:37 -04:00
Wilson Snyder 5efe9367d2 Fix SystemC signal copy macro use (#4135). 2023-05-27 07:00:26 -04:00
Wilson Snyder cfce92645f Tests: Check equality of complex types. 2023-05-26 18:01:11 -04:00
Krzysztof Bieganski 519792d02b Fix to commit coroutines immediately on wait statements (#4229)
Event-triggered coroutines live in two stages: 'uncommitted' and 'ready'. First
they land in 'uncommitted', meaning they can't be resumed yet. Only after
coroutines from the 'ready' queue are resumed, the 'uncommitted' ones are moved
to the 'ready' queue, and can be resumed. This is to avoid self-triggering in
situations like waiting for an event immediately after triggering it.

However, there is an issue with `wait` statements. If you have a `wait(b)`, it's
being translated into a loop that awaits a change in `b` as long as `b` is
false. If `b` is false at first, the coroutine is put into the `uncommitted`
queue. If `b` is set to true before it's committed, the coroutine won't get
resumed.

This patch fixes that by immediately committing event controls created from
`wait` statements. That means the coroutine from the example above will get
resumed from now on.
2023-05-25 20:20:44 -04:00
Jiamin Zhu bfa1f2d7ce Fix missing assignment for wide unpacked structs (#4233) 2023-05-25 20:13:02 -04:00
Aleksander Kiryk 42beaf467c Tests: Add incomplete type definition test (#4230) 2023-05-25 14:06:37 -04:00
Mariusz Glebocki 8303938d0e Internals: Make mutex classes uncopyable. (#4223) 2023-05-25 07:35:14 -04:00
Mariusz Glebocki 0ece155418 Internals: Move some includes to .cpp files. (#4224) 2023-05-24 18:53:14 -04:00
Ryszard Rozak 4f7e155e59 Fix class parameters of enum types (#4219) 2023-05-24 15:51:03 +02:00
Wilson Snyder ff324625e4 CI: Disable m32 on g++ 2023-05-23 23:04:24 -04:00
Wilson Snyder fd7515b046 Tests: Cleanup scenarios lines. No test run change. 2023-05-23 22:40:19 -04:00
Wilson Snyder 607f853bc7 CI: Disable m32 on g++ 2023-05-23 22:35:07 -04:00
Wilson Snyder 09fb1174f1 CI: Disable m32 on g++ 2023-05-23 22:34:27 -04:00
Wilson Snyder 542eaab3dd CI: Disable m32 on g++ 2023-05-23 22:28:40 -04:00
Wilson Snyder a301a498f5 CI: Disable m32 on g++ 2023-05-23 22:27:10 -04:00
Wilson Snyder d66e749ec8 CI: Fix mold build 2023-05-23 22:25:03 -04:00
Wilson Snyder c8ca54ea31 CI: Disable m32 on g++ 2023-05-23 22:24:47 -04:00
Wilson Snyder 5704552053 Fix ci for mold 2023-05-23 21:30:39 -04:00
Wilson Snyder 426069a4dd Configure for faster C++ linking using 'mold', if it is installed. 2023-05-23 21:26:29 -04:00
Wilson Snyder 19d0aabe7a Internals: Fix extra cast on ExprStmt. 2023-05-23 20:52:30 -04:00
Krzysztof Boroński fb0d735f68 Tests: Skip t_suspendable_deep if coroutines are not supported by CXX toolchain (#4217) 2023-05-23 14:02:40 -04:00
Krzysztof Boroński 167a30be1c Fix deep traversal of class inheritance timing (#4216) 2023-05-23 09:01:57 -04:00
Wilson Snyder 5982528274 Fix duplicate std:: declaration with -I (#4215). 2023-05-22 20:32:20 -04:00
Krzysztof Boroński de3095e3b4 Fix missing class forward declarations (#4151) 2023-05-22 08:29:01 -04:00
Wilson Snyder 8e0682f442 Fix V3Expand ignoring side effects 2023-05-21 21:02:39 -04:00
Wilson Snyder 140994d2c4 Internals: Cleanup more VL_RESTORER. No functional change intended. 2023-05-21 14:06:39 -04:00
Wilson Snyder 950e29d56e Internals: Cleanup some missing VL_RESTORER in V3Gate. No functional change intended. 2023-05-21 12:49:07 -04:00
Wilson Snyder 65667356b6 wip 2023-05-21 12:24:00 -04:00
Wilson Snyder 6b393e9d7c Internals: Allow statement-in-statement recursion in V3Premit 2023-05-21 12:03:13 -04:00
Wilson Snyder 4818130e4f Internals: Remove dead code, addBeforeStmt 2023-05-20 22:37:28 -04:00
Ryszard Rozak 9da3aacd08 Fix bit selections under parameterized classes (#4210) 2023-05-18 20:01:36 -04:00
Krzysztof Bieganski 729f8b9334 Move suspendable detection to a separate visitor (#4208)
This makes the implementation of the detection and propagation of the
suspendable property simpler and easier to read. More importantly, there are no
more jumps around the AST with the `visit` functions, which in some cases could
result in incorrect visitor context while in the `visit` function. See the added
test, which would cause Verilator to segfault before this patch.

In testing, verilation performance was not shown to be affected by this change.
Though there is a slight performance improvement from this patch, due to adding
one more check before refreshing class member cache.

Signed-off-by: Krzysztof Bieganski <[email protected]>
2023-05-17 17:09:33 +00:00
Ryszard Rozak 279216048b Fix dotted references in parameterized classes (#4206) 2023-05-16 07:40:02 -04:00
Ryszard Rozak 2ce7a348df Fix references to members of parameterized base classes (#4196) 2023-05-15 19:50:04 -04:00
Wilson Snyder 46f719ceaa Tests: Fix some coverage holes 2023-05-13 20:15:03 -04:00
Mariusz Glebocki 949be301de Internals: Fix unbalanced V3LockGuard locking (#4193) 2023-05-13 10:32:33 -04:00
Mariusz Glebocki be429a5800 Internals: Lock the same mutex reference as specified in VL_ACQUIRE. (#4194) 2023-05-13 10:28:34 -04:00
Mariusz Glebocki e7714e0902 Internals: Add additional clang's thread safety analysis annotations (#4195)
* Simplify some Clang-specific attribute defines.

* Add `VL_RETURN_CAPABILITY` and `VL_PT_GUARDED_BY`.
2023-05-12 09:06:36 -04:00
Krzysztof Bieganski 4835ed6967 Fix forced assignments that override non-continuous assignments (#4183) (#4192)
Only assign forced value on release if it was forced in the first place.
2023-05-12 06:57:12 -04:00
Ryszard Rozak 0198a3fc52 Use unchanged copy of parameterized class for instantation (#4179) 2023-05-11 09:56:15 +02:00
Krzysztof Boroński e095bf1af0 Support inside expressions with strings and doubles (#4138) (#4139) 2023-05-10 20:36:41 -04:00
Aylon Chaim Porat d5de67c6dc Fix wide structure VL_TOSTRING_W generation (#4188) (#4189)
* V3Common.cpp::makeVlToString: fix `VL_TOSTRING_W` statement generation to include width argument

* fix contribution name

* add testcase for long struct `VL_TO_STRING_W` bug
2023-05-10 20:34:44 -04:00
Ryszard Rozak 2267db093f Fix hashes of instances of parameterized classes (#4182) 2023-05-10 18:34:29 -04:00
Ryszard Rozak 5aecfa98a1 Internals: Add param value string to hash in V3Param.cpp (#4186) 2023-05-10 17:52:17 -04:00
Wilson Snyder 485e230dd8 Commentary (#4187) 2023-05-10 17:51:22 -04:00
Wilson Snyder befb415f27 Fix mod/div/round argument side effects 2023-05-07 22:31:31 -04:00
Wilson Snyder c0490627bf Tests: Update historical 2023-05-07 22:00:52 -04:00
Wilson Snyder d269fbb446 Add creating __inputs.vpp file with --debug (#4177). 2023-05-07 17:58:14 -04:00
Wilson Snyder 444020f7c7 Fix super.new missing data type (#4147). 2023-05-07 16:47:34 -04:00
Wilson Snyder 6188083baa Tests: Improve randc tests 2023-05-07 15:08:44 -04:00
Wilson Snyder c2a524d80b Commentary (#4175) 2023-05-07 08:36:03 -04:00
Wilson Snyder e6f5a0495f Fix $fscanf of decimals overflowing variables (#4174). 2023-05-07 08:25:10 -04:00
Wilson Snyder 0606a29f13 Fix arrays of unpacked structs (#4173). 2023-05-06 21:41:17 -04:00
Wilson Snyder c6052830e1 Fix C++17 requirement on previous commit. 2023-05-06 20:23:35 -04:00
Wilson Snyder 4bb876aee5 Fix false IMPLICITSTATIC on package functions. 2023-05-06 19:48:22 -04:00
Wilson Snyder a3640c1767 Support get_randstate/set_randstate class method function. 2023-05-06 19:09:19 -04:00
Wilson Snyder 250c6950cf Fix randomize missing simple class rand members. 2023-05-06 18:33:08 -04:00
Wilson Snyder 76f5de6e54 Replace flake8 with ruff for Python linting 2023-05-06 06:08:32 -04:00
Wilson Snyder 1a1c5c5d44 Internals: Fix pylint warning 2023-05-06 06:05:11 -04:00
Wilson Snyder 4ae80f9a9f Commentary 2023-05-05 22:50:28 -04:00
github action 8a3cb8daa8 Apply 'make format' 2023-05-06 02:37:42 +00:00
Ethan Sifferman 64ab537b68 Add NEWERSTD warning when using feature in newer language standard (#4168) (#4172). 2023-05-05 22:36:51 -04:00
Wilson Snyder fdea386727 Fix false WIDTHEXPAND on array declarations (#3959). 2023-05-05 22:05:19 -04:00
Wilson Snyder 28944ed862 Fix crash on duplicate imported modules (#3231). 2023-05-05 20:31:48 -04:00
Wilson Snyder 3250ee5707 Commentary: Changes update 2023-05-05 20:24:45 -04:00
Wilson Snyder d308a561e4 Fix detection of wire/reg duplicates 2023-05-05 20:16:27 -04:00
Wilson Snyder 584f8cc9e7 Internal: With --xml-only support --debug-exit-uvm 2023-05-05 13:47:34 -04:00
Kamil Rakoczy 61e1483b74 Add multi-threaded Verilating at emit stage (#3608) 2023-05-05 08:36:20 -04:00
Wilson Snyder 3bb4e34044 Commentary 2023-05-04 19:04:38 -04:00
Kamil Rakoczy 832b17d4d2 clang_check_attributes: fix mt-safe checks for global objects in constructor context (#4171) 2023-05-04 14:00:30 -04:00
Krzysztof Bieganski 76c5875912 Fix marking overridden methods as coroutines (#4120) (#4169)
This patch fixes two cases where methods in base classes were not being marked
as coroutines, even though they were being overridden by coroutines.
- One case is the class member cache not getting refreshed for searched classes.
- The other is when the overriding methods are not declared as `virtual`. In
  that case, the `isVirtual()` getter on such a method returns false, which led
  to `V3Timing` skipping the step of searching for overridden methods.

Signed-off-by: Krzysztof Bieganski <[email protected]>
2023-05-04 15:27:45 +02:00
Ryszard Rozak 266dc7679a Don't link AstRefDType if it has parameterized class ref (#4164) (#4170) 2023-05-04 15:09:01 +02:00
Jose Loyola d693dc85e4 Add Github action to build and push docker images (#4163) 2023-05-04 07:44:55 -04:00
Wilson Snyder add68130b8 Internals: Rename to dumpLevel(), to avoid confusion with make-a-dump() 2023-05-03 18:04:10 -04:00
Tudor Timi d9b5680a45 Fix crash caused by $display() optimization (#4165) (#4166) 2023-05-03 17:04:18 -04:00
Wilson Snyder dc25be536c Internals: Deprecate VL_ATTR_ALIGNED, use alignas instead. 2023-05-02 21:21:10 -04:00
Stefan Wallentowitz 7708c88e32 Fix duplicate static names in blocks in functions (#4144) (#4160)
Static variables of functions are created in the function. When blocks
in a function use identical names for static variables, we need to name
those variables properly.
2023-05-02 20:24:44 -04:00
february cozzocrea 707f230353 Commentary: Update install.rst help2man dependency (#4161) 2023-05-02 14:32:24 -04:00
Kamil Rakoczy 49d2eb9a08 Fix initialization order of initial static after function/task (#4159) 2023-05-02 11:50:57 -04:00
github action 128186ea68 Apply 'make format' 2023-05-02 11:29:45 +00:00
Hennadii ChernyshchykandOleh Maks 949be9d0a1 Optimize VPI callValueCbs (#4155)
Co-authored-by: Oleh Maks <[email protected]>
2023-05-02 07:28:56 -04:00
Wilson Snyder 3e986517c3 devel release 2023-04-30 07:10:14 -04:00
Wilson Snyder c37ec19751 Merge from master for release. 2023-04-30 07:09:22 -04:00
Wilson Snyder aaaf8e75af Merge from master for release. 2023-03-04 11:29:11 -05:00
Wilson Snyder 8cc31c6cf1 Merge from master for release. 2023-01-22 11:52:52 -05:00
Wilson Snyder 8468af1a20 Merge from master for release. 2022-12-14 22:07:37 -05:00
Wilson Snyder db39d70c75 Merge from master for release. 2022-10-29 17:52:23 -04:00
Wilson Snyder 04fff7ebd8 Merge from master for release. 2022-10-01 08:33:50 -04:00
Wilson Snyder b154af87a1 Merge from master for release. 2022-08-31 19:47:07 -04:00
Wilson Snyder 8b7480806a Merge from master for release. 2022-06-19 15:11:19 -04:00
Wilson Snyder e29f5ad559 Merge from master for release. 2022-06-19 15:08:17 -04:00
Wilson Snyder 4618fd5a43 Merge from master for release. 2022-05-02 22:22:20 -04:00
Wilson Snyder ed979c6aa1 Merge from master for release. 2022-03-12 13:14:48 -05:00
Wilson Snyder 141c5da3f9 Merge from master for release. 2021-12-05 11:15:32 -05:00
Wilson Snyder 0185ee5df3 Merge from master for release. 2021-10-17 14:46:03 -04:00
Wilson Snyder 27c08a65e1 Merge from master for release. 2021-09-01 21:02:51 -04:00
Wilson Snyder de408a5eb7 Merge from master for release. 2021-07-07 20:48:51 -04:00
Wilson Snyder fa3c072dd1 Merge from master for release. 2021-06-12 13:01:37 -04:00
Wilson Snyder aac4b7090a Merge from master for release. 2021-04-24 10:40:13 -04:00
Wilson Snyder f0d664530d Merge from master for release. 2021-03-12 15:48:45 -05:00
Wilson Snyder 702140bf93 Merge from master for release. 2021-02-25 21:49:42 -05:00
Wilson Snyder 2075db3db0 Merge from master for release. 2021-01-10 13:35:45 -05:00
Wilson Snyder b7fec513cf Merge from master for release. 2020-12-02 20:13:31 -05:00
Wilson Snyder 78e7758022 Merge from master for release. 2020-11-14 09:58:28 -05:00
Wilson Snyder 818318ddd5 Merge from master for release. 2020-10-15 08:25:44 -04:00
Wilson Snyder 39eea781bc Merge from master for release. 2020-09-07 09:38:50 -04:00
Wilson Snyder c4f7bd6a04 'Merge from master for release.' 2020-08-15 09:18:29 -04:00
Wilson Snyder dd03d0f3c9 'Merge from master for release.' 2020-07-11 09:16:56 -04:00
Wilson Snyder 369ce6af2c 'Merge from master for release.' 2020-06-06 07:49:57 -04:00
Wilson Snyder f83817510d 'Merge from master for release.' 2020-05-03 11:15:21 -04:00
Wilson Snyder bfcd779cfb 'Merge from master for release.' 2020-04-04 08:50:02 -04:00
Wilson Snyder e69c380205 'Merge from master for release.' 2020-03-08 08:39:29 -04:00
Wilson Snyder 61e14aa827 'Merge from master for release.' 2020-02-08 09:15:27 -05:00
Wilson Snyder 0c6c83e278 Merge from master for release. 2020-01-11 07:12:09 -05:00
Wilson Snyder 6f52208b68 Fix clang warning. 2019-12-09 18:28:56 -05:00
763 changed files with 93019 additions and 6645 deletions
+11 -7
View File
@@ -36,12 +36,14 @@ jobs:
m32: [0, 1]
exclude:
# Build pull requests only with ubuntu-22.04 and without m32
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-20.04' || 'do-not-exclude' }}
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
# Build -m32 only on ubuntu-22.04
# - os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
# - os: ${{ github.event_name == 'pull_request' && 'ubuntu-20.04' || 'do-not-exclude' }}
# - m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
# Build -m32 only on ubuntu-22.04 clang++
- {os: ubuntu-18.04, m32: 1}
- {os: ubuntu-20.04, m32: 1}
- compiler: { cc: gcc, cxx: g++ }
m32: 1
include:
# Build GCC 10 on ubuntu-20.04
- os: ubuntu-20.04
@@ -105,12 +107,14 @@ jobs:
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, vltmt-0, vltmt-1]
exclude:
# Build pull requests only with ubuntu-22.04 and without m32
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-20.04' || 'do-not-exclude' }}
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
# - os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
# - os: ${{ github.event_name == 'pull_request' && 'ubuntu-20.04' || 'do-not-exclude' }}
# - m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
# Build -m32 only on ubuntu-22.04
- {os: ubuntu-18.04, m32: 1}
- {os: ubuntu-20.04, m32: 1}
- compiler: { cc: gcc, cxx: g++ }
m32: 1
include:
# Test with GCC 10 on ubuntu-20.04 without m32
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, m32: 0, suite: dist-vlt-0}
+85
View File
@@ -0,0 +1,85 @@
# Build and push verilator docker image when tags are pushed to the repository.
# The following variable(s) must be configured in the github repository:
# DOCKER_HUB_NAMESPACE: docker hub namespace.
# The following secrets must be configured in the github repository:
# DOCKER_HUB_USER: user name for logging into docker hub
# DOCKER_HUB_ACCESS_TOKEN: docker hub access token.
name: Build Verilator Container
on:
push:
tags: [ 'v*' ]
workflow_dispatch:
inputs:
manual_tag:
description: 'Git tag to use for image build'
required: true
type: string
add_latest_tag:
description: 'Tag workflow_dispatch docker image as "latest"'
required: true
type: boolean
default: false
jobs:
build:
runs-on: ubuntu-22.04
strategy:
matrix:
contexts:
- "ci/docker/run:verilator"
# - "ci/docker/buildenv:verilator-buildenv"
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Extract context variables
run: |
echo "${{ matrix.contexts }}" | sed -r 's/(.*):.*/build_context=\1/' >> "$GITHUB_ENV"
echo "${{ matrix.contexts }}" | sed -r 's/.*:(.*)/image_name=\1/' >> "$GITHUB_ENV"
echo "git_tag=${GITHUB_REF#refs/*/}" >> "$GITHUB_ENV"
- name: Use manual tag
if: ${{ inputs.manual_tag }}
run: |
echo "git_tag=${{ inputs.manual_tag }}" >> "$GITHUB_ENV"
- name: Docker meta
id: docker_meta
uses: docker/metadata-action@v4
with:
images: |
${{ vars.DOCKER_HUB_NAMESPACE }}/${{ env.image_name }}
tags: |
type=match,pattern=(v.*),group=1,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
type=raw,value=${{ inputs.manual_tag }},enable=${{ inputs.manual_tag != '' }}
type=raw,value=latest,enable=${{ inputs.add_latest_tag == true }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
buildkitd-flags: --debug
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Build and Push to Docker
uses: docker/build-push-action@v4
if: startsWith(github.ref, 'refs/tags/v')
with:
context: ${{ env.build_context }}
build-args: SOURCE_COMMIT=${{ env.git_tag }}
platforms: linux/arm64,linux/amd64
push: ${{ !env.ACT && startsWith(github.ref, 'refs/tags/v') }}
tags: ${{ steps.docker_meta.outputs.tags }}
labels: ${{ steps.docker_meta.outputs.labels }}
+2
View File
@@ -41,3 +41,5 @@ verilator-config-version.cmake
**/_build/*
**/obj_dir/*
/.vscode/
/.idea/
/cmake-build-*/
+1 -1
View File
@@ -15,7 +15,7 @@
cmake_minimum_required(VERSION 3.15)
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
project(Verilator
VERSION 5.010
VERSION 5.016
HOMEPAGE_URL https://verilator.org
LANGUAGES CXX
)
+164
View File
@@ -8,6 +8,170 @@ The changes in each Verilator version are described below. The
contributors that suggested a given feature are shown in []. Thanks!
Verilator 5.016 2023-09-16
==========================
**Minor:**
* Add prepareClone and atClone APIs for Verilated models (#3503) (#4444). [Yinan Xu]
* Add check for conflicting options e.g. binary and lint-only (#4409). [Ethan Sifferman]
* Add --no-trace-top to not trace top signals (#4412) (#4422). [Frans Skarman]
* Support recursive function calls (#3267).
* Support assignments of packed values to stream expressions on queues (#4401). [Ryszard Rozak, Antmicro Ltd]
* Support no-parentheses calls to static methods (#4432). [Krzysztof Boroński]
* Support block_item_declaration in forks (#4455). [Krzysztof Boroński]
* Support assignments of stream expressions on queues to packed values (#4458). [Ryszard Rozak, Antmicro Ltd]
* Support function non-constant default arguments (#4470).
* Support 'let'.
* Optimize Verilator executable size by refactoring error reporting routines (#4446). [Anthony Donlon]
* Optimize Verilation runtime pointers and graphs (#4396) (#4397) (#4398). [Krzysztof Bieganski, Antmicro Ltd]
* Optimize preparations towards multithreaded Verilation (#4291) (#4463) (#4476) (#4477) (#4479). [Kamil Rakoczy, Antmicro Ltd]
* Fix Windows filename format, etc (#3873) (#4421). [Anthony Donlon].
* Fix t_dist_cppstyle Perl performance issue (#4085). [Srinivasan Venkataramanan]
* Fix using type in parameterized classes without #() (#4281) (#4440). [Anthony Donlon]
* Fix false INFINITELOOP on forever..mailbox.get() (#4323). [Srinivasan Venkataramanan]
* Fix data type of condition operation on class objects (#4345) (#4352). [Ryszard Rozak, Antmicro Ltd]
* Fix variables mutated under fork..join_none/join_any blocks into anonymous objects (#4356). [Krzysztof Boroński]
* Fix V3CUse, do not consider implementations (.cpp) at all (#4386). [Krzysztof Boroński]
* Fix ++/-- under statements (#4399). [Aleksander Kiryk, Antmicro Ltd]
* Fix detection of mixed blocking and nonblocking assignment in nested assignments (#4404). [Ryszard Rozak, Antmicro Ltd]
* Fix jumping over object initialization (#4411). [Krzysztof Boroński]
* Fix multiple issues towards short circuit support (#4413) (#4460). [Ryszard Rozak, Antmicro Ltd]
* Fix variable lifetimes in extern methods (#4414). [Krzysztof Boroński]
* Fix multiple function definitions in V3Sched (#4416). [Hennadii Chernyshchyk]
* Fix false UNUSEDPARAM on generate localparam (#4427). [Bill Pringlemeir]
* Fix checking for parameter and port connections in the wrong place (#4428). [Anthony Donlon]
* Fix coroutine handle movement during queue manipulation (#4431). [Aleksander Kiryk, Antmicro Ltd]
* Fix nested assignments on the LHS (#4435). [Ryszard Rozak, Antmicro Ltd]
* Fix false MULTITOP on bound interfaces (#4438). [Alex Solomatnikov]
* Fix internal error on real conversion (#4447). [vdhotre-ventana]
* Fix lifetime unknown error on enum.name (#4448). [jwoutersymatra]
* Fix unstable output of VHashSha256 (#4453). [Anthony Donlon]
* Fix static cast from a stream type (#4469) (#4485). [Ryszard Rozak, Antmicro Ltd]
* Fix error on enum with VARHIDDEN of cell (#4482). [Michail Rontionov]
* Fix lint of case statements with enum and wildcard bits (#4464) (#4487). [Anthony Donlon]
* Fix reference to extended class in parameterized class (#4466).
* Fix recursive display causing segfault (#4480). [Kuoping Hsu]
* Fix the error message when the type of ref argument is wrong (#4490). [Ryszard Rozak, Antmicro Ltd]
* Fix display %x formatting of real.
* Fix mis-warning on #() in classes' own functions.
* Fix IGNOREDRETURN to not warn on void-cast static function calls.
* Fix ZERODLY to not warn on 'wait(0)'.
Verilator 5.014 2023-08-06
==========================
**Minor:**
* Deprecation planned for 32-bit pointer -m32 mode (#4268).
* Deprecate CMake config below version 3.13 (#4389) (#4390). [Vito Gamberini]
* Support some stream operations on queues (#4292). [Ryszard Rozak, Antmicro Ltd]
* Support property declaration with empty parentheses (#4313) (#4317). [Anthony Donlon]
* Support locator methods with "with" on assoc arrays (#4335). [Ryszard Rozak, Antmicro Ltd]
* Support string replication with variable (#4341). [Aleksander Kiryk, Antmicro Ltd]
* Support more types in wait (#4374). [Aleksander Kiryk, Antmicro Ltd]
* Support static method calls as default values of function arguments (#4378). [Ryszard Rozak, Antmicro Ltd]
* Add GENUNNAMED lint warning. [Srinivasan Venkataramanan, Deepa Palaniappan]
* Add MISINDENT lint warning for misleading indentation.
* Fix 'VlForkSync' redeclaration (#4277). [Krzysztof Bieganski, Antmicro Ltd]
* Fix processes that can outlive their parents (#4253). [Krzysztof Boronski, Antmicro Ltd]
* Fix duplicate fork names (#4295). [Ryszard Rozak, Antmicro Ltd]
* Fix splitting coroutines (#4297) (#4307). [Jiamin Zhu]
* Fix error when multiple duplicate DPI exports (#4301).
* Fix class reference assignment checking (#4296). [Ryszard Rozak, Antmicro Ltd]
* Fix handling of ref types in initial values of type parameters (#4304). [Ryszard Rozak, Antmicro Ltd]
* Fix comparison of string parameters (#4308). [Ryszard Rozak, Antmicro Ltd]
* Fix state update for always processes (#4311). [Aleksander Kiryk, Antmicro Ltd]
* Fix multiple edge timing controls in class methods (#4318) (#4320) (#4344). [Krzysztof Bieganski, Antmicro Ltd]
* Fix implicit calls of base class constructors with optional arguments (#4319). [Ryszard Rozak, Antmicro Ltd]
* Fix propagation of process requirement (#4321). [Krzysztof Boroński]
* Fix unhandled overloads in V3InstrCount (#4324). [Krzysztof Boroński]
* Fix selects of static members (#4326). [Ryszard Rozak, Antmicro Ltd]
* Fix references to members of results of static methods (#4327). [Ryszard Rozak, Antmicro Ltd]
* Fix unique..with method on queues of class objects (#4328). [Ryszard Rozak, Antmicro Ltd]
* Fix queue slicing (#4329). [Aleksander Kiryk, Antmicro Ltd]
* Fix wildcard referring types (#4336) (#4342). [Aleksander Kiryk, Antmicro Ltd]
* Fix comparison of class objects (#4346). [Ryszard Rozak, Antmicro Ltd]
* Fix unexpected RefDType on assoc arrays (#4337). [Aleksander Kiryk, Antmicro Ltd]
* Fix cmake astgen for Rocky Linux 8.7 (#4343). [Julian Daube]
* Fix class timescale in class packages (#4348). [Krzysztof Bieganski, Antmicro Ltd]
* Fix string concatenations (#4354). [Ryszard Rozak, Antmicro Ltd]
* Fix unlinked task error from broken context (#4355) (#4402). [Aleksander Kiryk, Antmicro Ltd]
* Fix selects on unpacked structs (#4359). [Ryszard Rozak, Antmicro Ltd]
* Fix select operation on assoc array with wide keys (#4360). [Ryszard Rozak, Antmicro Ltd]
* Fix non-public methods with wide output (#4364). [Ryszard Rozak, Antmicro Ltd]
* Fix handling of super.new calls (#4366). [Ryszard Rozak, Antmicro Ltd]
* Fix assign to input var in methods (#4367). [Aleksander Kiryk, Antmicro Ltd]
* Fix VlProcess not found (#4368). [Aleksander Kiryk, Antmicro Ltd]
* Fix order of evaluation of function calls in statements (#4375). [Ryszard Rozak, Antmicro Ltd]
* Fix config_build.h issues (#4380) (#4381). [Andrew Miloradovsky]
Verilator 5.012 2023-06-13
==========================
**Major:**
* With -j or --build-jobs, multithread Verilator's emit phase of Verilation. [Kamil Rakoczy, Antmicro Ltd]
Additional Verilator-internal stages will become multithreaded over time.
**Minor:**
* Add --main-top-name option for C main TOP name (#4235) (#4249). [Don Williamson]
* Add creating __inputs.vpp file with --debug (#4177). [Tudor Timi]
* Add NEWERSTD warning when using feature in newer language standard (#4168) (#4172). [Ethan Sifferman]
* Add warning that timing controls in DPI exports are unsupported (#4238). [Krzysztof Bieganski, Antmicro Ltd]
* Support std::process class (#4212). [Aleksander Kiryk, Antmicro Ltd]
* Support inside expressions with strings and doubles (#4138) (#4139). [Krzysztof Boroński]
* Support get_randstate/set_randstate class method functions.
* Support for condition operator on class objects (#4214). [Ryszard Rozak, Antmicro Ltd]
* Support array max (#4275). [Aleksander Kiryk, Antmicro Ltd]
* Optimize VPI callValueCbs (#4155). [Hennadii Chernyshchyk]
* Configure for faster C++ linking using 'mold', if it is installed.
* Fix crash on duplicate imported modules (#3231). [Robert Balas]
* Fix false WIDTHEXPAND on array declarations (#3959). [JOTEGO]
* Fix marking overridden methods as coroutines (#4120) (#4169). [Krzysztof Bieganski, Antmicro Ltd]
* Fix SystemC signal copy macro use (#4135). [Josep Sans]
* Fix duplicate static names in blocks in functions (#4144) (#4160). [Stefan Wallentowitz]
* Fix initialization order of initial static after function/task (#4159). [Kamil Rakoczy, Antmicro Ltd]
* Fix linking AstRefDType if it has parameterized class ref (#4164) (#4170). [Ryszard Rozak, Antmicro Ltd]
* Fix crash caused by $display() optimization (#4165) (#4166). [Tudor Timi]
* Fix arrays of unpacked structs (#4173). [Risto Pejašinović]
* Fix $fscanf of decimals overflowing variables (#4174). [Ahmed El-Mahmoudy]
* Fix super.new missing data type (#4147). [Tudor Timi]
* Fix missing class forward declarations (#4151). [Krzysztof Boroński]
* Fix hashes of instances of parameterized classes (#4182). [Ryszard Rozak, Antmicro Ltd]
* Fix forced assignments that override non-continuous assignments (#4183) (#4192). [Krzysztof Bieganski, Antmicro Ltd]
* Fix wide structure VL_TOSTRING_W generation (#4188) (#4189). [Aylon Chaim Porat]
* Fix references to members of parameterized base classes (#4196). [Ryszard Rozak, Antmicro Ltd]
* Fix tracing undefined alignment (#4201) (#4288) [John Wehle]
* Fix class specific same methods for AstVarScope, AstVar, and AstScope (#4203) (#4250). [John Wehle]
* Fix dotted references in parameterized classes (#4206). [Ryszard Rozak, Antmicro Ltd]
* Fix bit selections under parameterized classes (#4210). [Ryszard Rozak, Antmicro Ltd]
* Fix duplicate std:: declaration with -I (#4215). [Harald Pretl]
* Fix deep traversal of class inheritance timing (#4216). [Krzysztof Boroński]
* Fix class parameters of enum types (#4219). [Ryszard Rozak, Antmicro Ltd]
* Fix static methods with prototypes (#4220). [Ryszard Rozak, Antmicro Ltd]
* Fix LATCH warning on function local variables (#4221) (#4284) [Julien Margetts]
* Fix VCD scope types (#4227) (#4282). [Àlex Torregrosa]
* Fix incorrect multi-driven lint warning (#4231) (#4248). [Adrien Le Masle]
* Fix missing assignment for wide unpacked structs (#4233). [Jiamin Zhu]
* Fix unpacked struct == and != operators (#4234) (#4240). [Risto Pejašinović]
* Fix AstStructSel clean when data type is structure (#4241) (#4244). [Risto Pejašinović]
* Fix function calls in with statements (#4245). [Ryszard Rozak, Antmicro Ltd]
* Fix operator == for unpacked struct, if elements are VlUnpacked arrays (#4247). [Risto Pejašinović]
* Fix STATIC lifetime for variables created from clocking items (#4262). [Krzysztof Boroński]
* Fix names of foreach blocks (#4264). [Ryszard Rozak, Antmicro Ltd]
* Fix iterated variables in foreach loops to have VAUTOM lifetimes (#4265). [Krzysztof Boroński]
* Fix missing assignment for wide class members (#4267). [Jiamin Zhu]
* Fix the global uses timing flag when forks exist (#4274). [Krzysztof Bieganski, Antmicro Ltd]
* Fix struct redefinition (#4276). [Aleksander Kiryk, Antmicro Ltd]
* Fix detection of wire/reg duplicates.
* Fix false IMPLICITSTATIC on package functions.
* Fix method calls on function return values.
Verilator 5.010 2023-04-30
==========================
+6 -8
View File
@@ -424,17 +424,15 @@ YAPF_FLAGS = -i
yapf:
$(YAPF) $(YAPF_FLAGS) $(PY_FILES)
FLAKE8 = flake8
FLAKE8_FLAGS = \
--extend-exclude=fastcov.py \
--ignore=E123,E129,E251,E402,E501,W503,W504,E701
PYLINT = pylint
PYLINT_FLAGS = --score=n --disable=R0801
RUFF = ruff
RUFF_FLAGS = check --ignore=E402,E501,E701
lint-py:
-$(FLAKE8) $(FLAKE8_FLAGS) $(PY_PROGRAMS)
-$(PYLINT) $(PYLINT_FLAGS) $(PY_PROGRAMS)
-$(RUFF) $(RUFF_FLAGS) $(PY_PROGRAMS)
format-pl-exec:
-chmod a+x test_regress/t/*.pl
@@ -451,8 +449,8 @@ install-msg:
IN_WILD := ${srcdir}/*.in ${srcdir}/*/*.in
# autoheader might not change config_build.h.in, so touch it
${srcdir}/config_build.h: ${srcdir}/config_build.h.in configure
# autoheader might not change config_package.h.in, so touch it
${srcdir}/config_package.h: ${srcdir}/config_package.h.in configure
cd ${srcdir} && autoheader
touch $@
Makefile: Makefile.in config.status $(IN_WILD)
+11 -8
View File
@@ -1,12 +1,16 @@
.. Github doesn't render images unless absolute URL
.. Do not know of a conditional tag, "only: github" nor "github display" works
.. image:: https://img.shields.io/badge/Website-Verilator.org-181717.svg
:target: https://verilator.org
.. image:: https://img.shields.io/badge/License-LGPL%20v3-blue.svg
:target: https://www.gnu.org/licenses/lgpl-3.0]
:target: https://www.gnu.org/licenses/lgpl-3.0
.. image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
:target: https://opensource.org/licenses/Artistic-2.0
.. image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
:target: https://repology.org/project/verilator/versions
.. image:: https://img.shields.io/docker/pulls/verilator/verilator
:target: https://hub.docker.com/r/verilator/verilator
.. image:: https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f
:target: https://www.codacy.com/gh/verilator/verilator
.. image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg
@@ -57,17 +61,16 @@ files, the "Verilated" code.
These Verilated C++/SystemC files are then compiled by a C++ compiler
(gcc/clang/MSVC++), optionally along with a user's own C++/SystemC wrapper
file to instantiate the Verilated model. Executing the resulting executable
performs the design simulation. Verilator also supports linking Verilated
generated libraries, optionally encrypted, into other simulators.
file, to instantiate the Verilated model. Executing the resulting
executable performs the design simulation. Verilator also supports linking
Verilated generated libraries, optionally encrypted, into other simulators.
Verilator may not be the best choice if you are expecting a full-featured
replacement for a closed-source Verilog simulator, needs SDF annotation,
mixed-signal simulation, or are doing a quick class project (we recommend
`Icarus Verilog`_ for classwork.) However, if you are looking for a path
to migrate SystemVerilog to C++/SystemC, or want high-speed simulation of
synthesizable designs containing limited verification constructs, Verilator
is the tool for you.
designs, Verilator is the tool for you.
Performance
@@ -83,11 +86,11 @@ as `Icarus Verilog`_. Another 2-10x speedup might be gained from
multithreading (yielding 200-1000x total over interpreted simulators).
Verilator has typically similar or better performance versus the
closed-source Verilog simulators (Carbon Design Systems Carbonator,
closed-source Verilog simulators (e.g., Carbon Design Systems Carbonator,
Modelsim/Questa, Cadence Incisive/NC-Verilog, Synopsys VCS, VTOC, and
Pragmatic CVer/CVC). But, Verilator is open-sourced, so you can spend on
computes rather than licenses. Thus, Verilator gives you the best
cycles/dollar.
simulation cycles/dollar.
Installation & Documentation
+2
View File
@@ -372,6 +372,7 @@ detailed descriptions of these arguments.
--make <build-tool> Generate scripts for specified build tool
-MAKEFLAGS <flags> Arguments to pass to make during --build
--main Generate C++ main() file
--main-top-name Specify top name passed to Verilated model in generated C++ main
--max-num-width <value> Maximum number width (default: 64K)
--Mdir <directory> Name of output object directory
--MMD Create .d dependency files
@@ -439,6 +440,7 @@ detailed descriptions of these arguments.
--trace-params Enable tracing of parameters
--trace-structs Enable tracing structure names
--trace-threads <threads> Enable FST waveform creation on separate threads
--no-trace-top Do not emit traces for signals in the top module generated by verilator
--trace-underscore Enable tracing of _signals
-U<var> Undefine preprocessor define
--no-unlimited-stack Don't disable stack size limit
+7 -3
View File
@@ -61,7 +61,7 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
sudo apt-get update
sudo apt-get install ccache help2man libfl-dev ||
sudo apt-get install ccache help2man libfl-dev
if [ "$CI_RUNS_ON" != "ubuntu-22.04" ]; then
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ]; then
# Some conflict of libunwind verison on 22.04, can live without it for now
sudo apt-get install libgoogle-perftools-dev ||
sudo apt-get install libgoogle-perftools-dev
@@ -70,6 +70,10 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
sudo apt-get install libsystemc libsystemc-dev ||
sudo apt-get install libsystemc libsystemc-dev
fi
if [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
sudo apt-get install bear mold ||
sudo apt-get install bear mold
fi
if [ "$COVERAGE" = 1 ]; then
yes yes | sudo cpan -fi Parallel::Forker
fi
@@ -102,8 +106,8 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
sudo apt-get install gdb gtkwave lcov libfl-dev ccache
# Required for test_regress/t/t_dist_attributes.pl
if [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
sudo apt-get install libclang-dev ||
sudo apt-get install libclang-dev
sudo apt-get install libclang-dev mold ||
sudo apt-get install libclang-dev mold
pip3 install clang==14.0
fi
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ] || [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
+7 -1
View File
@@ -19,10 +19,16 @@ RUN apt-get update \
ccache \
flex \
git \
help2man \
libfl2 \
libfl-dev \
libgoogle-perftools-dev \
numactl \
perl \
perl-doc \
python3 \
zlib1g \
zlib1g-dev \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
@@ -39,7 +45,7 @@ RUN git clone "${REPO}" verilator && \
git checkout "${SOURCE_COMMIT}" && \
autoconf && \
./configure && \
make -j "$(nproc)" && \
make && \
make install && \
cd .. && \
rm -r verilator
+8 -3
View File
@@ -5,16 +5,16 @@
# General Public License Version 3 or the Perl Artistic License Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
# When releasing, also update header of Changes file,
# When releasing, also update header of Changes file, and CmakeLists.txt,
# and commit using "devel release" or "Version bump" message
# Then 'make maintainer-dist'
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[5.010 2023-04-30],
AC_INIT([Verilator],[5.016 2023-09-16],
[https://verilator.org],
[verilator],[https://verilator.org])
AC_CONFIG_HEADERS(src/config_build.h)
AC_CONFIG_HEADERS(src/config_package.h)
AC_CONFIG_FILES(Makefile src/Makefile src/Makefile_obj include/verilated.mk include/verilated_config.h verilator.pc verilator-config.cmake verilator-config-version.cmake)
# Version
@@ -470,6 +470,10 @@ m4_foreach([ldflag], [
AC_SUBST(CFG_LDLIBS_THREADS)
AC_SUBST(CFG_LDFLAGS_THREADS_CMAKE)
# If 'mold' is installed, use it to link for faster buildtimes
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_SRC, -fuse-ld=mold)
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_VERILATED, -fuse-ld=mold)
# When linking partially statically
if test "$CFG_ENABLE_PARTIAL_STATIC" = "yes"; then
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_SRC, -static-libgcc)
@@ -480,6 +484,7 @@ else
LTCMALLOC=-ltcmalloc_minimal
fi
AC_SUBST(CFG_LDFLAGS_SRC)
AC_SUBST(CFG_LDFLAGS_VERILATED)
# The pthread library is required by tcmalloc, so add it if it exists. If it
# does not, the tcmalloc check below will fail anyway, and linking against
+23 -4
View File
@@ -12,8 +12,12 @@ Alex Chadwick
Aliaksei Chapyzhenka
Ameya Vikram Singh
Andreas Kuster
Andrei Kostovski
Andrew Miloradovsky
Andrew Nolte
Anthony Donlon
Arkadiusz Kozdra
Aylon Chaim Porat
Cameron Kirk
Chris Randall
Chuxuan Wang
@@ -24,15 +28,20 @@ David Horton
David Metz
David Stanford
David Turner
Don Williamson
Drew Ranck
Drew Taussig
Driss Hafdi
Edgar E. Iglesias
Eric Rippey
Ethan Sifferman
Eyck Jentzsch
Fan Shupei
february cozzocrea
Felix Neumärker
Felix Yan
Frans Skarman
G-A. Kamendje
Garrett Smith
Geza Lore
Gianfranco Costamagna
@@ -41,16 +50,16 @@ Gökçe Aydos
Graham Rushton
Guokai Chen
Gustav Svensk
G-A. Kamendje
Harald Heckmann
Hennadii Chernyshchyk
Howard Su
Huang Rui
Huanghuang Zhou
HungMingWu
HyungKi Jeong
Ilya Barkov
Iru Cai
Ivan Vnučec
Ilya Barkov
Iztok Jeras
Jake Merdich
James Hanlon
@@ -65,14 +74,18 @@ Jeremy Bennett
Jesse Taube
Jevin Sweval
Jiacheng Qian
Jiamin Zhu
Jiuyang Liu
Joey Liu
John Coiner
John Demme
John Wehle
Jonathan Drolet
Joseph Nwabueze
Jose Loyola
Josep Sans
Joseph Nwabueze
Josh Redford
Julian Daube
Julie Schwartz
Julien Margetts
Kaleb Barrett
@@ -82,6 +95,7 @@ Keith Colbert
Kevin Kiningham
Kritik Bhimani
Krzysztof Bieganski
Krzysztof Boronski
Krzysztof Boroński
Kuba Ober
Larry Doolittle
@@ -108,6 +122,7 @@ Mostafa Gamal
Nandu Raj
Nathan Kohagen
Nathan Myers
Oleh Maksymenko
Patrick Stewart
Paul Wright
Pawel Sagan
@@ -136,6 +151,7 @@ Stephen Henry
Steven Hugg
Sören Tempel
Teng Huang
Tim Hutt
Tim Snyder
Tobias Rosenkranz
Tobias Wölfel
@@ -143,6 +159,7 @@ Todd Strader
Tomasz Gorochowik
Topa Topino
Toru Niina
Tudor Timi
Tymoteusz Blazejczyk
Udi Finkelstein
Unai Martinez-Corral
@@ -150,15 +167,17 @@ Varun Koyyalagunta
Vassilis Papaefstathiou
Veripool API Bot
Victor Besyakov
Vito Gamberini
William D. Jones
Wilson Snyder
Xi Zhang
Yinan Xu
Yoda Lee
Yossi Nivin
Yu-Sheng Lin
Yuri Victorovich
Yutetsu TAKATSUKASA
Yu-Sheng Lin
Yves Mathieu
Zhanglei Wang
Zixi Li
أحمد المحمودي
+5 -1
View File
@@ -18,12 +18,16 @@
RST2HTML = rst2html
PYTHON3 = python3
DOXYGEN = doxygen
SPHINXOPTS ?= -c guide
SPHINXBUILD ?= sphinx-build
SOURCEDIR = guide
BUILDDIR = _build
SPHINXOPTS ?= -c guide
ifneq ($(VERILATOR_ANALYTICS_ID),)
SPHINXOPTS += -D html_theme_options.analytics_id=$(VERILATOR_ANALYTICS_ID)
endif
######################################################################
.SUFFIXES:
+1 -2
View File
@@ -31,7 +31,7 @@ def get_vlt_version():
if match:
try:
data = os.popen('git log -n 1 --pretty=%cs').read()
except Exception:
except Exception: # pylint: disable=broad-except
data = "" # fallback, and Sphinx will fill in today's date
return "Devel " + match.group(1), data
return "unknown", "unknown"
@@ -115,7 +115,6 @@ html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
html_theme_options = {
'analytics_id': 'G-D27B0C9QEB',
'logo_only': True,
'style_nav_header_background': "#45acf8", # Default is #2980B9
# 'canonical_url':
+37
View File
@@ -128,6 +128,43 @@ in the distribution. These headers use Doxygen comments, `///` and `//<`,
to indicate and document those functions that are part of the Verilated
public API.
Process-Level Clone APIs
--------------------------
Modern operating systems support process-level clone (a.k.a copying, forking)
with system call interfaces in C/C++, e.g., :code:`fork()` in Linux.
However, after cloning a parent process, some resources cannot be inherited
in the child process. For example, in POSIX systems, when you fork a process,
the child process inherits all the memory of the parent process. However,
only the thread that called fork is replicated in the child process. Other
threads are not.
Therefore, to support the process-level clone mechanisms, Verilator supports
:code:`prepareClone()` and :code:`atClone()` APIs to allow the user to manually
re-construct the model in the child process. The two APIs handle all necessary
resources required for releasing and re-initializing before and after cloning.
The two APIs are supported in the verilated models. Here is an example of usage
with Linux :code:`fork()` and :code:`pthread_atfork` APIs:
.. code-block:: C++
// static function pointers to fit pthread_atfork
static auto prepareClone = [](){ topp->prepareClone(); };
static auto atClone = [](){ topp->atClone(); };
// in main function, register the handlers:
pthread_atfork(prepareClone, atClone, atClone);
For better flexibility, you can also manually call the handlers before and
after :code:`fork()`.
With the process-level clone APIs, users can create process-level snapshots
for the verilated models. While the Verilator save/restore option provides
persistent and circuit-dependent snapshots, the process-level clone APIs
enable in-memory, circuit-transparent, and highly efficient snapshots.
Direct Programming Interface (DPI)
==================================
+4 -4
View File
@@ -17,10 +17,10 @@ C++11 compiler support
Verilator will require C++20 or newer compilers for both compiling
Verilator and compiling all Verilated models no sooner than January 2025.
Verilated_heavy.h
The legacy "verilated_heavy.h" include was replaced with just including
"verilated.h". Verilated_heavy.h is planned for removal no sooner than
July 2022.
32-bit compiler support
Verilator currently regresses both 64-bit and 32-bit pointer modes (GCC's
`-m64` and `-m32`). Support for 32-bit `-m32` mode will be deprecated no
sooner than January 2024.
Option `-O<letter>`
The debug `-O<letter>` options have been replaced with
+26 -6
View File
@@ -91,9 +91,29 @@ associated programs.
.. option:: VERILATOR_ROOT
Specifies the directory containing the distribution kit. This is used
to find the executable, Perl library, and include files. If not
specified, it will come from a default optionally specified at configure
time (before Verilator was compiled). It should not be specified if
using a pre-compiled Verilator package as the hard-coded value should be
correct. See :ref:`Installation`.
The ``VERILATOR_ROOT`` environment variable is used in several places:
* At ``./configure`` time: If set, it is embedded into the binary, and
at runtime if ``VERILATOR_ROOT`` is not set, the embedded value is
used for the runtime default.
* When ``verilator`` is run: If ``VERILATOR_ROOT`` is set it will be
used to find the ``verilator_bin`` executable (this is the actual
Verilator binary; ``verilator`` is a Perl wrapper). If not set, the
``verilator`` script uses other methods to find ``verilator_bin``
(looking in the same directory and falling back to ``$PATH``).
* When ``make`` is run on the Makefile generated by ``verilator``: The
value of ``VERILATOR_ROOT`` (falling back to the value embedded in the
binary if not set) is used to find the include files
(``include/verilated.mk``).
If you are using a pre-compiled Verilator package, you should not need
to set ``VERILATOR_ROOT`` - the value embedded in the binary should be
correct. In fact this option *does not work* with Verilator packages
that have been installed with ``make install``. If a Verilator package
has been installed using ``./configure --prefix=/some/path && make
install`` and then moved to another location, you cannot use
``VERILATOR_ROOT`` to point to the new version.
See :ref:`Installation` for more details.
+59 -25
View File
@@ -125,9 +125,12 @@ Summary:
After generating the SystemC/C++ code, Verilator will invoke the
toolchain to build the model library (and executable when :vlopt:`--exe`
is also used). Verilator manages the build itself, and for this --build
is also used). Verilator manages the build itself, and for this --build
requires GNU Make to be available on the platform.
:vlopt:`--build` cannot be specified when using :vlopt:`-E`,
:vlopt:`--dpi-hdr-only`, :vlopt:`--lint-only`, or :vlopt:`--xml-only`.
.. option:: --build-dep-bin <filename>
Rarely needed. When a dependency (.d) file is created, this filename
@@ -808,6 +811,13 @@ Summary:
See also :vlopt:`--binary`.
.. option:: --main-top-name <string>
Specify the name passed to the Verilated model being constructed, in the
generated C++ main() function.
If the string ``"-"`` is used, no top level scope is added.
.. option:: --max-num-width <value>
Set the maximum number literal width (e.g., in 1024'd22 this
@@ -1307,6 +1317,7 @@ Summary:
This is not needed with standard designs with only one top. See also
:option:`MULTITOP` warning.
.. option:: --trace
Adds waveform tracing code to the model using VCD format. This overrides
@@ -1385,6 +1396,17 @@ Summary:
This option is accepted, but has absolutely no effect with
:vlopt:`--trace`, which respects :vlopt:`--threads` instead.
.. option:: --no-trace-top
Disables tracing for the input and output signals in the top wrapper which
Verilator adds to the design. The signals are still traced in the original
verilog top modules.
When combined with :option:`--main-top-name` set to "-" or when the name of
the top module is set to "" in its constructor, the generated trace file
will have the verilog top module as its root, rather than another module
added by Verilator.
.. option:: --trace-underscore
Enable tracing of signals or modules that start with an
@@ -1467,8 +1489,11 @@ Summary:
them systematically.
The generated file is in the Verilator Configuration format, see
:ref:`Configuration Files`, and can directly be consumed by
Verilator. The standard file extension is ".vlt".
:ref:`Configuration Files`. The standard file extension is ".vlt".
These files can directly be consumed by Verilator, typically by placing
the filename as part of the Verilator command line options. Waiver files
need to be listed on the command line before listing the files they are
waiving.
.. option:: -Wall
@@ -1513,13 +1538,15 @@ Summary:
.. option:: -Wno-lint
Disable all lint-related warning messages, and all style warnings. This is
equivalent to ``-Wno-ALWCOMBORDER -Wno-ASCRANGE -Wno-BSSPACE -Wno-CASEINCOMPLETE
-Wno-CASEOVERLAP -Wno-CASEX -Wno-CASTCONST -Wno-CASEWITHX -Wno-CMPCONST -Wno-COLONPLUS
-Wno-IMPLICIT -Wno-IMPLICITSTATIC -Wno-PINCONNECTEMPTY
-Wno-PINMISSING -Wno-STATICVAR -Wno-SYNCASYNCNET -Wno-UNDRIVEN -Wno-UNSIGNED
-Wno-UNUSEDGENVAR -Wno-UNUSEDPARAM -Wno-UNUSEDSIGNAL
-Wno-WIDTH`` plus the list shown for Wno-style.
Disable all lint-related warning messages, and all style warnings. This
is equivalent to ``-Wno-ALWCOMBORDER`` ``-Wno-ASCRANGE``
``-Wno-BSSPACE`` ``-Wno-CASEINCOMPLETE`` ``-Wno-CASEOVERLAP``
``-Wno-CASEX`` ``-Wno-CASTCONST`` ``-Wno-CASEWITHX`` ``-Wno-CMPCONST``
``-Wno-COLONPLUS`` ``-Wno-IMPLICIT`` ``-Wno-IMPLICITSTATIC``
``-Wno-PINCONNECTEMPTY`` ``-Wno-PINMISSING`` ``-Wno-STATICVAR``
``-Wno-SYNCASYNCNET`` ``-Wno-UNDRIVEN`` ``-Wno-UNSIGNED``
``-Wno-UNUSEDGENVAR`` ``-Wno-UNUSEDPARAM`` ``-Wno-UNUSEDSIGNAL``
``-Wno-WIDTH``, plus the list shown for :vlopt:`-Wno-style`.
It is strongly recommended that you clean up your code rather than using this
option; it is only intended to be used when running test-cases of code
@@ -1527,12 +1554,13 @@ Summary:
.. option:: -Wno-style
Disable all code style related warning messages (note that by default, they are
already disabled). This is equivalent to ``-Wno-DECLFILENAME -Wno-DEFPARAM
-Wno-EOFNEWLINE -Wno-IMPORTSTAR -Wno-INCABSPATH -Wno-PINCONNECTEMPTY
-Wno-PINNOCONNECT -Wno-SYNCASYNCNET -Wno-UNDRIVEN
-Wno-UNUSEDGENVAR -Wno-UNUSEDPARAM -Wno-UNUSEDSIGNAL
-Wno-VARHIDDEN``.
Disable all code style related warning messages (note that by default,
they are already disabled). This is equivalent to ``-Wno-DECLFILENAME``
``-Wno-DEFPARAM`` ``-Wno-EOFNEWLINE`` ``-Wno-GENUNNAMED``
``-Wno-IMPORTSTAR`` ``-Wno-INCABSPATH`` ``-Wno-PINCONNECTEMPTY``
``-Wno-PINNOCONNECT`` ``-Wno-SYNCASYNCNET`` ``-Wno-UNDRIVEN``
``-Wno-UNUSEDGENVAR`` ``-Wno-UNUSEDPARAM`` ``-Wno-UNUSEDSIGNAL``
``-Wno-VARHIDDEN``.
.. option:: -Wpedantic
@@ -1550,19 +1578,25 @@ Summary:
.. option:: -Wwarn-lint
Enable all lint-related warning messages (note that by default, they are already
enabled), but do not affect style messages. This is equivalent to
``-Wwarn-ALWCOMBORDER -Wwarn-BSSPACE -Wwarn-CASEINCOMPLETE
-Wwarn-CASEOVERLAP -Wwarn-CASEX -Wwarn-CASTCONST -Wwarn-CASEWITHX -Wwarn-CMPCONST
-Wwarn-COLONPLUS -Wwarn-IMPLICIT -Wwarn-ASCRANGE
-Wwarn-PINMISSING -Wwarn-REALCVT -Wwarn-UNSIGNED -Wwarn-WIDTH``.
Enable all lint-related warning messages (note that by default, they are
already enabled), but do not affect style messages. This is equivalent
to ``-Wwarn-ALWCOMBORDER`` ``-Wwarn-ASCRANGE`` ``-Wwarn-BSSPACE``
``-Wwarn-CASEINCOMPLETE`` ``-Wwarn-CASEOVERLAP`` ``-Wwarn-CASEWITHX``
``-Wwarn-CASEX`` ``-Wwarn-CASTCONST`` ``-Wwarn-CMPCONST``
``-Wwarn-COLONPLUS`` ``-Wwarn-IMPLICIT`` ``-Wwarn-IMPLICITSTATIC``
``-Wwarn-LATCH`` ``-Wwarn-MISINDENT`` ``-Wwarn-NEWERSTD``
``-Wwarn-PINMISSING`` ``-Wwarn-REALCVT`` ``-Wwarn-STATICVAR``
``-Wwarn-UNSIGNED`` ``-Wwarn-WIDTHTRUNC`` ``-Wwarn-WIDTHEXPAND``
``-Wwarn-WIDTHXZEXPAND``.
.. option:: -Wwarn-style
Enable all code style-related warning messages. This is equivalent to
``-Wwarn ASSIGNDLY -Wwarn-DECLFILENAME -Wwarn-DEFPARAM -Wwarn-EOFNEWLINE
-Wwarn-INCABSPATH -Wwarn-PINNOCONNECT -Wwarn-SYNCASYNCNET -Wwarn-UNDRIVEN
-Wwarn-UNUSEDGENVAR -Wwarn-UNUSEDPARAM -Wwarn-UNUSEDSIGNAL -Wwarn-VARHIDDEN``.
``-Wwarn-ASSIGNDLY`` ``-Wwarn-DECLFILENAME`` ``-Wwarn-DEFPARAM``
``-Wwarn-EOFNEWLINE`` ``-Wwarn-GENUNNAMED`` ``-Wwarn-INCABSPATH``
``-Wwarn-PINNOCONNECT`` ``-Wwarn-SYNCASYNCNET`` ``-Wwarn-UNDRIVEN``
``-Wwarn-UNUSEDGENVAR`` ``-Wwarn-UNUSEDPARAM`` ``-Wwarn-UNUSEDSIGNAL``
``-Wwarn-VARHIDDEN``.
.. option:: --x-assign 0
+3 -3
View File
@@ -356,8 +356,8 @@ Why do I get "undefined reference to \`VL_RAND_RESET_I' or \`Verilated::...'"?
You need to link your compiled Verilated code against the
:code:`verilated.cpp` file found in the include directory of the Verilator
kit. This is one target in the ``$(VK_GLOBAL_OBJS)`` make variable, which
should be part of your Makefile's link rule. If you use :vlopt:`--exe`,
this is done for you.
should be part of your Makefile's link rule. If you use :vlopt:`--exe` or
:vlopt:`--binary`, this is done for you.
Is the PLI supported?
@@ -486,7 +486,7 @@ From the sc_main.cpp file, you'd then:
#include "Vour.h"
#include "Vour_our.h"
cout << "clock is " << top->our->clk << endl;
std::cout << "clock is " << top->our->clk << std::endl;
In this example, clk is a bool you can read or set as any other variable.
+4 -2
View File
@@ -126,8 +126,10 @@ In specific debug and other modes, it also creates:
- Debugging graph files (from --debug)
* - *{prefix}{misc}*\ .tree
- Debugging files (from --debug)
* - {mod_prefix}_{each_verilog_base_filename}*\ .vpp
- Pre-processed verilog (from --debug)
* - *{prefix}*\ __inputs\ .vpp
- Pre-processed verilog for all files (from --debug)
* - *{prefix}*\ _ *{each_verilog_base_filename}*\ .vpp
- Pre-processed verilog for each file (from --debug)
After running Make, the C++ compiler may produce the following:
+22 -34
View File
@@ -39,7 +39,7 @@ In brief, to install from git:
::
# Prerequisites:
#sudo apt-get install git perl python3 make autoconf g++ flex bison ccache
#sudo apt-get install git help2man perl python3 make autoconf g++ flex bison ccache
#sudo apt-get install libgoogle-perftools-dev numactl perl-doc
#sudo apt-get install libfl2 # Ubuntu only (ignore if gives error)
#sudo apt-get install libfl-dev # Ubuntu only (ignore if gives error)
@@ -104,6 +104,7 @@ for good performance:
::
sudo apt-get install ccache # If present at build, needed for run
sudo apt-get install mold # If present at build, needed for run
sudo apt-get install libgoogle-perftools-dev numactl
The following is optional but is recommended for nicely rendered command line
@@ -124,9 +125,9 @@ Those developing Verilator itself may also want these (see internals.rst):
::
sudo apt-get install gdb graphviz cmake clang clang-format-14 gprof lcov
sudo apt-get install libclang-dev yapf3
sudo pip3 install clang sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe
sudo apt-get install clang clang-format-14 cmake gdb gprof graphviz lcov
sudo apt-get install libclang-dev yapf3 bear
sudo pip3 install clang sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe ruff
cpan install Pod::Perldoc
cpan install Parallel::Forker
@@ -201,8 +202,9 @@ These are the installation options:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Our personal favorite is to always run Verilator in-place from its Git
directory. This allows the easiest experimentation and upgrading, and
allows many versions of Verilator to co-exist on a system.
directory (don't run ``make install``). This allows the easiest
experimentation and upgrading, and allows many versions of Verilator to
co-exist on a system.
::
@@ -211,17 +213,18 @@ allows many versions of Verilator to co-exist on a system.
./configure
# Running will use files from $VERILATOR_ROOT, so no install needed
Note after installing (below steps), a calling program or shell must set
the environment variable :option:`VERILATOR_ROOT` to point to this Git
directory, then execute ``$VERILATOR_ROOT/bin/verilator``, which will find
the path to all needed files.
Note after installing (see `Installation`_), a calling program or shell
must set the environment variable :option:`VERILATOR_ROOT` to point to this
Git directory, then execute ``$VERILATOR_ROOT/bin/verilator``, which will
find the path to all needed files.
2. Install into a specific location
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2. Install into a Specific Prefix
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You may eventually be installing onto a project/company-wide "CAD" tools
disk that may support multiple versions of every tool. Tell configure the
You may be an OS package maintainer building a Verilator package, or you
may eventually be installing onto a project/company-wide "CAD" tools disk
that may support multiple versions of every tool. Tell configure the
eventual destination directory name. We recommend that the destination
location include the Verilator version name:
@@ -232,7 +235,8 @@ location include the Verilator version name:
# For the tarball, use the version number instead of git describe
./configure --prefix /CAD_DISK/verilator/`git describe | sed "s/verilator_//"`
Note after installing (below steps), if you use `modulecmd
Note after installing (see `Installation`_), you need to add the path to
the ``bin`` directory to your ``PATH``. Or, if you use `modulecmd
<http://modules.sourceforge.net/>`__, you'll want a module file like the
following:
@@ -245,23 +249,7 @@ following:
prepend-path PKG_CONFIG_PATH $install_root/share/pkgconfig
3. Install into a Specific Path
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You may eventually install Verilator into a specific installation prefix,
as most GNU tools support:
::
unset VERILATOR_ROOT # if your shell is bash
unsetenv VERILATOR_ROOT # if your shell is csh
./configure --prefix /opt/verilator-VERSION
Then after installing (below steps), you will need to add
``/opt/verilator-VERSION/bin`` to your ``$PATH`` environment variable.
4. Install System Globally
3. Install System Globally
^^^^^^^^^^^^^^^^^^^^^^^^^^
The final option is to eventually install Verilator globally, using
@@ -273,8 +261,8 @@ configure's default system paths:
unsetenv VERILATOR_ROOT # if your shell is csh
./configure
Then after installing (below), the binaries should be in a location
already in your ``$PATH`` environment variable.
Then after installing (see `Installation`_), the binaries should be in a
location already in your ``$PATH`` environment variable.
Configure
+1 -1
View File
@@ -44,7 +44,7 @@ The best place to get started is to try the :ref:`Examples`.
.. [#] Verilog is defined by the `Institute of Electrical and Electronics
Engineers (IEEE) Standard for Verilog Hardware Description
Language`, Std. 1364, released in 1995, 2001, and 2005. The
Verilator documentation uses the shorthand, e.g., "IEEE 1394-2005",
Verilator documentation uses the shorthand, e.g., "IEEE 1364-2005",
to refer to the, e.g., 2005 version of this standard.
.. [#] SystemVerilog is defined by the `Institute of Electrical and
+2 -2
View File
@@ -143,7 +143,7 @@ Functional Coverage
-------------------
With :vlopt:`--coverage` or :vlopt:`--coverage-user`, Verilator will
translate functional coverage points the user has inserted manually win
translate functional coverage points the user has inserted manually in
SystemVerilog code through into the Verilated model.
Currently, all functional coverage points are specified using SystemVerilog
@@ -500,7 +500,7 @@ feedback-directed optimization. See the appropriate compiler
documentation.
.. _Runtime Debugging
.. _Runtime Debugging:
Runtime Debugging
=================
+1 -1
View File
@@ -165,7 +165,7 @@ compiled on the different target system.
To support this, none of the files that Verilator produces will reference
any configure-generated build-system-specific files, such as
:file:`config.h` (which is renamed in Verilator to :file:`config_build.h`
:file:`config.h` (which is renamed in Verilator to :file:`config_package.h`
to reduce confusion.) The disadvantage of this approach is that
:file:`include/verilatedos.h` must self-detect the requirements of the
target system, rather than using configure.
+214 -5
View File
@@ -670,6 +670,52 @@ List Of Warnings
used as a clock.
.. option:: GENUNNAMED
Warns that a generate block was unnamed and "genblk" will be used per
IEEE.
The potential issue is that adding additional generate blocks will
renumber the assigned names, which may cause eventual problems with
synthesis constraints or other tools that depend on hierarchical paths
remaining consistent.
Blocks that are empty may not be reported with this warning, as no
scopes are created for empty blocks, so there is no harm in having them
unnamed.
Disabled by default as this is a code-style warning; it will simulate
correctly.
.. code-block:: sv
:linenos:
:emphasize-lines: 2
generate
if (PARAM == 1) begin //<--- Warning
end
Results in:
.. code-block::
%Warning-GENUNNAMED: example.v:2:9: Unnamed generate block (IEEE 1800-2017 27.6)
To fix this assign a label (often with the naming convention prefix of
:code:`gen_` or :code:`g_`), for example:
.. code-block:: sv
:linenos:
:emphasize-lines: 2
generate
if (PARAM == 1) begin : gen_param_1 //<--- Repaired
end
Other tools with similar warnings: Verible's generate-label, "All
generate block statements must have a label."
.. option:: HIERBLOCK
Warns that the top module is marked as a hierarchy block by the
@@ -769,11 +815,14 @@ List Of Warnings
then automatic as static prevents the function from being reentrant,
which may be a source of bugs, and/or performance issues.
If the function does not require static behavior, change it to "function
automatic".
If the function is in a module, and does not require static behavior,
change it to "function automatic".
If the function requires static behavior, change it to "function
static".
If the function is in a module, and requires static behavior, change it
to "function static".
If the function is in a package, it defaults to static, and label the
function's variables as static.
Ignoring this warning will only suppress the lint check; it will
simulate correctly.
@@ -873,6 +922,102 @@ List Of Warnings
Ignoring this warning will only suppress the lint check; it will
simulate correctly.
.. option:: LIFETIME
Error when a variable is referenced in a process that can outlive the process
in which it was declared. This can happen when using 'fork..join_none' or
'fork..join_any' blocks, which spawn process that can outlive their parents.
This error occurs only when Verilator can't replace the reference with a
reference to copy of this variable, local to the forked process. For example:
.. code-block:: sv
:linenos:
:emphasize-lines: 3
task foo(int local_var);
fork
#10 local_var++;
#20 $display("local_var = %d", local_var);
join_none
endtask
In the example above 'local_var' exists only within scope of 'foo', once foo
finishes, the stack frame containing 'i' gets removed. However, the process
forked from foo continues, as it contains a delay. After 10 units of time
pass, this process attempts to modify 'local_var'. However, this variable no
longer exits. It can't be made local to the forked process upon spawning, because
it's modified and can be referenced somewhere else, for example in the other
forked process, that was delayed by 20 units of time in this example. Thus,
there's no viable stack allocation for it.
In order to fix it, if the intent is not to share the variable's state outside
of the process, then create a local copy of the variable.
For example:
.. code-block:: sv
:linenos:
:emphasize-lines: 4
task foo(int local_var);
fork
#10 begin
int forked_var = local_var;
forked_var++;
end
#20 begin
// Note that we are going to print the original value here,
// as `forked_var`is a local copy that was initialized while
// `foo` was still alive.
int forked_var = local_var;
$display("forked_var = %d", forked_var)
end
join_none
endtask
If you need to share its state, another strategy is to ensure it's allocated
statically:
.. code-block:: sv
:linenos:
:emphasize-lines: 1
int static_var;
task foo();
fork
#10 static_var++;
#20 $display("static_var = %d", static_var);
join_none
endtask
However, if you need to be able to instantiate at runtime, the solution would be to
wrap it in an object, since the forked process can hold a reference to that object
and ensure that the variable stays alive this way:
.. code-block:: sv
:linenos:
:emphasize-lines: 2
class Wrapper;
int m_var;
// Here we implicitly hold a reference to `this`
task foo();
fork
#10 m_var++;
#20 $display("this.m_var = %d", m_var);
join_none
endtask
endclass
// Here we explicitly hold a handle to an object
task bar(Wrapper wrapper);
fork
#10 wrapper.m_var++;
#20 $display("wrapper.m_var = %d", wrapper.m_var);
join_none
endtask
.. option:: LITENDIAN
@@ -894,6 +1039,51 @@ List Of Warnings
unsupported. Verilator uses only the typical delay value.
.. option:: MISINDENT
Warns that the indentation of a statement is misleading, suggesting the
statement is part of a previous :code:`if` or :code:`while` block while
it is not.
Verilator suppresses this check when there is an inconsistent mix of
spaces and tabs, as it cannot ensure the width of tabs. Verilator also
ignores blocks with :code:`begin`/:code:`end`, as the :code:`end`
visually indicates the earlier statement's end.
Ignoring this warning will only suppress the lint check; it will
simulate correctly.
For example
.. code-block:: sv
:linenos:
:emphasize-lines: 3
if (something)
statement_in_if;
statement_not_in_if; //<--- Warning
Results in:
.. code-block::
%Warning-MISINDENT: example.v:3:9: Misleading indentation
To fix this repair the indentation to match the correct earlier
statement, for example:
.. code-block:: sv
:linenos:
:emphasize-lines: 3
if (something)
statement_in_if;
statement_not_in_if; //<--- Repaired
Other tools with similar warnings: GCC -Wmisleading-indentation,
clang-tidy readability-misleading-indentation.
.. option:: MODDUP
.. TODO better example
@@ -972,6 +1162,21 @@ List Of Warnings
(neither :vlopt:`--timing` nor :vlopt:`--no-timing` option was provided).
.. option:: NEWERSTD
Warns that a feature requires a newer standard of Verilog or SystemVerilog
than the one specified by the :vlopt:`--language` option. For example, unsized
unbased literals (`'0`, `'1`, `'z`, `'x`) require 1800-2005 or later.
To avoid this warning, use a Verilog or SystemVerilog standard that
supports the feature. Alternatively, modify your code to use a different
syntax that is supported by the Verilog/SystemVerilog standard specified
by the :vlopt:`--language` option.
Ignoring this warning will only suppress the lint check; it will
simulate correctly.
.. option:: NOLATCH
.. TODO better example
@@ -1785,11 +1990,15 @@ List Of Warnings
.. code-block:: sv
wait(0); // Blocks forever
wait(1); // Blocks forever
Warns that a `wait` statement awaits a constant condition, which means it
either blocks forever or never blocks.
As a special case `wait(0)` with the literal constant `0` (as opposed to
something that elaborates to zero), does not warn, as it is presumed the
code is making the intent clear.
.. option:: WIDTH
+33 -12
View File
@@ -638,7 +638,22 @@ condition. See the `Timing Pass` section for more details.
Timing Pass
~~~~~~~~~~~
The visitor in ``V3Timing.cpp`` transforms each timing control into a ``co_await``.
There are two visitors in ``V3Timing.cpp``.
The first one, ``TimingSuspendableVisitor``, does not perform any AST
transformations. It is responsible for marking processes and C++ functions that
contain timing controls as suspendable. Processes that call suspendable
functions are also marked as suspendable. Functions that call, are overridden
by, or override suspendable functions are marked as suspendable as well.
The visitor keeps a dependency graph of functions and processes to handle such
cases. A function or process is dependent on a function if it calls it. A
virtual class method is dependent on another class method if it calls it,
overrides it, or is overriden by it.
The second visitor in ``V3Timing.cpp``, ``TimingControlVisitor``, uses the
information provided by ``TimingSuspendableVisitor`` and transforms each timing
control into a ``co_await``.
* event controls are turned into ``co_await`` on a trigger scheduler's
``trigger`` method. The awaited trigger scheduler is the one corresponding to
@@ -670,14 +685,9 @@ Each sub-statement of a ``fork`` is put in an ``AstBegin`` node for easier
grouping. In a later step, each of these gets transformed into a new, separate
function. See the `Forks` section for more detail.
Processes that use awaits are marked as suspendable. Later, during ``V3Sched``,
they are transformed into coroutines. Functions that use awaits get the return
type of ``VlCoroutine``. This immediately makes them coroutines. Note that if a
process calls a function that is a coroutine, the call gets wrapped in an
await, which means the process itself will be marked as suspendable. A virtual
function is a coroutine if any of its overriding or overridden functions are
coroutines. The visitor keeps a dependency graph of functions and processes to
handle such cases.
Suspendable functions get the return type of ``VlCoroutine``, which makes them
coroutines. Later, during ``V3Sched``, suspendable processes are also
transformed into coroutines.
Scheduling with timing
~~~~~~~~~~~~~~~~~~~~~~
@@ -1503,8 +1513,19 @@ debug level 5, with the V3Width.cpp file at level 9.
--debug
-------
When you run with ``--debug``, there are two primary output file types
placed into the obj_dir, .tree and .dot files.
When you run with ``--debug``, there are three primary output file types
placed into the obj_dir, .vpp, .tree and .dot files.
.vpp Output
-----------
Verilator creates a *{mod_prefix}*\ __inputs\ .vpp file containing all the
files that were read, filtered by preprocessing. This file can be fed back
into Verilator, replacing on the command line all of the previous input
files, to enable simplification of test cases.
Verilator also creates .vpp files for each individual file passed on the
command line.
.dot Output
@@ -2102,7 +2123,7 @@ Or in a hand-written C++ wrapper:
::
#ifdef TEST_VERBOSE
cout << "Read a=" << a << endl;
std::cout << "Read a=" << a << std::endl;
#endif
A filename that should be used to check the output results is given with
+18
View File
@@ -56,6 +56,7 @@ Das
Dcache
Deadman
Debacker
Deepa
Defparams
Denio
Deprecations
@@ -213,6 +214,7 @@ Maupin
Mdir
Mednick
Menküc
Michail
Michiels
Microsystems
Milanovic
@@ -246,6 +248,7 @@ Olof
Olofsson
Oyvind
PLI
Palaniappan
Patricio
Petr
Piechotka
@@ -260,6 +263,8 @@ Prateek
Pre
Preprocess
Pretet
Pretl
Pringlemeir
Priyadharshini
Pullup
Pulver
@@ -274,6 +279,7 @@ Renga
Requin
Rodionov
Rolfe
Rontionov
Roodselaar
Runtime
Ruud
@@ -375,6 +381,7 @@ Wfuture
Whatson
Wildman
Wim
Wmisleading
Wno
Wojciech
Wolfel
@@ -409,7 +416,9 @@ architected
args
arrarys
assertOn
astgen
async
atClone
ato
atoi
autoconf
@@ -472,6 +481,7 @@ constexpr
constpool
coredump
coroutine
coroutines
countbits
countones
cout
@@ -602,6 +612,7 @@ genvar
genvars
getenv
getline
ggdb
gmake
gmon
gotFinish
@@ -646,6 +657,7 @@ ish
isunbounded
isunknown
jobserver
jwoutersymatra
killua
lang
lcov
@@ -765,6 +777,7 @@ pragmas
pre
precisions
predefines
prepareClone
prepend
prepended
preprocess
@@ -791,6 +804,7 @@ qrq
radix
randc
randcase
randstate
rarr
rdtsc
reStructuredText
@@ -896,10 +910,12 @@ typedefs
typename
uint
un
unbased
undef
undefineall
undriven
ungetc
unhandled
uniquified
unistd
unlink
@@ -920,7 +936,9 @@ vc
vcd
vcddiff
vcoverage
vdhotre
vec
ventana
ver
verFiles
verible
+26 -22
View File
@@ -2052,7 +2052,8 @@ if(xc && !xc->already_in_close && !xc->already_in_flush)
int zfd;
int fourpack_duo = 0;
#ifndef __MINGW32__
char *fnam = (char *)malloc(strlen(xc->filename) + 5 + 1);
int fnam_len = strlen(xc->filename) + 5 + 1;
char *fnam = (char *)malloc(fnam_len);
#endif
fixup_offs = ftello(xc->handle);
@@ -2142,7 +2143,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush)
fflush(xc->handle);
#ifndef __MINGW32__
sprintf(fnam, "%s.hier", xc->filename);
snprintf(fnam, fnam_len, "%s.hier", xc->filename);
unlink(fnam);
free(fnam);
#endif
@@ -2821,7 +2822,7 @@ if(ctx && name && literal_arr && val_arr && (elem_count != 0))
uint32_t i;
name_len = strlen(name);
elem_count_len = sprintf(elem_count_buf, "%" PRIu32, elem_count);
elem_count_len = snprintf(elem_count_buf, 16, "%" PRIu32, elem_count);
literal_lens = (unsigned int *)calloc(elem_count, sizeof(unsigned int));
val_lens = (unsigned int *)calloc(elem_count, sizeof(unsigned int));
@@ -3909,7 +3910,8 @@ int pass_status = 1;
if(!xc->fh)
{
fst_off_t offs_cache = ftello(xc->f);
char *fnam = (char *)malloc(strlen(xc->filename) + 6 + 16 + 32 + 1);
int fnam_len = strlen(xc->filename) + 6 + 16 + 32 + 1;
char *fnam = (char *)malloc(fnam_len);
unsigned char *mem = (unsigned char *)malloc(FST_GZIO_LEN);
fst_off_t hl, uclen;
fst_off_t clen = 0;
@@ -3928,7 +3930,7 @@ if(!xc->fh)
htyp = xc->contains_hier_section_lz4duo ? FST_BL_HIER_LZ4DUO : FST_BL_HIER_LZ4;
}
sprintf(fnam, "%s.hier_%d_%p", xc->filename, getpid(), (void *)xc);
snprintf(fnam, fnam_len, "%s.hier_%d_%p", xc->filename, getpid(), (void *)xc);
fstReaderFseeko(xc, xc->f, xc->hier_pos, SEEK_SET);
uclen = fstReaderUint64(xc->f);
#ifndef __MINGW32__
@@ -4179,7 +4181,7 @@ if(!(isfeof=feof(xc->fh)))
if((xc->hier.u.attr.subtype == FST_MT_SOURCESTEM)||(xc->hier.u.attr.subtype == FST_MT_SOURCEISTEM))
{
int sidx_skiplen_dummy = 0;
xc->hier.u.attr.arg_from_name = fstGetVarint64((unsigned char *)xc->str_scope_nam, &sidx_skiplen_dummy);
xc->hier.u.attr.arg_from_name = fstGetVarint64((unsigned char *)xc->str_scope_attr, &sidx_skiplen_dummy);
}
}
break;
@@ -4585,15 +4587,17 @@ if(sectype == FST_BL_ZWRAPPER)
int zfd;
int flen = strlen(xc->filename);
char *hf;
int hf_len;
seclen = fstReaderUint64(xc->f);
uclen = fstReaderUint64(xc->f);
if(!seclen) return(0); /* not finished compressing, this is a failed read */
hf = (char *)calloc(1, flen + 16 + 32 + 1);
hf_len = flen + 16 + 32 + 1;
hf = (char *)calloc(1, hf_len);
sprintf(hf, "%s.upk_%d_%p", xc->filename, getpid(), (void *)xc);
snprintf(hf, hf_len, "%s.upk_%d_%p", xc->filename, getpid(), (void *)xc);
fcomp = fopen(hf, "w+b");
if(!fcomp)
{
@@ -5189,16 +5193,16 @@ for(;;)
if(beg_tim)
{
if(dumpvars_state == 1) { wx_len = sprintf(wx_buf, "$end\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 2; }
wx_len = sprintf(wx_buf, "#%" PRIu64 "\n", beg_tim);
if(dumpvars_state == 1) { wx_len = snprintf(wx_buf, 32, "$end\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 2; }
wx_len = snprintf(wx_buf, 32, "#%" PRIu64 "\n", beg_tim);
fstWritex(xc, wx_buf, wx_len);
if(!dumpvars_state) { wx_len = sprintf(wx_buf, "$dumpvars\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 1; }
if(!dumpvars_state) { wx_len = snprintf(wx_buf, 32, "$dumpvars\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 1; }
}
if((xc->num_blackouts)&&(cur_blackout != xc->num_blackouts))
{
if(beg_tim == xc->blackout_times[cur_blackout])
{
wx_len = sprintf(wx_buf, "$dump%s $end\n", (xc->blackout_activity[cur_blackout++]) ? "on" : "off");
wx_len = snprintf(wx_buf, 32, "$dump%s $end\n", (xc->blackout_activity[cur_blackout++]) ? "on" : "off");
fstWritex(xc, wx_buf, wx_len);
}
}
@@ -5332,7 +5336,7 @@ for(;;)
clone_d[j] = srcdata[7-j];
}
}
sprintf((char *)xc->temp_signal_value_buf, "%.16g", d);
snprintf((char *)xc->temp_signal_value_buf, xc->longest_signal_value_len + 1, "%.16g", d);
value_change_callback(user_callback_data_pointer, beg_tim, idx+1, xc->temp_signal_value_buf);
}
}
@@ -5360,7 +5364,7 @@ for(;;)
}
fstVcdID(vcdid_buf, idx+1);
wx_len = sprintf(wx_buf, "r%.16g %s\n", d, vcdid_buf);
wx_len = snprintf(wx_buf, 64, "r%.16g %s\n", d, vcdid_buf);
fstWritex(xc, wx_buf, wx_len);
}
}
@@ -5625,16 +5629,16 @@ for(;;)
}
}
if(dumpvars_state == 1) { wx_len = sprintf(wx_buf, "$end\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 2; }
wx_len = sprintf(wx_buf, "#%" PRIu64 "\n", time_table[i]);
if(dumpvars_state == 1) { wx_len = snprintf(wx_buf, 32, "$end\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 2; }
wx_len = snprintf(wx_buf, 32, "#%" PRIu64 "\n", time_table[i]);
fstWritex(xc, wx_buf, wx_len);
if(!dumpvars_state) { wx_len = sprintf(wx_buf, "$dumpvars\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 1; }
if(!dumpvars_state) { wx_len = snprintf(wx_buf, 32, "$dumpvars\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 1; }
if((xc->num_blackouts)&&(cur_blackout != xc->num_blackouts))
{
if(time_table[i] == xc->blackout_times[cur_blackout])
{
wx_len = sprintf(wx_buf, "$dump%s $end\n", (xc->blackout_activity[cur_blackout++]) ? "on" : "off");
wx_len = snprintf(wx_buf, 32, "$dump%s $end\n", (xc->blackout_activity[cur_blackout++]) ? "on" : "off");
fstWritex(xc, wx_buf, wx_len);
}
}
@@ -5884,7 +5888,7 @@ for(;;)
clone_d[j] = srcdata[7-j];
}
}
sprintf((char *)xc->temp_signal_value_buf, "%.16g", d);
snprintf((char *)xc->temp_signal_value_buf, xc->longest_signal_value_len + 1, "%.16g", d);
value_change_callback(user_callback_data_pointer, time_table[i], idx+1, xc->temp_signal_value_buf);
}
}
@@ -5910,7 +5914,7 @@ for(;;)
}
}
wx_len = sprintf(wx_buf, "r%.16g", d);
wx_len = snprintf(wx_buf, 32, "r%.16g", d);
fstWritex(xc, wx_buf, wx_len);
}
}
@@ -6016,7 +6020,7 @@ if(xc->signal_lens[facidx] == 1)
}
}
sprintf((char *)buf, "%.16g", d);
snprintf((char *)buf, 32, "%.16g", d); /* this will write 18 bytes */
}
}
@@ -6608,7 +6612,7 @@ if(xc->signal_lens[facidx] == 1)
}
}
sprintf(buf, "r%.16g", d);
snprintf(buf, 32, "r%.16g", d); /* this will write 19 bytes */
return(buf);
}
}
+51 -19
View File
@@ -315,15 +315,28 @@ void VlRNG::srandom(uint64_t n) VL_MT_UNSAFE {
if (VL_COUNTONES_I(m_state[0]) < 10) m_state[0] = ~m_state[0];
if (VL_COUNTONES_I(m_state[1]) < 10) m_state[1] = ~m_state[1];
}
// Unused: void VlRNG::set_randstate(const std::string& state) VL_MT_UNSAFE {
// Unused: if (VL_LIKELY(state.length() == sizeof(m_state))) {
// Unused: memcpy(m_state, state.data(), sizeof(m_state));
// Unused: }
// Unused: }
// Unused: std::string VlRNG::get_randstate() const VL_MT_UNSAFE {
// Unused: std::string result{reinterpret_cast<const char *>(&m_state), sizeof(m_state)};
// Unused: return result;
// Unused: }
std::string VlRNG::get_randstate() const VL_MT_UNSAFE {
// Though not stated in IEEE, assumption is the string must be printable
const char* const stateCharsp = reinterpret_cast<const char*>(&m_state);
static_assert(sizeof(m_state) == 16, "");
std::string result{"R00112233445566770011223344556677"};
for (size_t i = 0; i < sizeof(m_state); ++i) {
result[1 + i * 2] = 'a' + ((stateCharsp[i] >> 4) & 15);
result[1 + i * 2 + 1] = 'a' + (stateCharsp[i] & 15);
}
return result;
}
void VlRNG::set_randstate(const std::string& state) VL_MT_UNSAFE {
if (VL_UNLIKELY((state.length() != 1 + 2 * sizeof(m_state)) || (state[0] != 'R'))) {
VL_PRINTF_MT("%%Warning: set_randstate ignored as state string not from get_randstate\n");
return;
}
char* const stateCharsp = reinterpret_cast<char*>(&m_state);
for (size_t i = 0; i < sizeof(m_state); ++i) {
stateCharsp[i]
= (((state[1 + i * 2] - 'a') & 15) << 4) | ((state[1 + i * 2 + 1] - 'a') & 15);
}
}
static uint32_t vl_sys_rand32() VL_MT_SAFE {
// Return random 32-bits using system library.
@@ -1360,16 +1373,18 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
*p = t_tmp;
} else if (obits <= VL_BYTESIZE) {
CData* const p = va_arg(ap, CData*);
*p = owp[0];
*p = VL_CLEAN_II(obits, obits, owp[0]);
} else if (obits <= VL_SHORTSIZE) {
SData* const p = va_arg(ap, SData*);
*p = owp[0];
*p = VL_CLEAN_II(obits, obits, owp[0]);
} else if (obits <= VL_IDATASIZE) {
IData* const p = va_arg(ap, IData*);
*p = owp[0];
*p = VL_CLEAN_II(obits, obits, owp[0]);
} else if (obits <= VL_QUADSIZE) {
QData* const p = va_arg(ap, QData*);
*p = VL_SET_QW(owp);
*p = VL_CLEAN_QQ(obits, obits, VL_SET_QW(owp));
} else {
_vl_clean_inplace_w(obits, owp);
}
}
} // switch
@@ -1881,6 +1896,12 @@ std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_PURE {
return std::string{destout, len};
}
std::string VL_CVT_PACK_STR_ND(const VlQueue<std::string>& q) VL_PURE {
std::string output;
for (const std::string& s : q) output += s;
return output;
}
std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE {
std::string lstring = lhs;
const int32_t rhs_s = rhs; // To signed value
@@ -2587,6 +2608,14 @@ VerilatedVirtualBase* VerilatedContext::threadPoolp() {
return m_threadPool.get();
}
void VerilatedContext::prepareClone() { delete m_threadPool.release(); }
VerilatedVirtualBase* VerilatedContext::threadPoolpOnClone() {
if (VL_UNLIKELY(m_threadPool)) m_threadPool.release();
m_threadPool = std::unique_ptr<VlThreadPool>(new VlThreadPool{this, m_threads - 1});
return m_threadPool.get();
}
VerilatedVirtualBase*
VerilatedContext::enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&)) {
if (!m_executionProfiler) m_executionProfiler.reset(construct(*this));
@@ -3213,15 +3242,18 @@ void VerilatedAssertOneThread::fatal_different() VL_MT_SAFE {
//===========================================================================
// VlDeleter:: Methods
void VlDeleter::deleteAll() {
void VlDeleter::deleteAll() VL_EXCLUDES(m_mutex) VL_EXCLUDES(m_deleteMutex) VL_MT_SAFE {
while (true) {
VerilatedLockGuard lock{m_mutex};
if (m_newGarbage.empty()) break;
VerilatedLockGuard deleteLock{m_deleteMutex};
std::swap(m_newGarbage, m_toDelete);
lock.unlock(); // So destructors can enqueue new objects
{
VerilatedLockGuard lock{m_mutex};
if (m_newGarbage.empty()) break;
m_deleteMutex.lock();
std::swap(m_newGarbage, m_toDelete);
// m_mutex is unlocked here, so destructors can enqueue new objects
}
for (VlDeletable* const objp : m_toDelete) delete objp;
m_toDelete.clear();
m_deleteMutex.unlock();
}
}
+4 -14
View File
@@ -175,6 +175,7 @@ public:
/// Construct mutex (without locking it)
VerilatedMutex() = default;
~VerilatedMutex() = default;
VL_UNCOPYABLE(VerilatedMutex);
const VerilatedMutex& operator!() const { return *this; } // For -fthread_safety
/// Acquire/lock mutex
void lock() VL_ACQUIRE() VL_MT_SAFE {
@@ -218,23 +219,10 @@ public:
/// Construct and hold given mutex lock until destruction or unlock()
explicit VerilatedLockGuard(VerilatedMutex& mutexr) VL_ACQUIRE(mutexr) VL_MT_SAFE
: m_mutexr(mutexr) { // Need () or GCC 4.8 false warning
m_mutexr.lock();
mutexr.lock();
}
/// Destruct and unlock the mutex
~VerilatedLockGuard() VL_RELEASE() { m_mutexr.unlock(); }
/// Lock the mutex
void lock() VL_ACQUIRE() VL_MT_SAFE { m_mutexr.lock(); }
/// Unlock the mutex
void unlock() VL_RELEASE() VL_MT_SAFE { m_mutexr.unlock(); }
/// Acquire/lock mutex and check for stop request.
/// It tries to lock the mutex and if it fails, it check if stop request was send.
/// It returns after locking mutex.
/// This function should be extracted to V3ThreadPool, but due to clang thread-safety
/// limitations it needs to be placed here.
void lockCheckStopRequest(std::function<void()> checkStopRequestFunction)
VL_ACQUIRE() VL_MT_SAFE {
m_mutexr.lockCheckStopRequest(checkStopRequestFunction);
}
};
// Internals: Remember the calling thread at construction time, and make
@@ -580,6 +568,8 @@ public:
void addModel(VerilatedModel*);
VerilatedVirtualBase* threadPoolp();
void prepareClone();
VerilatedVirtualBase* threadPoolpOnClone();
VerilatedVirtualBase*
enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&));
+4
View File
@@ -31,6 +31,8 @@ CFG_CXXFLAGS_NO_UNUSED = @CFG_CXXFLAGS_NO_UNUSED@
CFG_CXXFLAGS_WEXTRA = @CFG_CXXFLAGS_WEXTRA@
# Compiler flags that enable coroutine support
CFG_CXXFLAGS_COROUTINES = @CFG_CXXFLAGS_COROUTINES@
# Linker flags
CFG_LDFLAGS_VERILATED = @CFG_LDFLAGS_VERILATED@
# Linker libraries for multithreading
CFG_LDLIBS_THREADS = @CFG_LDLIBS_THREADS@
@@ -77,6 +79,8 @@ VPATH += ..
VPATH += $(VERILATOR_ROOT)/include
VPATH += $(VERILATOR_ROOT)/include/vltstd
LDFLAGS += $(CFG_LDFLAGS_VERILATED)
#OPT = -ggdb -DPRINTINITSTR -DDETECTCHANGE
#OPT = -ggdb -DPRINTINITSTR
CPPFLAGS += $(OPT)
+4 -4
View File
@@ -210,8 +210,8 @@ private:
std::string result = prefix + "*" + suffix;
// cout << "\nch pre="<<prefix<<" s="<<suffix<<"\nch a="<<old<<"\nch b="<<add
// <<"\ncho="<<result<<endl;
// std::cout << "\nch pre=" << prefix << " s=" << suffix << "\nch a="
// << old << "\nch b=" << add << "\ncho=" << result << std::endl;
return result;
}
bool itemMatchesString(VerilatedCovImpItem* itemp, const std::string& match)
@@ -316,7 +316,7 @@ public:
valps[2] = page_default.c_str();
// Keys -> strings
std::string keys[VerilatedCovConst::MAX_KEYS];
std::array<std::string, VerilatedCovConst::MAX_KEYS> keys;
for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
if (ckeyps[i] && ckeyps[i][0]) keys[i] = ckeyps[i];
}
@@ -337,7 +337,7 @@ public:
const std::string key = keys[i];
if (!keys[i].empty()) {
const std::string val = valps[i];
// cout<<" "<<__FUNCTION__<<" "<<key<<" = "<<val<<endl;
// std::cout << " " << __FUNCTION__ << " " << key << " = " << val << std::endl;
m_insertp->m_keys[addKeynum] = valueIndex(key);
m_insertp->m_vals[addKeynum] = valueIndex(val);
++addKeynum;
+38 -4
View File
@@ -152,9 +152,11 @@ extern const char* vl_mc_scan_plusargs(const char* prefixp) VL_MT_SAFE; // PLIi
// Base macros
// Return true if data[bit] set; not 0/1 return, but 0/non-zero return.
// Arguments must not have side effects
#define VL_BITISSETLIMIT_W(data, width, bit) (((bit) < (width)) && VL_BITISSET_W(data, bit))
// Shift appropriate word by bit. Does not account for wrapping between two words
// Argument 'bit' must not have side effects
#define VL_BITRSHIFT_W(data, bit) ((data)[VL_BITWORD_E(bit)] >> VL_BITBIT_E(bit))
// Create two 32-bit words from quadword
@@ -758,6 +760,7 @@ static inline IData VL_ONEHOT0_W(int words, WDataInP const lwp) VL_PURE {
static inline IData VL_CLOG2_I(IData lhs) VL_PURE {
// There are faster algorithms, or fls GCC4 builtins, but rarely used
// In C++20 there will be std::bit_width(lhs) - 1
if (VL_UNLIKELY(!lhs)) return 0;
--lhs;
int shifts = 0;
@@ -953,11 +956,19 @@ static inline void VL_NEGATE_INPLACE_W(int words, WDataOutP owp_lwp) VL_MT_SAFE
// EMIT_RULE: VL_MUL: oclean=dirty; lclean==clean; rclean==clean;
// EMIT_RULE: VL_DIV: oclean=dirty; lclean==clean; rclean==clean;
// EMIT_RULE: VL_MODDIV: oclean=dirty; lclean==clean; rclean==clean;
#define VL_DIV_III(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) / (rhs))
#define VL_DIV_QQQ(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) / (rhs))
static inline IData VL_DIV_III(int lbits, IData lhs, IData rhs) {
return (rhs == 0) ? 0 : lhs / rhs;
}
static inline QData VL_DIV_QQQ(int lbits, QData lhs, QData rhs) {
return (rhs == 0) ? 0 : lhs / rhs;
}
#define VL_DIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 0))
#define VL_MODDIV_III(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) % (rhs))
#define VL_MODDIV_QQQ(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) % (rhs))
static inline IData VL_MODDIV_III(int lbits, IData lhs, IData rhs) {
return (rhs == 0) ? 0 : lhs % rhs;
}
static inline QData VL_MODDIV_QQQ(int lbits, QData lhs, QData rhs) {
return (rhs == 0) ? 0 : lhs % rhs;
}
#define VL_MODDIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 1))
static inline WDataOutP VL_ADD_W(int words, WDataOutP owp, WDataInP const lwp,
@@ -1515,6 +1526,28 @@ static inline WDataOutP VL_STREAML_WWI(int lbits, WDataOutP owp, WDataInP const
return owp;
}
template <typename T>
static inline void VL_ASSIGN_DYN_Q(VlQueue<T>& q, int elem_size, int lbits, QData from) {
const int size = (lbits + elem_size - 1) / elem_size;
q.renew(size);
const QData mask = VL_MASK_Q(elem_size);
for (int i = 0; i < size; ++i) q.at(i) = (T)((from >> (i * elem_size)) & mask);
}
template <typename T>
static inline IData VL_DYN_TO_I(const VlQueue<T>& q, int elem_size) {
IData ret = 0;
for (int i = 0; i < q.size(); ++i) ret |= q.at(i) << (i * elem_size);
return ret;
}
template <typename T>
static inline QData VL_DYN_TO_Q(const VlQueue<T>& q, int elem_size) {
QData ret = 0;
for (int i = 0; i < q.size(); ++i) ret |= q.at(i) << (i * elem_size);
return ret;
}
// Because concats are common and wide, it's valuable to always have a clean output.
// Thus we specify inputs must be clean, so we don't need to clean the output.
// Note the bit shifts are always constants, so the adds in these constify out.
@@ -2185,6 +2218,7 @@ extern IData VL_DIST_UNIFORM(IData& seedr, IData ustart, IData uend) VL_MT_SAFE;
// Conversion functions
extern std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_PURE;
extern std::string VL_CVT_PACK_STR_ND(const VlQueue<std::string>& q) VL_PURE;
inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE {
VlWide<VL_WQ_WORDS_E> lw;
VL_SET_WQ(lw, lhs);
-31
View File
@@ -1,31 +0,0 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Code available from: https://verilator.org
//
// Copyright 2010-2023 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
///
/// \file
/// \brief Verilated old string and data-type header
///
/// This file is deprecated, and provided for backwards compatibility.
/// Include verilated.h instead.
///
//*************************************************************************
#ifndef VERILATOR_VERILATED_HEAVY_H_
#define VERILATOR_VERILATED_HEAVY_H_
#ifdef VL_NO_LEGACY
#error "Include <verilated.h> instead of <verilated_heavy.h>"
#endif
#include "verilated.h"
#endif // Guard
+57 -24
View File
@@ -26,10 +26,6 @@
// verilator lint_off TIMESCALEMOD
// verilator lint_off UNUSEDSIGNAL
package std;
// The process class is not implemented, but it's predeclared here,
// so the linter accepts references to it.
typedef class process;
class mailbox #(type T);
protected int m_bound;
protected T m_queue[$];
@@ -45,7 +41,7 @@ package std;
task put(T message);
`ifdef VERILATOR_TIMING
if (m_bound != 0)
wait (m_queue.size() < m_bound);
wait (m_queue.size() < m_bound);
m_queue.push_back(message);
`endif
endtask
@@ -117,43 +113,80 @@ package std;
endclass
class process;
typedef enum { FINISHED, RUNNING, WAITING, SUSPENDED, KILLED } state;
static process _s_global_process;
typedef enum {
FINISHED = 0,
RUNNING = 1,
WAITING = 2,
SUSPENDED = 3,
KILLED = 4
} state;
`ifdef VERILATOR_TIMING
// Width visitor changes it to VlProcessRef
protected chandle m_process;
`endif
static function process self();
// Unsupported, emulating with single process' state
if (!_s_global_process) _s_global_process = new;
return _s_global_process;
process p = new;
`ifdef VERILATOR_TIMING
$c(p.m_process, " = vlProcess;");
`endif
return p;
endfunction
protected function void set_status(state s);
`ifdef VERILATOR_TIMING
$c(m_process, "->state(", s, ");");
`endif
endfunction
function state status();
// Unsupported, emulating with single process' state
`ifdef VERILATOR_TIMING
return state'($c(m_process, "->state()"));
`else
return RUNNING;
`endif
endfunction
function void kill();
$error("std::process::kill() not supported");
set_status(KILLED);
endfunction
task await();
$error("std::process::await() not supported");
endtask
function void suspend();
$error("std::process::suspend() not supported");
endfunction
function void resume();
$error("std::process::resume() not supported");
set_status(RUNNING);
endfunction
// When really implemented, srandom must operates on the process, but for
task await();
`ifdef VERILATOR_TIMING
wait (status() == FINISHED || status() == KILLED);
`endif
endtask
// When really implemented, srandom must operate on the process, but for
// now rely on the srandom() that is automatically generated for all
// classes.
//
// function void srandom(int seed);
// endfunction
// The methods below work only if set_randstate is never applied to
// a state string created before another such string. Full support
// could use VlRNG class to store the state per process in VlProcess
// objects.
function string get_randstate();
// Could operate on all proceses for now
// No error, as harmless until set_randstate is called
return "NOT_SUPPORTED";
string s;
s.itoa($random); // Get a random number
set_randstate(s); // Pretend it's the state of RNG
return s;
endfunction
function void set_randstate(string randstate);
$error("std::process::set_randstate() not supported");
// Could operate on all proceses for now
function void set_randstate(string s);
$urandom(s.atoi()); // Set the seed using a string
endfunction
endclass
endpackage
+1 -1
View File
@@ -173,7 +173,7 @@ public:
VerilatedLockGuard lock{m_mutex};
while (m_ready.empty()) {
m_waiting = true;
m_cv.wait(lock);
m_cv.wait(m_mutex);
}
m_waiting = false;
// As noted above this is inefficient if our ready list is ever
+13 -3
View File
@@ -30,7 +30,16 @@ void VlCoroutineHandle::resume() {
// main process
if (VL_LIKELY(m_coro)) {
VL_DEBUG_IF(VL_DBG_MSGF(" Resuming: "); dump(););
m_coro();
if (m_process) { // If process state is managed with std::process
if (m_process->state() == VlProcess::KILLED) {
m_coro.destroy();
} else {
m_process->state(VlProcess::RUNNING);
m_coro();
}
} else {
m_coro();
}
m_coro = nullptr;
}
}
@@ -95,8 +104,9 @@ void VlTriggerScheduler::resume(const char* eventDescription) {
VL_DEBUG_IF(dump(eventDescription);
VL_DBG_MSGF(" Resuming processes waiting for %s\n", eventDescription););
#endif
for (auto& susp : m_ready) susp.resume();
m_ready.clear();
std::swap(m_ready, m_resumeQueue);
for (VlCoroutineHandle& coro : m_resumeQueue) coro.resume();
m_resumeQueue.clear();
commit(eventDescription);
}
+61 -26
View File
@@ -96,33 +96,52 @@ class VlCoroutineHandle final {
// MEMBERS
std::coroutine_handle<> m_coro; // The wrapped coroutine handle
VlProcessRef m_process; // Data of the suspended process, null if not needed
VlFileLineDebug m_fileline;
public:
// CONSTRUCTORS
// Construct
VlCoroutineHandle()
: m_coro{nullptr} {}
VlCoroutineHandle(std::coroutine_handle<> coro, VlFileLineDebug fileline)
// non-explicit:
// cppcheck-suppress noExplicitConstructor
VlCoroutineHandle(VlProcessRef process)
: m_coro{nullptr}
, m_process{process} {
if (m_process) m_process->state(VlProcess::WAITING);
}
VlCoroutineHandle(std::coroutine_handle<> coro, VlProcessRef process, VlFileLineDebug fileline)
: m_coro{coro}
, m_fileline{fileline} {}
, m_process{process}
, m_fileline{fileline} {
if (m_process) m_process->state(VlProcess::WAITING);
}
// Move the handle, leaving a nullptr
// non-explicit:
// cppcheck-suppress noExplicitConstructor
VlCoroutineHandle(VlCoroutineHandle&& moved)
: m_coro{std::exchange(moved.m_coro, nullptr)}
, m_process{std::exchange(moved.m_process, nullptr)}
, m_fileline{moved.m_fileline} {}
// Destroy if the handle isn't null
~VlCoroutineHandle() {
// Usually these coroutines should get resumed; we only need to clean up if we destroy a
// model with some coroutines suspended
if (VL_UNLIKELY(m_coro)) m_coro.destroy();
if (VL_UNLIKELY(m_coro)) {
m_coro.destroy();
if (m_process && m_process->state() != VlProcess::KILLED) {
m_process->state(VlProcess::FINISHED);
}
}
}
// METHODS
// Move the handle, leaving a null handle
auto& operator=(VlCoroutineHandle&& moved) {
m_coro = std::exchange(moved.m_coro, nullptr);
m_process = std::exchange(moved.m_process, nullptr);
m_fileline = moved.m_fileline;
return *this;
}
// Resume the coroutine if the handle isn't null
// Resume the coroutine if the handle isn't null and the process isn't killed
void resume();
#ifdef VL_DEBUG
void dump() const;
@@ -173,21 +192,24 @@ public:
void dump() const;
#endif
// Used by coroutines for co_awaiting a certain simulation time
auto delay(uint64_t delay, const char* filename = VL_UNKNOWN, int lineno = 0) {
auto delay(uint64_t delay, VlProcessRef process, const char* filename = VL_UNKNOWN,
int lineno = 0) {
struct Awaitable {
VlProcessRef process; // Data of the suspended process, null if not needed
VlDelayedCoroutineQueue& queue;
uint64_t delay;
VlFileLineDebug fileline;
bool await_ready() const { return false; } // Always suspend
void await_suspend(std::coroutine_handle<> coro) {
queue.push_back({delay, VlCoroutineHandle{coro, fileline}});
queue.push_back({delay, VlCoroutineHandle{coro, process, fileline}});
// Move last element to the proper place in the max-heap
std::push_heap(queue.begin(), queue.end());
}
void await_resume() const {}
};
return Awaitable{m_queue, m_context.time() + delay, VlFileLineDebug{filename, lineno}};
return Awaitable{process, m_queue, m_context.time() + delay,
VlFileLineDebug{filename, lineno}};
}
};
@@ -207,6 +229,10 @@ class VlTriggerScheduler final {
// (not resumable)
VlCoroutineVec m_ready; // Coroutines that can be resumed (all coros from m_uncommitted are
// moved here in commit())
VlCoroutineVec m_resumeQueue; // Coroutines being resumed by resume(); kept as a field to
// avoid reallocation. Resumed coroutines are moved to
// m_resumeQueue to allow adding coroutines to m_ready
// during resume(). Outside of resume() should always be empty.
public:
// METHODS
@@ -220,21 +246,23 @@ public:
void dump(const char* eventDescription) const;
#endif
// Used by coroutines for co_awaiting a certain trigger
auto trigger(const char* eventDescription = VL_UNKNOWN, const char* filename = VL_UNKNOWN,
int lineno = 0) {
auto trigger(bool commit, VlProcessRef process, const char* eventDescription = VL_UNKNOWN,
const char* filename = VL_UNKNOWN, int lineno = 0) {
VL_DEBUG_IF(VL_DBG_MSGF(" Suspending process waiting for %s at %s:%d\n",
eventDescription, filename, lineno););
struct Awaitable {
VlCoroutineVec& suspended; // Coros waiting on trigger
VlProcessRef process; // Data of the suspended process, null if not needed
VlFileLineDebug fileline;
bool await_ready() const { return false; } // Always suspend
void await_suspend(std::coroutine_handle<> coro) {
suspended.emplace_back(coro, fileline);
suspended.emplace_back(coro, process, fileline);
}
void await_resume() const {}
};
return Awaitable{m_uncommitted, VlFileLineDebug{filename, lineno}};
return Awaitable{commit ? m_ready : m_uncommitted, process,
VlFileLineDebug{filename, lineno}};
}
};
@@ -267,18 +295,19 @@ class VlDynamicTriggerScheduler final {
// with destructive post updates, e.g. named events)
// METHODS
auto awaitable(VlCoroutineVec& queue, const char* filename, int lineno) {
auto awaitable(VlProcessRef process, VlCoroutineVec& queue, const char* filename, int lineno) {
struct Awaitable {
VlProcessRef process; // Data of the suspended process, null if not needed
VlCoroutineVec& suspended; // Coros waiting on trigger
VlFileLineDebug fileline;
bool await_ready() const { return false; } // Always suspend
void await_suspend(std::coroutine_handle<> coro) {
suspended.emplace_back(coro, fileline);
suspended.emplace_back(coro, process, fileline);
}
void await_resume() const {}
};
return Awaitable{queue, VlFileLineDebug{filename, lineno}};
return Awaitable{process, queue, VlFileLineDebug{filename, lineno}};
}
public:
@@ -292,23 +321,26 @@ public:
void dump() const;
#endif
// Used by coroutines for co_awaiting trigger evaluation
auto evaluation(const char* eventDescription, const char* filename, int lineno) {
auto evaluation(VlProcessRef process, const char* eventDescription, const char* filename,
int lineno) {
VL_DEBUG_IF(VL_DBG_MSGF(" Suspending process waiting for %s at %s:%d\n",
eventDescription, filename, lineno););
return awaitable(m_suspended, filename, lineno);
return awaitable(process, m_suspended, filename, lineno);
}
// Used by coroutines for co_awaiting the trigger post update step
auto postUpdate(const char* eventDescription, const char* filename, int lineno) {
auto postUpdate(VlProcessRef process, const char* eventDescription, const char* filename,
int lineno) {
VL_DEBUG_IF(
VL_DBG_MSGF(" Process waiting for %s at %s:%d awaiting the post update step\n",
eventDescription, filename, lineno););
return awaitable(m_post, filename, lineno);
return awaitable(process, m_post, filename, lineno);
}
// Used by coroutines for co_awaiting the resumption step (in 'act' eval)
auto resumption(const char* eventDescription, const char* filename, int lineno) {
auto resumption(VlProcessRef process, const char* eventDescription, const char* filename,
int lineno) {
VL_DEBUG_IF(VL_DBG_MSGF(" Process waiting for %s at %s:%d awaiting resumption\n",
eventDescription, filename, lineno););
return awaitable(m_triggered, filename, lineno);
return awaitable(process, m_triggered, filename, lineno);
}
};
@@ -338,24 +370,27 @@ class VlForkSync final {
public:
// Create the join object and set the counter to the specified number
void init(size_t count) { m_join.reset(new VlJoin{count, {}}); }
void init(size_t count, VlProcessRef process) { m_join.reset(new VlJoin{count, {process}}); }
// Called whenever any of the forked processes finishes. If the join counter reaches 0, the
// main process gets resumed
void done(const char* filename = VL_UNKNOWN, int lineno = 0);
// Used by coroutines for co_awaiting a join
auto join(const char* filename = VL_UNKNOWN, int lineno = 0) {
auto join(VlProcessRef process, const char* filename = VL_UNKNOWN, int lineno = 0) {
assert(m_join);
VL_DEBUG_IF(
VL_DBG_MSGF(" Awaiting join of fork at: %s:%d\n", filename, lineno););
struct Awaitable {
VlProcessRef process; // Data of the suspended process, null if not needed
const std::shared_ptr<VlJoin> join; // Join to await on
VlFileLineDebug fileline;
bool await_ready() { return join->m_counter == 0; } // Suspend if join still exists
void await_suspend(std::coroutine_handle<> coro) { join->m_susp = {coro, fileline}; }
void await_suspend(std::coroutine_handle<> coro) {
join->m_susp = {coro, process, fileline};
}
void await_resume() const {}
};
return Awaitable{m_join, VlFileLineDebug{filename, lineno}};
return Awaitable{process, m_join, VlFileLineDebug{filename, lineno}};
}
};
+7 -4
View File
@@ -75,7 +75,7 @@ public:
// Get an element from the front of the queue. Blocks if none available
T get() VL_MT_SAFE_EXCLUDES(m_mutex) {
VerilatedLockGuard lock{m_mutex};
m_cv.wait(lock, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); });
m_cv.wait(m_mutex, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); });
assert(!m_queue.empty());
T value = m_queue.front();
m_queue.pop_front();
@@ -447,7 +447,9 @@ public:
if (VL_UNLIKELY(diff)) fullIData(oldp, newval, bits);
}
VL_ATTR_ALWINLINE void chgQData(uint32_t* oldp, QData newval, int bits) {
const uint64_t diff = *reinterpret_cast<QData*>(oldp) ^ newval;
QData old;
std::memcpy(&old, oldp, sizeof(old));
const uint64_t diff = old ^ newval;
if (VL_UNLIKELY(diff)) fullQData(oldp, newval, bits);
}
VL_ATTR_ALWINLINE void chgWData(uint32_t* oldp, const WData* newvalp, int bits) {
@@ -460,8 +462,9 @@ public:
}
VL_ATTR_ALWINLINE void chgEvent(uint32_t* oldp, VlEvent newval) { fullEvent(oldp, newval); }
VL_ATTR_ALWINLINE void chgDouble(uint32_t* oldp, double newval) {
// cppcheck-suppress invalidPointerCast
if (VL_UNLIKELY(*reinterpret_cast<double*>(oldp) != newval)) fullDouble(oldp, newval);
double old;
std::memcpy(&old, oldp, sizeof(old));
if (VL_UNLIKELY(old != newval)) fullDouble(oldp, newval);
}
};
+3 -3
View File
@@ -482,7 +482,7 @@ VL_ATTR_NOINLINE void VerilatedTrace<VL_SUB_T, VL_BUF_T>::ParallelWorkerData::wa
// We have been spinning for a while, so yield the thread
VerilatedLockGuard lock{m_mutex};
m_waiting = true;
m_cv.wait(lock, [this] { return m_ready.load(std::memory_order_relaxed); });
m_cv.wait(m_mutex, [this] { return m_ready.load(std::memory_order_relaxed); });
m_waiting = false;
}
@@ -872,7 +872,7 @@ void VerilatedTraceBuffer<VL_BUF_T>::fullIData(uint32_t* oldp, IData newval, int
template <>
void VerilatedTraceBuffer<VL_BUF_T>::fullQData(uint32_t* oldp, QData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*reinterpret_cast<QData*>(oldp) = newval;
std::memcpy(oldp, &newval, sizeof(newval));
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
emitQData(code, newval, bits);
}
@@ -888,7 +888,7 @@ void VerilatedTraceBuffer<VL_BUF_T>::fullWData(uint32_t* oldp, const WData* newv
template <>
void VerilatedTraceBuffer<VL_BUF_T>::fullDouble(uint32_t* oldp, double newval) {
const uint32_t code = oldp - m_sigs_oldvalp;
*reinterpret_cast<double*>(oldp) = newval;
std::memcpy(oldp, &newval, sizeof(newval));
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
// cppcheck-suppress invalidPointerCast
emitDouble(code, newval);
+266 -8
View File
@@ -73,6 +73,36 @@ extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
#define VL_OUT(name, msb, lsb) IData name ///< Declare output signal, 17-32 bits
#define VL_OUTW(name, msb, lsb, words) VlWide<words> name ///< Declare output signal, 65+ bits
//===================================================================
// VlProcess stores metadata of running processes
class VlProcess final {
// MEMBERS
int m_state; // Current state of the process
public:
// TYPES
enum : int { // Type int for compatibility with $c
FINISHED = 0,
RUNNING = 1,
WAITING = 2,
SUSPENDED = 3,
KILLED = 4,
};
// CONSTRUCTORS
VlProcess()
: m_state{RUNNING} {}
// METHODS
int state() { return m_state; }
void state(int s) { m_state = s; }
};
using VlProcessRef = std::shared_ptr<VlProcess>;
inline std::string VL_TO_STRING(const VlProcessRef& p) { return std::string("process"); }
//===================================================================
// Activity trigger vector
@@ -161,8 +191,8 @@ public:
VlRNG() VL_MT_SAFE;
explicit VlRNG(uint64_t seed0) VL_MT_SAFE : m_state{0x12341234UL, seed0} {}
void srandom(uint64_t n) VL_MT_UNSAFE;
// Unused: std::string get_randstate() const VL_MT_UNSAFE;
// Unused: void set_randstate(const std::string& state) VL_MT_UNSAFE;
std::string get_randstate() const VL_MT_UNSAFE;
void set_randstate(const std::string& state) VL_MT_UNSAFE;
uint64_t rand64() VL_MT_UNSAFE;
// Threadsafe, but requires use on vl_thread_rng
static uint64_t vl_thread_rng_rand64() VL_MT_SAFE;
@@ -487,7 +517,7 @@ public:
template <typename Func>
VlQueue unique(Func with_func) const {
VlQueue out;
std::set<T_Value> saw;
std::set<decltype(with_func(0, m_deque[0]))> saw;
for (const auto& i : m_deque) {
const auto i_mapped = with_func(0, i);
const auto it = saw.find(i_mapped);
@@ -516,7 +546,7 @@ public:
VlQueue<IData> unique_index(Func with_func) const {
VlQueue<IData> out;
IData index = 0;
std::unordered_set<T_Value> saw;
std::set<decltype(with_func(0, m_deque[0]))> saw;
for (const auto& i : m_deque) {
const auto i_mapped = with_func(index, i);
auto it = saw.find(i_mapped);
@@ -830,6 +860,22 @@ public:
}
return out;
}
template <typename Func>
VlQueue<T_Value> unique(Func with_func) const {
VlQueue<T_Value> out;
T_Key default_key;
using WithType = decltype(with_func(m_map.begin()->first, m_map.begin()->second));
std::set<WithType> saw;
for (const auto& i : m_map) {
const auto i_mapped = with_func(default_key, i.second);
const auto it = saw.find(i_mapped);
if (it == saw.end()) {
saw.insert(it, i_mapped);
out.push_back(i.second);
}
}
return out;
}
VlQueue<T_Key> unique_index() const {
VlQueue<T_Key> out;
std::set<T_Key> saw;
@@ -843,6 +889,21 @@ public:
return out;
}
template <typename Func>
VlQueue<T_Key> unique_index(Func with_func) const {
VlQueue<T_Key> out;
using WithType = decltype(with_func(m_map.begin()->first, m_map.begin()->second));
std::set<WithType> saw;
for (const auto& i : m_map) {
const auto i_mapped = with_func(i.first, i.second);
auto it = saw.find(i_mapped);
if (it == saw.end()) {
saw.insert(it, i_mapped);
out.push_back(i.first);
}
}
return out;
}
template <typename Func>
VlQueue<T_Value> find(Func with_func) const {
VlQueue<T_Value> out;
for (const auto& i : m_map)
@@ -903,6 +964,16 @@ public:
});
return VlQueue<T_Value>::cons(it->second);
}
template <typename Func>
VlQueue<T_Value> min(Func with_func) const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::min_element(
m_map.begin(), m_map.end(),
[&with_func](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return with_func(a.first, a.second) < with_func(b.first, b.second);
});
return VlQueue<T_Value>::cons(it->second);
}
VlQueue<T_Value> max() const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::max_element(
@@ -912,6 +983,16 @@ public:
});
return VlQueue<T_Value>::cons(it->second);
}
template <typename Func>
VlQueue<T_Value> max(Func with_func) const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::max_element(
m_map.begin(), m_map.end(),
[&with_func](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return with_func(a.first, a.second) < with_func(b.first, b.second);
});
return VlQueue<T_Value>::cons(it->second);
}
T_Value r_sum() const {
T_Value out(0); // Type must have assignment operator
@@ -1040,9 +1121,15 @@ void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
template <class T_Value, std::size_t T_Depth>
struct VlUnpacked final {
private:
// TYPES
using T_Key = IData; // Index type, for uniformity with other containers
using Unpacked = T_Value[T_Depth];
public:
// MEMBERS
// This should be the only data member, otherwise generated static initializers need updating
T_Value m_storage[T_Depth]; // Contents of the unpacked array
Unpacked m_storage; // Contents of the unpacked array
// CONSTRUCTORS
// Default constructors and destructor are used. Note however that C++20 requires that
@@ -1053,6 +1140,7 @@ struct VlUnpacked final {
// Default copy assignment operators are used.
// METHODS
public:
// Raw access
WData* data() { return &m_storage[0]; }
const WData* data() const { return &m_storage[0]; }
@@ -1065,6 +1153,170 @@ struct VlUnpacked final {
bool neq(const VlUnpacked<T_Value, T_Depth>& that) const { return neq(*this, that); }
// Similar to 'neq' above, *this = that used for change detection
void assign(const VlUnpacked<T_Value, T_Depth>& that) { *this = that; }
bool operator==(const VlUnpacked<T_Value, T_Depth>& that) const { return !neq(that); }
bool operator!=(const VlUnpacked<T_Value, T_Depth>& that) { return neq(that); }
void sort() { std::sort(std::begin(m_storage), std::end(m_storage)); }
template <typename Func>
void sort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
std::sort(std::begin(m_storage), std::end(m_storage),
[=](const T_Value& a, const T_Value& b) {
// index number is meaningless with sort, as it changes
return with_func(0, a) < with_func(0, b);
});
}
// std::rbegin/std::rend not available until C++14
void rsort() {
std::sort(std::begin(m_storage), std::end(m_storage), std::greater<T_Value>());
}
template <typename Func>
void rsort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
// std::rbegin/std::rend not available until C++14, so using > below
std::sort(std::begin(m_storage), std::end(m_storage),
[=](const T_Value& a, const T_Value& b) {
// index number is meaningless with sort, as it changes
return with_func(0, a) > with_func(0, b);
});
}
void reverse() { std::reverse(std::begin(m_storage), std::end(m_storage)); }
void shuffle() { std::shuffle(std::begin(m_storage), std::end(m_storage), VlURNG{}); }
VlQueue<T_Value> unique() const {
VlQueue<T_Value> out;
std::set<T_Value> saw;
for (const auto& i : m_storage) {
const auto it = saw.find(i);
if (it == saw.end()) {
saw.insert(it, i);
out.push_back(i);
}
}
return out;
}
template <typename Func>
VlQueue<T_Value> unique(Func with_func) const {
VlQueue<T_Value> out;
std::set<T_Value> saw;
for (const auto& i : m_storage) {
const auto i_mapped = with_func(0, i);
const auto it = saw.find(i_mapped);
if (it == saw.end()) {
saw.insert(it, i_mapped);
out.push_back(i);
}
}
return out;
}
VlQueue<T_Key> unique_index() const {
VlQueue<T_Key> out;
IData index = 0;
std::set<T_Value> saw;
for (const auto& i : m_storage) {
const auto it = saw.find(i);
if (it == saw.end()) {
saw.insert(it, i);
out.push_back(index);
}
++index;
}
return out;
}
template <typename Func>
VlQueue<T_Key> unique_index(Func with_func) const {
VlQueue<T_Key> out;
IData index = 0;
std::unordered_set<T_Value> saw;
for (const auto& i : m_storage) {
const auto i_mapped = with_func(index, i);
auto it = saw.find(i_mapped);
if (it == saw.end()) {
saw.insert(it, i_mapped);
out.push_back(index);
}
++index;
}
return out;
}
template <typename Func>
VlQueue<T_Value> find(Func with_func) const {
VlQueue<T_Value> out;
IData index = 0;
for (const auto& i : m_storage) {
if (with_func(index, i)) out.push_back(i);
++index;
}
return out;
}
template <typename Func>
VlQueue<T_Key> find_index(Func with_func) const {
VlQueue<T_Key> out;
IData index = 0;
for (const auto& i : m_storage) {
if (with_func(index, i)) out.push_back(index);
++index;
}
return out;
}
template <typename Func>
VlQueue<T_Value> find_first(Func with_func) const {
// Can't use std::find_if as need index number
IData index = 0;
for (const auto& i : m_storage) {
if (with_func(index, i)) return VlQueue<T_Value>::cons(i);
++index;
}
return VlQueue<T_Value>{};
}
template <typename Func>
VlQueue<T_Key> find_first_index(Func with_func) const {
IData index = 0;
for (const auto& i : m_storage) {
if (with_func(index, i)) return VlQueue<IData>::cons(index);
++index;
}
return VlQueue<T_Key>{};
}
template <typename Func>
VlQueue<T_Value> find_last(Func with_func) const {
for (int i = T_Depth - 1; i >= 0; i--) {
if (with_func(i, m_storage[i])) return VlQueue<T_Value>::cons(m_storage[i]);
}
return VlQueue<T_Value>{};
}
template <typename Func>
VlQueue<T_Key> find_last_index(Func with_func) const {
for (int i = T_Depth - 1; i >= 0; i--) {
if (with_func(i, m_storage[i])) return VlQueue<IData>::cons(i);
}
return VlQueue<T_Key>{};
}
// Reduction operators
VlQueue<T_Value> min() const {
const auto it = std::min_element(std::begin(m_storage), std::end(m_storage));
return VlQueue<T_Value>::cons(*it);
}
template <typename Func>
VlQueue<T_Value> min(Func with_func) const {
const auto it = std::min_element(std::begin(m_storage), std::end(m_storage),
[&with_func](const IData& a, const IData& b) {
return with_func(0, a) < with_func(0, b);
});
return VlQueue<T_Value>::cons(*it);
}
VlQueue<T_Value> max() const {
const auto it = std::max_element(std::begin(m_storage), std::end(m_storage));
return VlQueue<T_Value>::cons(*it);
}
template <typename Func>
VlQueue<T_Value> max(Func with_func) const {
const auto it = std::max_element(std::begin(m_storage), std::end(m_storage),
[&with_func](const IData& a, const IData& b) {
return with_func(0, a) < with_func(0, b);
});
return VlQueue<T_Value>::cons(*it);
}
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
@@ -1138,7 +1390,7 @@ public:
}
// Deletes all queued garbage objects.
void deleteAll() VL_MT_SAFE;
void deleteAll() VL_EXCLUDES(m_mutex) VL_EXCLUDES(m_deleteMutex) VL_MT_SAFE;
};
//===================================================================
@@ -1286,8 +1538,14 @@ public:
// For 'if (ptr)...'
operator bool() const { return m_objp; }
// In SV A == B iff both are handles to the same object (IEEE 1800-2017 8.4)
bool operator==(const VlClassRef& rhs) const { return m_objp == rhs.m_objp; };
bool operator!=(const VlClassRef& rhs) const { return m_objp != rhs.m_objp; };
template <typename T_OtherClass>
bool operator==(const VlClassRef<T_OtherClass>& rhs) const {
return m_objp == rhs.m_objp;
};
template <typename T_OtherClass>
bool operator!=(const VlClassRef<T_OtherClass>& rhs) const {
return m_objp != rhs.m_objp;
};
};
template <typename T, typename U>
+2 -16
View File
@@ -410,28 +410,14 @@ void VerilatedVcd::dumpHeader() {
if (*np == ' ') np++;
if (*np == '\t') break; // tab means signal name starts
printIndent(1);
// Find character after name end
const char* sp = np;
while (*sp && *sp != ' ' && *sp != '\t' && !(*sp & '\x80')) sp++;
printStr("$scope ");
if (*sp & '\x80') {
switch (*sp & 0x7f) {
case VLT_TRACE_SCOPE_STRUCT: printStr("struct "); break;
case VLT_TRACE_SCOPE_INTERFACE: printStr("interface "); break;
case VLT_TRACE_SCOPE_UNION: printStr("union "); break;
default: printStr("module ");
}
} else {
printStr("module ");
}
printStr("$scope module ");
for (; *np && *np != ' ' && *np != '\t'; np++) {
if (*np == '[') {
printStr("[");
} else if (*np == ']') {
printStr("]");
} else if (!(*np & '\x80')) {
} else {
*m_writep++ = *np;
}
}
+21 -24
View File
@@ -165,6 +165,7 @@ class VerilatedVpioVarBase VL_NOT_FINAL : public VerilatedVpio {
protected:
const VerilatedVar* m_varp = nullptr;
const VerilatedScope* m_scopep = nullptr;
std::string m_fullname;
const VerilatedRange& get_range() const {
// Determine number of dimensions and return outermost
return (m_varp->dims() > 1) ? m_varp->unpacked() : m_varp->packed();
@@ -173,11 +174,13 @@ protected:
public:
VerilatedVpioVarBase(const VerilatedVar* varp, const VerilatedScope* scopep)
: m_varp{varp}
, m_scopep{scopep} {}
, m_scopep{scopep}
, m_fullname{std::string{m_scopep->name()} + '.' + m_varp->name()} {}
explicit VerilatedVpioVarBase(const VerilatedVpioVarBase* varp) {
if (varp) {
m_varp = varp->m_varp;
m_scopep = varp->m_scopep;
m_fullname = varp->m_fullname;
}
}
static VerilatedVpioVarBase* castp(vpiHandle h) {
@@ -188,11 +191,7 @@ public:
uint32_t size() const override { return get_range().elements(); }
const VerilatedRange* rangep() const override { return &get_range(); }
const char* name() const override { return m_varp->name(); }
const char* fullname() const override {
static thread_local std::string t_out;
t_out = std::string{m_scopep->name()} + "." + name();
return t_out.c_str();
}
const char* fullname() const override { return m_fullname.c_str(); }
};
class VerilatedVpioParam final : public VerilatedVpioVarBase {
@@ -646,23 +645,21 @@ public:
continue;
}
VerilatedVpiCbHolder& ho = *it++;
if (VerilatedVpioVar* const varop = VerilatedVpioVar::castp(ho.cb_datap()->obj)) {
void* const newDatap = varop->varDatap();
void* const prevDatap
= varop->prevDatap(); // Was malloced when we added the callback
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
varop->fullname(), *(static_cast<CData*>(newDatap)),
*(static_cast<CData*>(prevDatap)), newDatap,
prevDatap););
if (std::memcmp(prevDatap, newDatap, varop->entSize()) != 0) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" PRId64 " %s v[0]=%d\n",
ho.id(), varop->fullname(),
*(static_cast<CData*>(newDatap))););
update.insert(varop);
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
(ho.cb_rtnp())(ho.cb_datap());
called = true;
}
VerilatedVpioVar* const varop
= reinterpret_cast<VerilatedVpioVar*>(ho.cb_datap()->obj);
void* const newDatap = varop->varDatap();
void* const prevDatap = varop->prevDatap(); // Was malloced when we added the callback
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
varop->fullname(), *(static_cast<CData*>(newDatap)),
*(static_cast<CData*>(prevDatap)), newDatap, prevDatap););
if (std::memcmp(prevDatap, newDatap, varop->entSize()) != 0) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" PRId64 " %s v[0]=%d\n",
ho.id(), varop->fullname(),
*(static_cast<CData*>(newDatap))););
update.insert(varop);
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
(ho.cb_rtnp())(ho.cb_datap());
called = true;
}
if (was_last) break;
}
@@ -2310,7 +2307,7 @@ PLI_INT32 vpi_flush(void) {
VerilatedVpiImp::assertOneCheck();
VL_VPI_ERROR_RESET_();
Verilated::runFlushCallbacks();
return 0;
return 0; // Gcc coverage bug // LCOV_EXCL_LINE
}
PLI_INT32 vpi_mcd_flush(PLI_UINT32 mcd) {
+99 -74
View File
@@ -37,8 +37,13 @@
//=========================================================================
// Compiler pragma abstraction
#if defined(__clang__)
# define VL_CLANG_ATTR(attr) __attribute__(( attr ))
#else
# define VL_CLANG_ATTR(attr)
#endif
#ifdef __GNUC__
# define VL_ATTR_ALIGNED(alignment) __attribute__((aligned(alignment)))
# define VL_ATTR_ALWINLINE __attribute__((always_inline)) inline
# define VL_ATTR_NOINLINE __attribute__((noinline))
# define VL_ATTR_COLD __attribute__((cold))
@@ -57,19 +62,6 @@
// All VL_ATTR_WEAK symbols must be marked with the macOS -U linker flag in verilated.mk.in
# define VL_ATTR_WEAK __attribute__((weak))
# endif
# if defined(__clang__)
# define VL_ACQUIRE(...) __attribute__((annotate("ACQUIRE"))) __attribute__((acquire_capability(__VA_ARGS__)))
# define VL_ACQUIRE_SHARED(...) __attribute__((annotate("ACQUIRE_SHARED"))) __attribute__((acquire_shared_capability(__VA_ARGS__)))
# define VL_RELEASE(...) __attribute__((annotate("RELEASE"))) __attribute__((release_capability(__VA_ARGS__)))
# define VL_RELEASE_SHARED(...) __attribute__((annotate("RELEASE_SHARED"))) __attribute__((release_shared_capability(__VA_ARGS__)))
# define VL_TRY_ACQUIRE(...) __attribute__((try_acquire_capability(__VA_ARGS__)))
# define VL_TRY_ACQUIRE_SHARED(...) __attribute__((try_acquire_shared_capability(__VA_ARGS__)))
# define VL_CAPABILITY(x) __attribute__((capability(x)))
# define VL_REQUIRES(x) __attribute__((annotate("REQUIRES"))) __attribute__((requires_capability(x)))
# define VL_GUARDED_BY(x) __attribute__((annotate("GUARDED_BY"))) __attribute__((guarded_by(x)))
# define VL_EXCLUDES(x) __attribute__((annotate("EXCLUDES"))) __attribute__((locks_excluded(x)))
# define VL_SCOPED_CAPABILITY __attribute__((scoped_lockable))
# endif
# define VL_LIKELY(x) __builtin_expect(!!(x), 1) // Prefer over C++20 [[likely]]
# define VL_UNLIKELY(x) __builtin_expect(!!(x), 0) // Prefer over C++20 [[unlikely]]
# define VL_UNREACHABLE __builtin_unreachable() // C++23 std::unreachable()
@@ -77,10 +69,74 @@
# define VL_PREFETCH_RW(p) __builtin_prefetch((p), 1)
#endif
// Defaults for unsupported compiler features
#ifndef VL_ATTR_ALIGNED
# define VL_ATTR_ALIGNED(alignment) ///< Attribute to align structure to byte alignment
// Function acquires a capability/lock (-fthread-safety)
#define VL_ACQUIRE(...) \
VL_CLANG_ATTR(annotate("ACQUIRE")) \
VL_CLANG_ATTR(acquire_capability(__VA_ARGS__))
// Function acquires a shared capability/lock (-fthread-safety)
#define VL_ACQUIRE_SHARED(...) \
VL_CLANG_ATTR(annotate("ACQUIRE_SHARED")) \
VL_CLANG_ATTR(acquire_shared_capability(__VA_ARGS__))
// Function releases a capability/lock (-fthread-safety)
#define VL_RELEASE(...) \
VL_CLANG_ATTR(annotate("RELEASE")) \
VL_CLANG_ATTR(release_capability(__VA_ARGS__))
// Function releases a shared capability/lock (-fthread-safety)
#define VL_RELEASE_SHARED(...) \
VL_CLANG_ATTR(annotate("RELEASE_SHARED")) \
VL_CLANG_ATTR(release_shared_capability(__VA_ARGS__))
// Function returns bool if acquired a capability (-fthread-safety)
#define VL_TRY_ACQUIRE(...) \
VL_CLANG_ATTR(try_acquire_capability(__VA_ARGS__))
// Function returns bool if acquired shared (-fthread-safety)
#define VL_TRY_ACQUIRE_SHARED(...) \
VL_CLANG_ATTR(try_acquire_shared_capability(__VA_ARGS__))
// Function requires a capability inbound (-fthread-safety)
#define VL_CAPABILITY(x) \
VL_CLANG_ATTR(capability(x))
// Name of mutex protecting this variable (-fthread-safety)
#define VL_EXCLUDES(x) \
VL_CLANG_ATTR(annotate("EXCLUDES")) \
VL_CLANG_ATTR(locks_excluded(x))
// Scoped threaded capability/lock (-fthread-safety)
#define VL_SCOPED_CAPABILITY \
VL_CLANG_ATTR(scoped_lockable)
// Annotated function returns reference to the given capability.
// Allowed on: function, method. (-fthread-safety)
#define VL_RETURN_CAPABILITY(x) \
VL_CLANG_ATTR(lock_returned(x))
// Assert that capability is already held.
// Allowed on: function, method. (-fthread-safety)
#define VL_ASSERT_CAPABILITY(x) \
VL_CLANG_ATTR(assert_capability(x))
// Require mutex locks only in code units which work with enabled multi-threading.
#if !defined(VL_MT_DISABLED_CODE_UNIT)
// Function requires not having a capability inbound (-fthread-safety)
# define VL_REQUIRES(x) \
VL_CLANG_ATTR(annotate("REQUIRES")) \
VL_CLANG_ATTR(requires_capability(x))
// Name of capability/lock (-fthread-safety)
# define VL_GUARDED_BY(x) \
VL_CLANG_ATTR(annotate("GUARDED_BY")) \
VL_CLANG_ATTR(guarded_by(x))
// The data that the annotated pointer points to is protected by the given capability.
// The pointer itself is not protected.
// Allowed on: pointer data member. (-fthread-safety)
# define VL_PT_GUARDED_BY(x) \
VL_CLANG_ATTR(annotate("PT_GUARDED_BY")) \
VL_CLANG_ATTR(pt_guarded_by(x))
#else
// Keep annotations for clang_check_attributes
# define VL_REQUIRES(x) \
VL_CLANG_ATTR(annotate("REQUIRES"))
# define VL_GUARDED_BY(x) \
VL_CLANG_ATTR(annotate("GUARDED_BY"))
# define VL_PT_GUARDED_BY(x) \
VL_CLANG_ATTR(annotate("PT_GUARDED_BY"))
#endif
// Defaults for unsupported compiler features
#ifndef VL_ATTR_ALWINLINE
# define VL_ATTR_ALWINLINE ///< Attribute to inline, even when not optimizing
#endif
@@ -111,19 +167,6 @@
#ifndef VL_ATTR_WEAK
# define VL_ATTR_WEAK ///< Attribute that function external that is optionally defined
#endif
#ifndef VL_CAPABILITY
# define VL_ACQUIRE(...) ///< Function acquires a capability/lock (-fthread-safety)
# define VL_ACQUIRE_SHARED(...) ///< Function acquires a shared capability/lock (-fthread-safety)
# define VL_RELEASE(...) ///< Function releases a capability/lock (-fthread-safety)
# define VL_RELEASE_SHARED(...) ///< Function releases a shared capability/lock (-fthread-safety)
# define VL_TRY_ACQUIRE(...) ///< Function returns bool if acquired a capability (-fthread-safety)
# define VL_TRY_ACQUIRE_SHARED(...) ///< Function returns bool if acquired shared (-fthread-safety)
# define VL_REQUIRES(x) ///< Function requires a capability inbound (-fthread-safety)
# define VL_EXCLUDES(x) ///< Function requires not having a capability inbound (-fthread-safety)
# define VL_CAPABILITY(x) ///< Name of capability/lock (-fthread-safety)
# define VL_GUARDED_BY(x) ///< Name of mutex protecting this variable (-fthread-safety)
# define VL_SCOPED_CAPABILITY ///< Scoped threaded capability/lock (-fthread-safety)
#endif
#ifndef VL_LIKELY
# define VL_LIKELY(x) (!!(x)) ///< Return boolean expression that is more often true
# define VL_UNLIKELY(x) (!!(x)) ///< Return boolean expression that is more often false
@@ -142,6 +185,7 @@
#ifndef VL_NO_LEGACY
# define VL_ATTR_ALIGNED(alignment) // Deprecated
# define VL_FUNC __func__ // Deprecated
# define VL_THREAD // Deprecated
# define VL_THREAD_LOCAL thread_local // Deprecated
@@ -149,56 +193,24 @@
#endif
// Comment tag that Function is pure (and thus also VL_MT_SAFE)
#if defined(__clang__)
# define VL_PURE __attribute__((annotate("PURE")))
#else
# define VL_PURE
#endif
#define VL_PURE VL_CLANG_ATTR(annotate("PURE"))
// Comment tag that function is threadsafe
#if defined(__clang__)
# define VL_MT_SAFE __attribute__((annotate("MT_SAFE")))
#else
# define VL_MT_SAFE
#endif
#define VL_MT_SAFE VL_CLANG_ATTR(annotate("MT_SAFE"))
// Comment tag that function is threadsafe, only if
// other threads doesn't change tree topology
#if defined(__clang__)
# define VL_MT_STABLE __attribute__((annotate("MT_STABLE")))
#else
# define VL_MT_STABLE
#endif
#define VL_MT_STABLE VL_CLANG_ATTR(annotate("MT_STABLE"))
// Comment tag that function is threadsafe, only
// during normal operation (post-init)
#if defined(__clang__)
# define VL_MT_SAFE_POSTINIT __attribute__((annotate("MT_SAFE_POSTINIT")))
#else
# define VL_MT_SAFE_POSTINIT
#endif
#define VL_MT_SAFE_POSTINIT VL_CLANG_ATTR(annotate("MT_SAFE_POSTINIT"))
// Attribute that function is clang threadsafe and uses given mutex
#if defined(__clang__)
# define VL_MT_SAFE_EXCLUDES(mutex) __attribute__((annotate("MT_SAFE_EXCLUDES"))) VL_EXCLUDES(mutex)
#else
# define VL_MT_SAFE_EXCLUDES(mutex) VL_EXCLUDES(mutex)
#endif
#define VL_MT_SAFE_EXCLUDES(mutex) VL_CLANG_ATTR(annotate("MT_SAFE_EXCLUDES")) VL_EXCLUDES(mutex)
// Comment tag that function is not threadsafe
#if defined(__clang__)
# define VL_MT_UNSAFE __attribute__((annotate("MT_UNSAFE")))
#else
# define VL_MT_UNSAFE
#endif
#define VL_MT_UNSAFE VL_CLANG_ATTR(annotate("MT_UNSAFE"))
// Comment tag that function is not threadsafe
// protected to make sure single-caller
#if defined(__clang__)
# define VL_MT_UNSAFE_ONE __attribute__((annotate("MT_UNSAFE_ONE")))
#else
# define VL_MT_UNSAFE_ONE
#endif
#define VL_MT_UNSAFE_ONE VL_CLANG_ATTR(annotate("MT_UNSAFE_ONE"))
// Comment tag that function is entry point of parallelization
#if defined(__clang__)
# define VL_MT_START __attribute__((annotate("MT_START")))
#else
# define VL_MT_START
#endif
#define VL_MT_START VL_CLANG_ATTR(annotate("MT_START"))
#ifndef VL_NO_LEGACY
# define VL_ULL(c) (c##ULL) // Add appropriate suffix to 64-bit constant (deprecated)
@@ -324,6 +336,7 @@ extern "C" void __gcov_dump();
// Now that C++ requires these standard types the vl types are deprecated
#include <cstdint>
#include <cinttypes>
#include <cmath>
#ifndef VL_NO_LEGACY
using vluint8_t = uint8_t; ///< 8-bit unsigned type (backward compatibility)
@@ -429,6 +442,11 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
Type(const Type& other) = delete; \
Type& operator=(const Type&) = delete
// Declare a class as unmovable; put after a private:
#define VL_UNMOVABLE(Type) \
Type(Type&& other) = delete; \
Type& operator=(Type&&) = delete
//=========================================================================
// Verilated function size macros
@@ -448,11 +466,14 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
#define VL_SIZEBITS_E (VL_EDATASIZE - 1) ///< Bit mask for bits in a quad
/// Return mask for words with 1's where relevant bits are (0=all bits)
/// Arguments must not have side effects
#define VL_MASK_I(nbits) (((nbits) & VL_SIZEBITS_I) ? ((1U << ((nbits) & VL_SIZEBITS_I)) - 1) : ~0)
/// Return mask for quads with 1's where relevant bits are (0=all bits)
/// Arguments must not have side effects
#define VL_MASK_Q(nbits) \
(((nbits) & VL_SIZEBITS_Q) ? ((1ULL << ((nbits) & VL_SIZEBITS_Q)) - 1ULL) : ~0ULL)
/// Return mask for EData with 1's where relevant bits are (0=all bits)
/// Arguments must not have side effects
#define VL_MASK_E(nbits) VL_MASK_I(nbits)
#define VL_EUL(n) VL_UL(n) // Make constant number EData sized
@@ -474,8 +495,12 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
// #defines, to avoid requiring math.h on all compile runs
#ifdef _MSC_VER
# define VL_TRUNC(n) (((n) < 0) ? std::ceil((n)) : std::floor((n)))
# define VL_ROUND(n) (((n) < 0) ? std::ceil((n)-0.5) : std::floor((n) + 0.5))
static inline double VL_TRUNC(double n) {
return (n < 0) ? std::ceil(n) : std::floor(n);
}
static inline double VL_ROUND(double n) {
return (n < 0) ? std::ceil(n-0.5) : std::floor(n + 0.5);
}
#else
# define VL_TRUNC(n) std::trunc(n)
# define VL_ROUND(n) std::round(n)
@@ -569,8 +594,8 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
//=========================================================================
// Stringify macros
#define VL_STRINGIFY(x) VL_STRINGIFY2(x)
#define VL_STRINGIFY2(x) #x
#define VL_STRINGIFY(...) VL_STRINGIFY2(__VA_ARGS__)
#define VL_STRINGIFY2(...) #__VA_ARGS__
//=========================================================================
// Offset of field in type
+330 -66
View File
@@ -10,16 +10,36 @@ import argparse
import os
import sys
import shlex
from typing import Callable, Iterable, Optional, Union
from typing import Callable, Iterable, Optional, Union, TYPE_CHECKING
import dataclasses
from dataclasses import dataclass
import enum
from enum import Enum
import multiprocessing
import re
import tempfile
import clang.cindex
from clang.cindex import CursorKind, Index, TranslationUnitSaveError, TranslationUnitLoadError
from clang.cindex import (
Index,
TranslationUnitSaveError,
TranslationUnitLoadError,
CompilationDatabase,
)
if not TYPE_CHECKING:
from clang.cindex import CursorKind
else:
# Workaround for missing support for members defined out-of-class in Pylance:
# https://github.com/microsoft/pylance-release/issues/2365#issuecomment-1035803067
class CursorKindMeta(type):
def __getattr__(cls, name: str) -> clang.cindex.CursorKind:
return getattr(clang.cindex.CursorKind, name)
class CursorKind(clang.cindex.CursorKind, metaclass=CursorKindMeta):
pass
def fully_qualified_name(node):
@@ -33,6 +53,26 @@ def fully_qualified_name(node):
return [node.displayname] if node.displayname else []
# Returns True, if `class_node` contains node
# that matches `member` spelling
def check_class_member_exists(class_node, member):
for child in class_node.get_children():
if member.spelling == child.spelling:
return True
return False
# Returns Base class (if found) of `class_node`
# that is of type `base_type`
def get_base_class(class_node, base_type):
for child in class_node.get_children():
if child.kind is CursorKind.CXX_BASE_SPECIFIER:
base_class = child.type
if base_type.spelling == base_class.spelling:
return base_class
return None
@dataclass
class VlAnnotations:
mt_start: bool = False
@@ -40,6 +80,7 @@ class VlAnnotations:
stable_tree: bool = False
mt_safe_postinit: bool = False
mt_unsafe: bool = False
mt_disabled: bool = False
mt_unsafe_one: bool = False
pure: bool = False
guarded: bool = False
@@ -61,7 +102,7 @@ class VlAnnotations:
return self.stable_tree or self.mt_start
def is_mt_unsafe_call(self):
return self.mt_unsafe or self.mt_unsafe_one
return self.mt_unsafe or self.mt_unsafe_one or self.mt_disabled
def is_mt_safe_call(self):
return (not self.is_mt_unsafe_call()
@@ -111,6 +152,8 @@ class VlAnnotations:
result.mt_unsafe = True
elif node.displayname == "MT_UNSAFE_ONE":
result.mt_unsafe_one = True
elif node.displayname == "MT_DISABLED":
result.mt_disabled = True
elif node.displayname == "PURE":
result.pure = True
elif node.displayname in ["ACQUIRE", "ACQUIRE_SHARED"]:
@@ -183,6 +226,19 @@ class FunctionInfo:
def copy(self, /, **changes):
return dataclasses.replace(self, **changes)
@staticmethod
def from_decl_file_line_and_refd_node(file: str, line: int,
refd: clang.cindex.Cursor,
annotations: VlAnnotations):
file = os.path.abspath(file)
refd = refd.canonical
assert refd is not None
name_parts = fully_qualified_name(refd)
usr = refd.get_usr()
ftype = FunctionType.from_node(refd)
return FunctionInfo(name_parts, usr, file, line, annotations, ftype)
@staticmethod
def from_node(node: clang.cindex.Cursor,
refd: Optional[clang.cindex.Cursor] = None,
@@ -208,6 +264,7 @@ class DiagnosticKind(Enum):
NON_PURE_CALL_IN_PURE_CTX = enum.auto()
NON_MT_SAFE_CALL_IN_MT_SAFE_CTX = enum.auto()
NON_STABLE_TREE_CALL_IN_STABLE_TREE_CTX = enum.auto()
MISSING_MT_DISABLED_ANNOTATION = enum.auto()
def __lt__(self, other):
return self.value < other.value
@@ -245,13 +302,109 @@ class CallAnnotationsValidator:
self._index = Index.create()
self._processed_headers: set[str] = set()
# Map key represents translation unit initial defines
# (from command line and source's lines before any include)
self._processed_headers: dict[str, set[str]] = {}
self._external_decls: dict[str, set[tuple[str, int]]] = {}
# Current context
self._main_source_file: str = ""
self._defines: dict[str, str] = {}
self._call_location: Optional[FunctionInfo] = None
self._caller: Optional[FunctionInfo] = None
self._constructor_context: list[clang.cindex.Cursor] = []
self._level: int = 0
self._constructor_context: int = 0
def is_mt_disabled_code_unit(self):
return "VL_MT_DISABLED_CODE_UNIT" in self._defines
def is_constructor_context(self):
return len(self._constructor_context) > 0
# Parses all lines in a form: `#define KEY VALUE` located before any `#include` line.
# The parsing is very simple, there is no support for line breaks, etc.
@staticmethod
def parse_initial_defines(source_file: str) -> dict[str, str]:
defs: dict[str, str] = {}
with open(source_file, "r", encoding="utf-8") as file:
for line in file:
line = line.strip()
match = re.fullmatch(
r"^#\s*(define\s+(\w+)(?:\s+(.*))?|include\s+.*)$", line)
if match:
if match.group(1).startswith("define"):
key = match.group(2)
value = match.groups("1")[2]
defs[key] = value
elif match.group(1).startswith("include"):
break
return defs
@staticmethod
def filter_out_unsupported_compiler_args(
args: list[str]) -> tuple[list[str], dict[str, str]]:
filtered_args = []
defines = {}
args_iter = iter(args)
try:
while arg := next(args_iter):
# Skip positional arguments (input file name).
if not arg.startswith("-") and (arg.endswith(".cpp")
or arg.endswith(".c")
or arg.endswith(".h")):
continue
# Skipped options with separate value argument.
if arg in ["-o", "-T", "-MT", "-MQ", "-MF"
"-L"]:
next(args_iter)
continue
# Skipped options without separate value argument.
if arg == "-c" or arg.startswith("-W") or arg.startswith("-L"):
continue
# Preserved options with separate value argument.
if arg in [
"-x"
"-Xclang", "-I", "-isystem", "-iquote", "-include",
"-include-pch"
]:
filtered_args += [arg, next(args_iter)]
continue
kv_str = None
d_or_u = None
# Preserve define/undefine with separate value argument.
if arg in ["-D", "-U"]:
filtered_args.append(arg)
d_or_u = arg[1]
kv_str = next(args_iter)
filtered_args.append(kv_str)
# Preserve define/undefine without separate value argument.
elif arg[0:2] in ["-D", "-U"]:
filtered_args.append(arg)
kv_str = arg[2:]
d_or_u = arg[1]
# Preserve everything else.
else:
filtered_args.append(arg)
continue
# Keep track of defines for class' internal purposes.
key_value = kv_str.split("=", 1)
key = key_value[0]
val = "1" if len(key_value) == 1 else key_value[1]
if d_or_u == "D":
defines[key] = val
elif d_or_u == "U" and key in defines:
del defines[key]
except StopIteration:
pass
return (filtered_args, defines)
def compile_and_analyze_file(self, source_file: str,
compiler_args: list[str],
@@ -259,18 +412,32 @@ class CallAnnotationsValidator:
filename = os.path.abspath(source_file)
initial_cwd = "."
filtered_args, defines = self.filter_out_unsupported_compiler_args(
compiler_args)
defines.update(self.parse_initial_defines(source_file))
if build_dir:
initial_cwd = os.getcwd()
os.chdir(build_dir)
translation_unit = self._index.parse(filename, compiler_args)
has_errors = False
for diag in translation_unit.diagnostics:
if diag.severity > clang.cindex.Diagnostic.Error:
has_errors = True
if translation_unit and not has_errors:
try:
translation_unit = self._index.parse(filename, filtered_args)
except TranslationUnitLoadError:
translation_unit = None
errors = []
if translation_unit:
for diag in translation_unit.diagnostics:
if diag.severity >= clang.cindex.Diagnostic.Error:
errors.append(str(diag))
if translation_unit and len(errors) == 0:
self._defines = defines
self._main_source_file = filename
self.process_translation_unit(translation_unit)
self._main_source_file = ""
self._defines = {}
else:
print(f"%Error: parsing failed: {filename}", file=sys.stderr)
for error in errors:
print(f" {error}", file=sys.stderr)
if build_dir:
os.chdir(initial_cwd)
@@ -341,7 +508,7 @@ class CallAnnotationsValidator:
return None
refn = find_object_ref(node)
if self._constructor_context and not refn:
if self.is_constructor_context() and not refn:
# we are in constructor and no object reference means
# we are calling local method. It is MT safe
# only if this method is also only calling local methods or
@@ -353,20 +520,44 @@ class CallAnnotationsValidator:
elif refn and refn.kind == CursorKind.MEMBER_REF_EXPR and refn.referenced:
refn = refn.referenced
refna = VlAnnotations.from_nodes_list(refn.get_children())
if refna.guarded or self._constructor_context:
if refna.guarded:
is_mt_safe = True
if self.is_constructor_context() and refn.semantic_parent:
# we are in constructor, so calling local members is MT_SAFE,
# make sure object that we are calling is local to the constructor
constructor_class = self._constructor_context[
-1].semantic_parent
if refn.semantic_parent.spelling == constructor_class.spelling:
if check_class_member_exists(constructor_class, refn):
is_mt_safe = True
else:
# check if this class inherits from some base class
base_class = get_base_class(constructor_class,
refn.semantic_parent)
if base_class:
if check_class_member_exists(
base_class.get_declaration(), refn):
is_mt_safe = True
# variable
elif refn and refn.kind == CursorKind.DECL_REF_EXPR and refn.referenced:
# This is probably a local or an argument.
# Calling methods on local pointers or references is MT-safe,
# but on argument pointers or references is not.
if "*" not in refn.type.spelling and "&" not in refn.type.spelling:
is_mt_safe = True
# local variable
if refn.referenced.kind == CursorKind.VAR_DECL:
is_mt_safe = True
if refn.get_definition():
if refn.referenced.semantic_parent:
if refn.referenced.semantic_parent.kind in [
CursorKind.FUNCTION_DECL, CursorKind.CXX_METHOD
]:
# This is a local or an argument.
# Calling methods on local pointers or references is MT-safe,
# but on argument pointers or references is not.
if "*" not in refn.type.spelling and "&" not in refn.type.spelling:
is_mt_safe = True
# local variable
if refn.referenced.kind == CursorKind.VAR_DECL:
is_mt_safe = True
else:
# Global variable in different translation unit, unsafe
pass
elif refn and refn.kind == CursorKind.CALL_EXPR:
if self._constructor_context:
if self.is_constructor_context():
# call to local function from constructor context
# safe if this function also calling local methods or
# MT-safe methods
@@ -442,18 +633,18 @@ class CallAnnotationsValidator:
# Constructors are OK in MT-safe context
# only if they call local methods or MT-safe functions.
if ctx.is_mt_safe_context() or self._constructor_context:
self._constructor_context += 1
if ctx.is_mt_safe_context() or self.is_constructor_context():
self._constructor_context.append(refd)
self.iterate_children(refd.get_children(),
self.dispatch_node_inside_definition)
self._constructor_context -= 1
self._constructor_context.pop()
# stable tree context
if ctx.is_stabe_tree_context():
self._constructor_context += 1
self._constructor_context.append(refd)
self.iterate_children(refd.get_children(),
self.dispatch_node_inside_definition)
self._constructor_context -= 1
self._constructor_context.pop()
# pure context
if ctx.is_pure_context():
@@ -516,6 +707,18 @@ class CallAnnotationsValidator:
f" from: {node.location.file.name}:{node.location.line}")
return True
def process_function_declaration(self, node: clang.cindex.Cursor):
# Ignore declarations in main .cpp file
if node.location.file.name != self._main_source_file:
children = list(node.get_children())
annotations = VlAnnotations.from_nodes_list(children)
if not annotations.mt_disabled:
self._external_decls.setdefault(node.get_usr(), set()).add(
(str(node.location.file.name), int(node.location.line)))
return self.iterate_children(children, self.dispatch_node)
return self.iterate_children(node.get_children(), self.dispatch_node)
# Definition handling
def dispatch_node_inside_definition(self, node: clang.cindex.Cursor):
@@ -546,6 +749,18 @@ class CallAnnotationsValidator:
assert refd is not None
def_annotations = VlAnnotations.from_nodes_list(node_children)
# Implicitly mark definitions in VL_MT_DISABLED_CODE_UNIT .cpp files as
# VL_MT_DISABLED. Existence of the annotation on declarations in .h
# files is verified below.
# Also sets VL_REQUIRES, as this annotation is added together with
# explicit VL_MT_DISABLED.
if self.is_mt_disabled_code_unit():
if node.location.file.name == self._main_source_file:
annotations.mt_disabled = True
annotations.requires = True
if refd.location.file.name == self._main_source_file:
def_annotations.mt_disabled = True
def_annotations.requires = True
if not (def_annotations.is_empty() or def_annotations == annotations):
# Use definition's annotations for the diagnostic
@@ -558,12 +773,26 @@ class CallAnnotationsValidator:
DiagnosticKind.ANNOTATIONS_DEF_DECL_MISMATCH)
# Use concatenation of definition and declaration annotations
# for callees validation.
# for calls validation.
self._caller = FunctionInfo.from_node(node, refd,
def_annotations | annotations)
prev_call_location = self._call_location
self._call_location = self._caller
if self.is_mt_disabled_code_unit():
# Report declarations of this functions that don't have MT_DISABLED annotation
# and are located in headers.
if node.location.file.name == self._main_source_file:
usr = node.get_usr()
declarations = self._external_decls.get(usr, set())
for file, line in declarations:
self.emit_diagnostic(
FunctionInfo.from_decl_file_line_and_refd_node(
file, line, refd, def_annotations),
DiagnosticKind.MISSING_MT_DISABLED_ANNOTATION)
if declarations:
del self._external_decls[usr]
self.iterate_children(node_children,
self.dispatch_node_inside_definition)
@@ -575,34 +804,36 @@ class CallAnnotationsValidator:
# Nodes not located inside definition
def dispatch_node(self, node: clang.cindex.Cursor):
if node.is_definition() and node.kind in [
if node.kind in [
CursorKind.CXX_METHOD, CursorKind.FUNCTION_DECL,
CursorKind.CONSTRUCTOR, CursorKind.CONVERSION_FUNCTION
]:
return self.process_function_definition(node)
if node.is_definition() and node.kind in [
CursorKind.NAMESPACE, CursorKind.STRUCT_DECL,
CursorKind.UNION_DECL, CursorKind.CLASS_DECL
]:
return self.iterate_children(node.get_children(),
self.dispatch_node)
if node.is_definition():
return self.process_function_definition(node)
# else:
return self.process_function_declaration(node)
return self.iterate_children(node.get_children(), self.dispatch_node)
def process_translation_unit(
self, translation_unit: clang.cindex.TranslationUnit):
self._level += 1
kv_defines = sorted([f"{k}={v}" for k, v in self._defines.items()])
concat_defines = '\n'.join(kv_defines)
# List of headers already processed in a TU with specified set of defines.
tu_processed_headers = self._processed_headers.setdefault(
concat_defines, set())
for child in translation_unit.cursor.get_children():
if self._is_ignored_top_level(child):
continue
if self._processed_headers:
if tu_processed_headers:
filename = os.path.abspath(child.location.file.name)
if filename in self._processed_headers:
if filename in tu_processed_headers:
continue
self.dispatch_node(child)
self._level -= 1
self._processed_headers.update([
tu_processed_headers.update([
os.path.abspath(str(hdr.source))
for hdr in translation_unit.get_includes()
])
@@ -664,34 +895,40 @@ def get_filter_funcs(verilator_root: str):
def precompile_header(compile_command: CompileCommand, tmp_dir: str) -> str:
initial_cwd = os.getcwd()
errors = []
try:
initial_cwd = os.getcwd()
os.chdir(compile_command.directory)
index = Index.create()
translation_unit = index.parse(compile_command.filename,
compile_command.args)
for diag in translation_unit.diagnostics:
if diag.severity > clang.cindex.Diagnostic.Error:
pch_file = None
break
else:
if diag.severity >= clang.cindex.Diagnostic.Error:
errors.append(str(diag))
if len(errors) == 0:
pch_file = os.path.join(
tmp_dir,
f"{compile_command.refid:02}_{os.path.basename(compile_command.filename)}.pch"
)
translation_unit.save(pch_file)
if pch_file:
return pch_file
except (TranslationUnitSaveError, TranslationUnitLoadError,
OSError) as exception:
print(f"%Warning: {exception}", file=sys.stderr)
finally:
os.chdir(initial_cwd)
if pch_file:
return pch_file
except (TranslationUnitSaveError, TranslationUnitLoadError, OSError):
pass
print(
f"%Warning: Precompiling failed, skipping: {compile_command.filename}")
f"%Warning: Precompilation failed, skipping: {compile_command.filename}",
file=sys.stderr)
for error in errors:
print(f" {error}", file=sys.stderr)
return ""
@@ -714,7 +951,7 @@ def run_analysis(ccl: Iterable[CompileCommand], pccl: Iterable[CompileCommand],
is_ignored_def, is_ignored_call)
for compile_command in ccl:
cav.compile_and_analyze_file(compile_command.filename,
compile_command.args + extra_args,
extra_args + compile_command.args,
compile_command.directory)
@@ -834,6 +1071,10 @@ class TopDownSummaryPrinter():
name += "is pure but calls non-pure function(s)"
elif func.reason == DiagnosticKind.NON_STABLE_TREE_CALL_IN_STABLE_TREE_CTX:
name += "is stable_tree but calls non-stable_tree or non-mtsafe"
elif func.reason == DiagnosticKind.MISSING_MT_DISABLED_ANNOTATION:
name += ("defined in a file marked as " +
"VL_MT_DISABLED_CODE_UNIT has declaration(s) " +
"without VL_MT_DISABLED annotation")
else:
name += "for unknown reason (please add description)"
@@ -900,10 +1141,15 @@ def main():
type=int,
default=0,
help="Number of parallel jobs to use.")
parser.add_argument(
"--compile-commands-dir",
type=str,
default=None,
help="Path to directory containing compile_commands.json.")
parser.add_argument("--cxxflags",
type=str,
default=None,
help="Flags passed to clang++.")
help="Extra flags passed to clang++.")
parser.add_argument(
"--compilation-root",
type=str,
@@ -928,34 +1174,52 @@ def main():
cmdline.compilation_root = cmdline.verilator_root
verilator_root = os.path.abspath(cmdline.verilator_root)
compilation_root = os.path.abspath(cmdline.compilation_root)
default_compilation_root = os.path.abspath(cmdline.compilation_root)
compdb: Optional[CompilationDatabase] = None
if cmdline.compile_commands_dir:
compdb = CompilationDatabase.fromDirectory(
cmdline.compile_commands_dir)
default_cxx_flags = [
f"-I{verilator_root}/src",
f"-I{verilator_root}/include",
f"-I{verilator_root}/src/obj_opt",
"-fcoroutines-ts",
]
if cmdline.cxxflags is not None:
cxxflags = shlex.split(cmdline.cxxflags)
common_cxxflags = shlex.split(cmdline.cxxflags)
else:
cxxflags = default_cxx_flags
common_cxxflags = []
precompile_commands_list = []
if cmdline.precompile:
hdr_cxxflags = ['-xc++-header'] + cxxflags
hdr_cxxflags = ['-xc++-header'] + common_cxxflags
for refid, file in enumerate(cmdline.precompile):
filename = os.path.abspath(file)
compile_command = CompileCommand(refid, filename, hdr_cxxflags,
compilation_root)
default_compilation_root)
precompile_commands_list.append(compile_command)
compile_commands_list = []
for refid, file in enumerate(cmdline.file):
filename = os.path.abspath(file)
compile_command = CompileCommand(refid, filename, cxxflags,
compilation_root)
root = default_compilation_root
cxxflags = []
if compdb:
entry = compdb.getCompileCommands(filename)
entry_list = list(entry)
# Compilation database can contain multiple entries for single file,
# e.g. when it has been updated by appending new entries.
# Use last entry for the file, if it exists, as it is the newest one.
if len(entry_list) > 0:
last_entry = entry_list[-1]
root = last_entry.directory
entry_args = list(last_entry.arguments)
# First argument in compile_commands.json arguments list is
# compiler executable name/path. CIndex (libclang) always
# implicitly prepends executable name, so it shouldn't be passed
# here.
cxxflags = common_cxxflags + entry_args[1:]
else:
cxxflags = common_cxxflags[:]
compile_command = CompileCommand(refid, filename, cxxflags, root)
compile_commands_list.append(compile_command)
summary_printer = TopDownSummaryPrinter()
+1 -1
View File
@@ -1,6 +1,6 @@
*~
*.old
config_build.h
config_package.h
Makefile
Makefile_obj
.objcache*
+7 -2
View File
@@ -82,6 +82,7 @@ set(HEADERS
V3File.h
V3FileLine.h
V3Force.h
V3Fork.h
V3FunctionTraits.h
V3Gate.h
V3Global.h
@@ -150,6 +151,7 @@ set(HEADERS
V3Table.h
V3Task.h
V3ThreadPool.h
V3ThreadSafety.h
V3Timing.h
V3Trace.h
V3TraceDecl.h
@@ -227,6 +229,7 @@ set(COMMON_SOURCES
V3File.cpp
V3FileLine.cpp
V3Force.cpp
V3Fork.cpp
V3Gate.cpp
V3Global.cpp
V3Graph.cpp
@@ -322,7 +325,7 @@ file(TO_NATIVE_PATH ${srcdir}/../bin/verilator_includer VERILATOR_INCLUDER)
file(TO_NATIVE_PATH ${srcdir}/vlcovgen VLCOVGEN)
file(TO_NATIVE_PATH ${srcdir}/config_rev CONFIG_REV)
configure_file(config_build.h.in config_build.h @ONLY)
configure_file(config_package.h.in config_package.h @ONLY)
add_custom_command(
OUTPUT V3Ast__gen_forward_class_decls.h V3Dfg__gen_forward_class_decls.h
@@ -414,7 +417,7 @@ foreach(astgen_name ${ASTGENERATED_NAMES})
MAIN_DEPENDENCY ${astgen_name}.cpp
DEPENDS ${ASTGEN} V3Ast.h
COMMAND ${PYTHON3} ARGS
${ASTGEN} -I"${srcdir}" --astdef V3AstNodeDType.h --astdef V3AstNodeExpr.h --astdef V3AstNodeOther.h --dfgdef V3DfgVertices.h ${astgen_name}.cpp
${ASTGEN} -I "${srcdir}" --astdef V3AstNodeDType.h --astdef V3AstNodeExpr.h --astdef V3AstNodeOther.h --dfgdef V3DfgVertices.h ${astgen_name}.cpp
)
list(APPEND GENERATED_FILES ${astgen_name}__gen.cpp)
endforeach()
@@ -488,11 +491,13 @@ target_include_directories(${verilator}
)
if (WIN32)
target_compile_options(${verilator} PRIVATE /bigobj)
target_compile_definitions(${verilator} PRIVATE
YY_NO_UNISTD_H
)
target_include_directories(${verilator} PRIVATE ../platform/win32)
target_link_libraries(${verilator} PRIVATE bcrypt psapi)
target_link_options(${verilator} PRIVATE /STACK:10000000)
endif()
install(TARGETS ${verilator})
+24 -5
View File
@@ -18,14 +18,13 @@
#### Start of system configuration section. ####
srcdir = @srcdir@
PYTHON3 = @PYTHON3@
EXEEXT = @EXEEXT@
PYTHON3 = @PYTHON3@
# VPATH only does sources; fix install_test's not creating ../bin
vpath %.h @srcdir@
#### End of system configuration section. ####
default: dbg opt
debug: dbg
optimize: opt
@@ -36,6 +35,26 @@ endif
UNDER_GIT = $(wildcard ${srcdir}/../.git/logs/HEAD)
ifeq (,$(wildcard obj_dbg/bear.o))
ifneq (, $(shell which bear 2>/dev/null))
BEAR := $(shell which bear)
ifeq (, $(shell $(BEAR) --output obj_dbg/comptest.json -- true))
$(shell which bear 2>/dev/null >obj_dbg/bear.o)
else
# unsupported version
BEAR :=
endif
endif
else
BEAR := $(shell cat obj_dbg/bear.o)
endif
ifneq ($(BEAR),)
BEAR_OBJ_OPT := $(BEAR) --append --output obj_dbg/compile_commands.json --
else
BEAR_OBJ_OPT :=
endif
#*********************************************************************
obj_opt:
@@ -62,8 +81,8 @@ endif
dbg: ../bin/verilator_bin_dbg$(EXEEXT) ../bin/verilator_coverage_bin_dbg$(EXEEXT)
../bin/verilator_bin_dbg$(EXEEXT): obj_dbg ../bin prefiles
$(MAKE) -C obj_dbg -j 1 TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj serial
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj
$(BEAR_OBJ_OPT) $(MAKE) -C obj_dbg -j 1 TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj serial
$(BEAR_OBJ_OPT) $(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj
../bin/verilator_coverage_bin_dbg$(EXEEXT): obj_dbg ../bin prefiles
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj serial_vlcov
@@ -95,7 +114,7 @@ clean mostlyclean distclean maintainer-clean::
-rm -f .objcache*
distclean maintainer-clean::
-rm -f Makefile Makefile_obj config_build.h
-rm -f Makefile Makefile_obj config_package.h
maintainer-clean::
-rm -f config_rev.h
+2
View File
@@ -205,6 +205,7 @@ RAW_OBJS = \
V3File.o \
V3FileLine.o \
V3Force.o \
V3Fork.o \
V3Gate.o \
V3Global.o \
V3Graph.o \
@@ -288,6 +289,7 @@ NON_STANDALONE_HEADERS = \
V3AstNodeExpr.h \
V3AstNodeOther.h \
V3DfgVertices.h \
V3ThreadPool.h \
V3WidthCommit.h \
AST_DEFS := \
+7 -3
View File
@@ -47,6 +47,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
// Extend V3GraphVertex class for use in latch detection graph
class LatchDetectGraphVertex final : public V3GraphVertex {
VL_RTTI_IMPL(LatchDetectGraphVertex, V3GraphVertex)
public:
enum VertexType : uint8_t { VT_BLOCK, VT_BRANCH, VT_OUTPUT };
@@ -183,7 +184,7 @@ public:
<< " (not all control paths of combinational always assign a value)\n"
<< nodep->warnMore()
<< "... Suggest use of always_latch for intentional latches");
if (dumpGraph() >= 9) dumpDotFilePrefixed("latch_" + vrp->name());
if (dumpGraphLevel() >= 9) dumpDotFilePrefixed("latch_" + vrp->name());
}
vertp->user(false); // Clear again (see above)
vrp->varp()->isLatched(latch_detected);
@@ -320,7 +321,8 @@ private:
// VISITORS
void visit(AstVarRef* nodep) override {
const AstVar* const varp = nodep->varp();
if (nodep->access().isWriteOrRW() && varp->isSignal() && !varp->isUsedLoopIdx()) {
if (nodep->access().isWriteOrRW() && varp->isSignal() && !varp->isUsedLoopIdx()
&& !varp->isFuncLocalSticky()) {
m_graph.addAssignment(nodep);
}
}
@@ -333,6 +335,8 @@ private:
m_graph.addPathVertex(branchp, "ELSE");
iterateAndNextConstNull(nodep->elsesp());
m_graph.currentp(parentp);
} else {
iterateChildrenConst(nodep);
}
}
//--------------------
@@ -652,5 +656,5 @@ public:
void V3Active::activeAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ActiveVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("active", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("active", 0, dumpTreeLevel() >= 3);
}
+1 -1
View File
@@ -145,5 +145,5 @@ public:
void V3ActiveTop::activeTopAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ActiveTopVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("activetop", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("activetop", 0, dumpTreeLevel() >= 3);
}
+4 -3
View File
@@ -20,6 +20,7 @@
#include "V3Assert.h"
#include "V3Ast.h"
#include "V3Error.h"
#include "V3Global.h"
#include "V3Stats.h"
@@ -156,7 +157,7 @@ private:
sentreep->unlinkFrBack();
if (m_procedurep) {
// To support this need queue of asserts to activate
nodep->v3error("Unsupported: Procedural concurent assertion with"
nodep->v3error("Unsupported: Procedural concurrent assertion with"
" clocking event inside always (IEEE 1800-2917 16.14.6)");
}
}
@@ -255,7 +256,7 @@ private:
ifp = nextifp;
} while (ifp);
AstNode* const newifp = nodep->cloneTree(false);
AstIf* const newifp = nodep->cloneTree(false);
const bool allow_none = nodep->unique0Pragma();
// Empty case means no property
@@ -544,5 +545,5 @@ public:
void V3Assert::assertAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ AssertVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("assert", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("assert", 0, dumpTreeLevel() >= 3);
}
-1
View File
@@ -21,7 +21,6 @@
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3Error.h"
//============================================================================
+1 -1
View File
@@ -496,5 +496,5 @@ public:
void V3AssertPre::assertPreAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ AssertPreVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("assertpre", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("assertpre", 0, dumpTreeLevel() >= 3);
}
+89 -23
View File
@@ -55,12 +55,28 @@ bool VNUser5InUse::s_userBusy = false;
int AstNodeDType::s_uniqueNum = 0;
//######################################################################
// V3AstType
// VNType
const VNTypeInfo VNType::typeInfoTable[] = {
#include "V3Ast__gen_type_info.h" // From ./astgen
};
std::ostream& operator<<(std::ostream& os, VNType rhs);
//######################################################################
// Creators
// VSelfPointerText
const std::shared_ptr<const string> VSelfPointerText::s_emptyp = std::make_shared<string>("");
const std::shared_ptr<const string> VSelfPointerText::s_thisp = std::make_shared<string>("this");
string VSelfPointerText::protect(bool useSelfForThis, bool protect) const {
const string& sp
= useSelfForThis ? VString::replaceWord(asString(), "this", "vlSelf") : asString();
return VIdProtect::protectWordsIf(sp, protect);
}
//######################################################################
// AstNode
AstNode::AstNode(VNType t, FileLine* fl)
: m_type{t}
@@ -125,9 +141,7 @@ string AstNode::encodeNumber(int64_t num) {
}
}
string AstNode::nameProtect() const VL_MT_STABLE {
return VIdProtect::protectIf(name(), protect());
}
string AstNode::nameProtect() const { return VIdProtect::protectIf(name(), protect()); }
string AstNode::origNameProtect() const { return VIdProtect::protectIf(origName(), protect()); }
string AstNode::shortName() const {
@@ -285,7 +299,7 @@ string AstNode::vpiName(const string& namein) {
return pretty;
}
string AstNode::prettyTypeName() const VL_MT_STABLE {
string AstNode::prettyTypeName() const {
if (name() == "") return typeName();
return std::string{typeName()} + " '" + prettyName() + "'";
}
@@ -1081,10 +1095,12 @@ bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ig
// private: Return true if the two trees are identical
if (!node1p && !node2p) return true;
if (!node1p || !node2p) return false;
if (node1p->type() != node2p->type() || node1p->dtypep() != node2p->dtypep()
|| !node1p->same(node2p) || (gateOnly && !node1p->isGateOptimizable())) {
return false;
}
if (node1p->type() != node2p->type()) return false;
UASSERT_OBJ(
(!node1p->dtypep() && !node2p->dtypep()) || (node1p->dtypep() && node2p->dtypep()), node1p,
"Comparison of a node with dtypep() with a node without dtypep()\n-node2=" << node2p);
if (node1p->dtypep() && !node1p->dtypep()->similarDType(node2p->dtypep())) return false;
if (!node1p->same(node2p) || (gateOnly && !node1p->isGateOptimizable())) return false;
return (sameTreeIter(node1p->m_op1p, node2p->m_op1p, false, gateOnly)
&& sameTreeIter(node1p->m_op2p, node2p->m_op2p, false, gateOnly)
&& sameTreeIter(node1p->m_op3p, node2p->m_op3p, false, gateOnly)
@@ -1098,9 +1114,57 @@ bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ig
void AstNode::checkTreeIter(const AstNode* prevBackp) const VL_MT_STABLE {
// private: Check a tree and children
UASSERT_OBJ(prevBackp == this->backp(), this, "Back node inconsistent");
switch (this->type()) {
#include "V3Ast__gen_op_checks.h"
default: VL_UNREACHABLE; // LCOV_EXCL_LINE
const VNTypeInfo& typeInfo = *type().typeInfo();
for (int i = 1; i <= 4; i++) {
AstNode* nodep = nullptr;
switch (i) {
case 1: nodep = op1p(); break;
case 2: nodep = op2p(); break;
case 3: nodep = op3p(); break;
case 4: nodep = op4p(); break;
default: this->v3fatalSrc("Bad case"); break;
}
const char* opName = typeInfo.m_opNamep[i - 1];
switch (typeInfo.m_opType[i - 1]) {
case VNTypeInfo::OP_UNUSED:
UASSERT_OBJ(!nodep, this, typeInfo.m_namep << " must not use " << opName << "()");
break;
case VNTypeInfo::OP_USED:
UASSERT_OBJ(nodep, this,
typeInfo.m_namep << " must have non nullptr " << opName << "()");
UASSERT_OBJ(!nodep->nextp(), this,
typeInfo.m_namep << "::" << opName
<< "() cannot have a non nullptr nextp()");
nodep->checkTreeIter(this);
break;
case VNTypeInfo::OP_LIST:
if (const AstNode* const headp = nodep) {
const AstNode* backp = this;
const AstNode* tailp = headp;
const AstNode* opp = headp;
do {
opp->checkTreeIter(backp);
UASSERT_OBJ(opp == headp || !opp->nextp() || !opp->m_headtailp, opp,
"Headtailp should be null in middle of lists");
backp = tailp = opp;
opp = opp->nextp();
} while (opp);
UASSERT_OBJ(headp->m_headtailp == tailp, headp,
"Tail in headtailp is inconsistent");
UASSERT_OBJ(tailp->m_headtailp == headp, tailp,
"Head in headtailp is inconsistent");
}
break;
case VNTypeInfo::OP_OPTIONAL:
if (nodep) {
UASSERT_OBJ(!nodep->nextp(), this,
typeInfo.m_namep << "::" << opName
<< "() cannot have a non-nullptr nextp()");
nodep->checkTreeIter(this);
}
break;
default: this->v3fatalSrc("Bad case"); break;
}
}
}
@@ -1217,7 +1281,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo
if (logsp->fail()) v3fatal("Can't write " << filename);
*logsp << "Verilator Tree Dump (format 0x3900) from <e" << std::dec << editCountLast();
*logsp << "> to <e" << std::dec << editCountGbl() << ">\n";
if (editCountGbl() == editCountLast() && ::dumpTree() < 9) {
if (editCountGbl() == editCountLast() && ::dumpTreeLevel() < 9) {
*logsp << '\n';
*logsp << "No changes since last dump!\n";
} else {
@@ -1227,7 +1291,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo
}
}
if (doDump && v3Global.opt.debugEmitV()) V3EmitV::debugEmitV(filename + ".v");
if (doCheck && (v3Global.opt.debugCheck() || ::dumpTree())) {
if (doCheck && (v3Global.opt.debugCheck() || ::dumpTreeLevel())) {
// Error check
checkTree();
// Broken isn't part of check tree because it can munge iterp's
@@ -1289,12 +1353,6 @@ bool AstNode::isTreePureRecurse() const {
return true;
}
void AstNode::v3errorEndFatal(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) {
v3errorEnd(str);
assert(0); // LCOV_EXCL_LINE
VL_UNREACHABLE;
}
string AstNode::instanceStr() const {
// Max iterations before giving up on location search,
// in case we have some circular reference bug.
@@ -1319,9 +1377,9 @@ string AstNode::instanceStr() const {
return "";
}
void AstNode::v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) {
void AstNode::v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
if (!m_fileline) {
V3Error::s().v3errorEnd(str, instanceStr());
V3Error::v3errorEnd(str, instanceStr());
} else {
std::ostringstream nsstr;
nsstr << str.str();
@@ -1338,6 +1396,11 @@ void AstNode::v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s()
nsstr, m_fileline->warnIsOff(V3Error::s().errorCode()) ? "" : instanceStr());
}
}
void AstNode::v3errorEndFatal(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
v3errorEnd(str);
assert(0); // LCOV_EXCL_LINE
VL_UNREACHABLE;
}
//======================================================================
// Data type conversion
@@ -1406,6 +1469,9 @@ AstNodeDType* AstNode::findQueueIndexDType() const {
AstNodeDType* AstNode::findVoidDType() const {
return v3Global.rootp()->typeTablep()->findVoidDType(fileline());
}
AstNodeDType* AstNode::findStreamDType() const {
return v3Global.rootp()->typeTablep()->findStreamDType(fileline());
}
//######################################################################
// VNDeleter
+192 -107
View File
@@ -82,12 +82,36 @@ using MTaskIdSet = std::set<int>; // Set of mtaskIds for Var sorting
// Ast<nodetypename>*' is returned.
#define VN_AS(nodep, nodetypename) (AstNode::privateAs<Ast##nodetypename, decltype(nodep)>(nodep))
// Same as VN_AS, but only checks the type in debug builds. Type checking is less critical in node
// child getters (the strongly-typed functions that wrap op*p pointers). This is because the op*p
// pointers are usually populated by code that already asserts the correct type. Having fewer type
// assertions yields better performance in release builds.
#ifdef VL_DEBUG
#define VN_DBG_AS(nodep, nodetypename) VN_AS(nodep, nodetypename)
#else
#define VN_DBG_AS(nodep, nodetypename) \
(AstNode::unsafePrivateAs<Ast##nodetypename, decltype(nodep)>(nodep))
#endif
// (V)erilator (N)ode deleted: Pointer to deleted AstNode (for assertions only)
#define VN_DELETED(nodep) VL_UNLIKELY((uint64_t)(nodep) == 0x1)
//######################################################################
struct VNTypeInfo {
const char* m_namep;
enum uint8_t {
OP_UNUSED,
OP_USED,
OP_LIST,
OP_OPTIONAL,
} m_opType[4];
const char* m_opNamep[4];
};
class VNType final {
static const VNTypeInfo typeInfoTable[];
public:
#include "V3Ast__gen_type_enum.h" // From ./astgen
// Above include has:
@@ -100,6 +124,7 @@ public:
constexpr VNType(en _e) VL_MT_SAFE : m_e{_e} {}
explicit VNType(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
const VNTypeInfo* typeInfo() const VL_MT_SAFE { return &typeInfoTable[m_e]; }
constexpr operator en() const VL_MT_SAFE { return m_e; }
};
constexpr bool operator==(const VNType& lhs, const VNType& rhs) VL_PURE {
@@ -461,6 +486,7 @@ public:
TRIGGER_SCHEDULER,
DYNAMIC_TRIGGER_SCHEDULER,
FORK_SYNC,
PROCESS_REFERENCE,
// Unsigned and two state; fundamental types
UINT32,
UINT64,
@@ -493,6 +519,7 @@ public:
"VlTriggerScheduler",
"VlDynamicTriggerScheduler",
"VlFork",
"VlProcessRef",
"IData",
"QData",
"LOGIC_IMPLICIT",
@@ -500,13 +527,20 @@ public:
return names[m_e];
}
const char* dpiType() const {
static const char* const names[]
= {"%E-unk", "svBit", "char", "void*", "char",
"int", "%E-integer", "svLogic", "long long", "double",
"short", "%E-time", "const char*", "%E-untyped", "dpiScope",
"const char*", "%E-mtaskstate", "%E-triggervec", "%E-dly-sched", "%E-trig-sched",
"%E-dyn-sched", "%E-fork", "IData", "QData", "%E-logic-implct",
" MAX"};
static const char* const names[] = {"%E-unk", "svBit",
"char", "void*",
"char", "int",
"%E-integer", "svLogic",
"long long", "double",
"short", "%E-time",
"const char*", "%E-untyped",
"dpiScope", "const char*",
"%E-mtaskstate", "%E-triggervec",
"%E-dly-sched", "%E-trig-sched",
"%E-dyn-sched", "%E-fork",
"%E-proc-ref", "IData",
"QData", "%E-logic-implct",
" MAX"};
return names[m_e];
}
static void selfTest() {
@@ -545,6 +579,7 @@ public:
case TRIGGER_SCHEDULER: return 0; // opaque
case DYNAMIC_TRIGGER_SCHEDULER: return 0; // opaque
case FORK_SYNC: return 0; // opaque
case PROCESS_REFERENCE: return 0; // opaque
case UINT32: return 32;
case UINT64: return 64;
default: return 0;
@@ -584,7 +619,7 @@ public:
return (m_e == EVENT || m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR
|| m_e == MTASKSTATE || m_e == TRIGGERVEC || m_e == DELAY_SCHEDULER
|| m_e == TRIGGER_SCHEDULER || m_e == DYNAMIC_TRIGGER_SCHEDULER || m_e == FORK_SYNC
|| m_e == DOUBLE || m_e == UNTYPED);
|| m_e == PROCESS_REFERENCE || m_e == DOUBLE || m_e == UNTYPED);
}
bool isDouble() const VL_MT_SAFE { return m_e == DOUBLE; }
bool isEvent() const { return m_e == EVENT; }
@@ -781,6 +816,7 @@ public:
"BLOCKTEMP", "MODULETEMP", "STMTTEMP", "XTEMP", "IFACEREF", "MEMBER"};
return names[m_e];
}
bool isParam() const { return m_e == GPARAM || m_e == LPARAM; }
bool isSignal() const {
return (m_e == WIRE || m_e == WREAL || m_e == IMPLICITWIRE || m_e == TRIWIRE || m_e == TRI0
|| m_e == TRI1 || m_e == PORT || m_e == SUPPLY0 || m_e == SUPPLY1 || m_e == VAR);
@@ -1147,30 +1183,34 @@ inline std::ostream& operator<<(std::ostream& os, const VNumRange& rhs) {
class VUseType final {
public:
enum en : uint8_t {
IMP_INCLUDE, // Implementation (.cpp) needs an include
INT_INCLUDE, // Interface (.h) needs an include
IMP_FWD_CLASS, // Implementation (.cpp) needs a forward class declaration
INT_FWD_CLASS, // Interface (.h) needs a forward class declaration
// Enum values are compared with <, so order matters
INT_FWD_CLASS = 1 << 0, // Interface (.h) needs a forward class declaration
INT_INCLUDE = 1 << 1, // Interface (.h) needs an include
};
enum en m_e;
VUseType()
: m_e{IMP_FWD_CLASS} {}
: m_e{INT_FWD_CLASS} {}
// cppcheck-suppress noExplicitConstructor
constexpr VUseType(en _e)
: m_e{_e} {}
explicit VUseType(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
bool isInclude() const { return m_e == IMP_INCLUDE || m_e == INT_INCLUDE; }
bool isFwdClass() const { return m_e == IMP_FWD_CLASS || m_e == INT_FWD_CLASS; }
constexpr operator en() const { return m_e; }
bool containsAny(VUseType other) { return m_e & other.m_e; }
const char* ascii() const {
static const char* const names[] = {"IMP_INC", "INT_INC", "IMP_FWD", "INT_FWD"};
return names[m_e];
static const char* const names[] = {"INT_FWD", "INT_INC", "INT_FWD_INC"};
return names[m_e - 1];
}
};
constexpr bool operator==(const VUseType& lhs, const VUseType& rhs) { return lhs.m_e == rhs.m_e; }
constexpr bool operator==(const VUseType& lhs, VUseType::en rhs) { return lhs.m_e == rhs; }
constexpr bool operator==(VUseType::en lhs, const VUseType& rhs) { return lhs == rhs.m_e; }
constexpr VUseType::en operator|(VUseType::en lhs, VUseType::en rhs) {
return VUseType::en((uint8_t)lhs | (uint8_t)rhs);
}
constexpr VUseType::en operator&(VUseType::en lhs, VUseType::en rhs) {
return VUseType::en((uint8_t)lhs & (uint8_t)rhs);
}
inline std::ostream& operator<<(std::ostream& os, const VUseType& rhs) {
return os << rhs.ascii();
}
@@ -1211,6 +1251,44 @@ public:
~VBasicTypeKey() = default;
};
// ######################################################################
// VSelfPointerText - Represents text to be emitted before a given var reference, call, etc. to
// serve as a pointer to a 'self' object. For example, it could be empty (no self pointer), or the
// string 'this', or 'vlSymsp->...'
class VSelfPointerText final {
private:
// STATIC MEMBERS
// Keep these in shared pointers to avoid branching for special cases
static const std::shared_ptr<const string> s_emptyp; // Holds ""
static const std::shared_ptr<const string> s_thisp; // Holds "this"
// MEMBERS
std::shared_ptr<const string> m_strp;
public:
// CONSTRUCTORS
class Empty {}; // for creator type-overload selection
VSelfPointerText(Empty)
: m_strp{s_emptyp} {}
class This {}; // for creator type-overload selection
VSelfPointerText(This)
: m_strp{s_thisp} {}
VSelfPointerText(This, const string& field)
: m_strp{std::make_shared<const string>("this->" + field)} {}
class VlSyms {}; // for creator type-overload selection
VSelfPointerText(VlSyms, const string& field)
: m_strp{std::make_shared<const string>("(&vlSymsp->" + field + ')')} {}
// METHODS
bool isEmpty() const { return m_strp == s_emptyp; }
bool isVlSym() const { return m_strp->find("vlSymsp") != string::npos; }
bool hasThis() const { return m_strp == s_thisp || VString::startsWith(*m_strp, "this"); }
string protect(bool useSelfForThis, bool protect) const;
const std::string& asString() const { return *m_strp; }
bool operator==(const VSelfPointerText& other) const { return *m_strp == *other.m_strp; }
};
//######################################################################
// AstNUser - Generic base class for AST User nodes.
// - Also used to allow parameter passing up/down iterate calls
@@ -1293,55 +1371,55 @@ protected:
class VNUser1InUse final : VNUserInUseBase {
protected:
friend class AstNode;
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
static bool s_userBusy; // Count is in use
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
static bool s_userBusy; // Count is in use
public:
VNUser1InUse() { allocate(1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
~VNUser1InUse() { free (1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
VNUser1InUse() { allocate(1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
~VNUser1InUse() { free (1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
static void clear() { clearcnt(1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
static void check() { checkcnt(1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
};
class VNUser2InUse final : VNUserInUseBase {
protected:
friend class AstNode;
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
static bool s_userBusy; // Count is in use
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
static bool s_userBusy; // Count is in use
public:
VNUser2InUse() { allocate(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
~VNUser2InUse() { free (2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
VNUser2InUse() { allocate(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
~VNUser2InUse() { free (2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
static void clear() { clearcnt(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
static void check() { checkcnt(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
};
class VNUser3InUse final : VNUserInUseBase {
protected:
friend class AstNode;
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
static bool s_userBusy; // Count is in use
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
static bool s_userBusy; // Count is in use
public:
VNUser3InUse() { allocate(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
~VNUser3InUse() { free (3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
VNUser3InUse() { allocate(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
~VNUser3InUse() { free (3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
static void clear() { clearcnt(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
static void check() { checkcnt(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
};
class VNUser4InUse final : VNUserInUseBase {
protected:
friend class AstNode;
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
static bool s_userBusy; // Count is in use
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
static bool s_userBusy; // Count is in use
public:
VNUser4InUse() { allocate(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
~VNUser4InUse() { free (4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
VNUser4InUse() { allocate(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
~VNUser4InUse() { free (4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
static void clear() { clearcnt(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
static void check() { checkcnt(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
};
class VNUser5InUse final : VNUserInUseBase {
protected:
friend class AstNode;
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
static bool s_userBusy; // Count is in use
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
static bool s_userBusy; // Count is in use
public:
VNUser5InUse() { allocate(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
~VNUser5InUse() { free (5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
VNUser5InUse() { allocate(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
~VNUser5InUse() { free (5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
static void clear() { clearcnt(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
static void check() { checkcnt(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
};
@@ -1624,7 +1702,7 @@ public:
const char* typeName() const VL_MT_SAFE { return type().ascii(); } // See also prettyTypeName
AstNode* nextp() const VL_MT_STABLE { return m_nextp; }
AstNode* backp() const VL_MT_STABLE { return m_backp; }
AstNode* abovep() const; // Parent node above, only when no nextp() as otherwise slow
AstNode* abovep() const; // Get parent node above, only for list head and tail
AstNode* op1p() const VL_MT_STABLE { return m_op1p; }
AstNode* op2p() const VL_MT_STABLE { return m_op2p; }
AstNode* op3p() const VL_MT_STABLE { return m_op3p; }
@@ -1676,7 +1754,7 @@ public:
static constexpr int INSTR_COUNT_PLI = 20; // PLI routines
// ACCESSORS
virtual string name() const VL_MT_STABLE { return ""; }
virtual string name() const { return ""; }
virtual string origName() const { return ""; }
virtual void name(const string& name) {
this->v3fatalSrc("name() called on object without name() method");
@@ -1684,7 +1762,7 @@ public:
virtual void tag(const string& text) {}
virtual string tag() const { return ""; }
virtual string verilogKwd() const { return ""; }
string nameProtect() const VL_MT_STABLE; // Name with --protect-id applied
string nameProtect() const; // Name with --protect-id applied
string origNameProtect() const; // origName with --protect-id applied
string shortName() const; // Name with __PVT__ removed for concatenating scopes
static string dedotName(const string& namein); // Name with dots removed
@@ -1697,10 +1775,10 @@ public:
static string encodeName(const string& namein);
static string encodeNumber(int64_t num); // Encode number into internal C representation
static string vcdName(const string& namein); // Name for printing out to vcd files
string prettyName() const VL_MT_STABLE { return prettyName(name()); }
string prettyName() const { return prettyName(name()); }
string prettyNameQ() const { return prettyNameQ(name()); }
// "VARREF" for error messages (NOT dtype's pretty name)
string prettyTypeName() const VL_MT_STABLE;
string prettyTypeName() const;
virtual string prettyOperatorName() const { return "operator " + prettyTypeName(); }
FileLine* fileline() const VL_MT_SAFE { return m_fileline; }
void fileline(FileLine* fl) { m_fileline = fl; }
@@ -1732,74 +1810,74 @@ public:
inline bool isString() const VL_MT_STABLE;
// clang-format off
VNUser user1u() const VL_MT_STABLE {
VNUser user1u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser1InUse::s_userBusy, "userp set w/o busy");
return ((m_user1Cnt==VNUser1InUse::s_userCntGbl) ? m_user1u : VNUser{0});
//UASSERT_STATIC(VNUser1InUse::s_userBusy, "user1p used without AstUserInUse");
return ((m_user1Cnt == VNUser1InUse::s_userCntGbl) ? m_user1u : VNUser{0});
}
AstNode* user1p() const VL_MT_STABLE { return user1u().toNodep(); }
void user1u(const VNUser& user) { m_user1u=user; m_user1Cnt=VNUser1InUse::s_userCntGbl; }
void user1p(void* userp) { user1u(VNUser{userp}); }
int user1() const { return user1u().toInt(); }
void user1(int val) { user1u(VNUser{val}); }
int user1Inc(int val=1) { int v=user1(); user1(v+val); return v; }
int user1SetOnce() { int v=user1(); if (!v) user1(1); return v; } // Better for cache than user1Inc()
AstNode* user1p() const VL_MT_STABLE { return user1u().toNodep(); }
void user1u(const VNUser& user) { m_user1u = user; m_user1Cnt = VNUser1InUse::s_userCntGbl; }
void user1p(void* userp) { user1u(VNUser{userp}); }
void user1(int val) { user1u(VNUser{val}); }
int user1() const { return user1u().toInt(); }
int user1Inc(int val = 1) { int v = user1(); user1(v + val); return v; }
int user1SetOnce() { int v = user1(); if (!v) user1(1); return v; } // Better for cache than user1Inc()
static void user1ClearTree() { VNUser1InUse::clear(); } // Clear userp()'s across the entire tree
VNUser user2u() const VL_MT_STABLE {
VNUser user2u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser2InUse::s_userBusy, "userp set w/o busy");
return ((m_user2Cnt==VNUser2InUse::s_userCntGbl) ? m_user2u : VNUser{0});
//UASSERT_STATIC(VNUser2InUse::s_userBusy, "user2p used without AstUserInUse");
return ((m_user2Cnt == VNUser2InUse::s_userCntGbl) ? m_user2u : VNUser{0});
}
AstNode* user2p() const VL_MT_STABLE { return user2u().toNodep(); }
void user2u(const VNUser& user) { m_user2u=user; m_user2Cnt=VNUser2InUse::s_userCntGbl; }
void user2p(void* userp) { user2u(VNUser{userp}); }
int user2() const { return user2u().toInt(); }
void user2(int val) { user2u(VNUser{val}); }
int user2Inc(int val=1) { int v=user2(); user2(v+val); return v; }
int user2SetOnce() { int v=user2(); if (!v) user2(1); return v; } // Better for cache than user2Inc()
AstNode* user2p() const VL_MT_STABLE { return user2u().toNodep(); }
void user2u(const VNUser& user) { m_user2u = user; m_user2Cnt = VNUser2InUse::s_userCntGbl; }
void user2p(void* userp) { user2u(VNUser{userp}); }
void user2(int val) { user2u(VNUser{val}); }
int user2() const { return user2u().toInt(); }
int user2Inc(int val = 1) { int v = user2(); user2(v + val); return v; }
int user2SetOnce() { int v = user2(); if (!v) user2(1); return v; } // Better for cache than user2Inc()
static void user2ClearTree() { VNUser2InUse::clear(); } // Clear userp()'s across the entire tree
VNUser user3u() const VL_MT_STABLE {
VNUser user3u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser3InUse::s_userBusy, "userp set w/o busy");
return ((m_user3Cnt==VNUser3InUse::s_userCntGbl) ? m_user3u : VNUser{0});
//UASSERT_STATIC(VNUser3InUse::s_userBusy, "user3p used without AstUserInUse");
return ((m_user3Cnt == VNUser3InUse::s_userCntGbl) ? m_user3u : VNUser{0});
}
AstNode* user3p() const VL_MT_STABLE { return user3u().toNodep(); }
void user3u(const VNUser& user) { m_user3u=user; m_user3Cnt=VNUser3InUse::s_userCntGbl; }
void user3p(void* userp) { user3u(VNUser{userp}); }
int user3() const { return user3u().toInt(); }
void user3(int val) { user3u(VNUser{val}); }
int user3Inc(int val=1) { int v=user3(); user3(v+val); return v; }
int user3SetOnce() { int v=user3(); if (!v) user3(1); return v; } // Better for cache than user3Inc()
AstNode* user3p() const VL_MT_STABLE { return user3u().toNodep(); }
void user3u(const VNUser& user) { m_user3u = user; m_user3Cnt = VNUser3InUse::s_userCntGbl; }
void user3p(void* userp) { user3u(VNUser{userp}); }
void user3(int val) { user3u(VNUser{val}); }
int user3() const { return user3u().toInt(); }
int user3Inc(int val = 1) { int v = user3(); user3(v + val); return v; }
int user3SetOnce() { int v = user3(); if (!v) user3(1); return v; } // Better for cache than user3Inc()
static void user3ClearTree() { VNUser3InUse::clear(); } // Clear userp()'s across the entire tree
VNUser user4u() const VL_MT_STABLE {
VNUser user4u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser4InUse::s_userBusy, "userp set w/o busy");
return ((m_user4Cnt==VNUser4InUse::s_userCntGbl) ? m_user4u : VNUser{0});
//UASSERT_STATIC(VNUser4InUse::s_userBusy, "user4p used without AstUserInUse");
return ((m_user4Cnt == VNUser4InUse::s_userCntGbl) ? m_user4u : VNUser{0});
}
AstNode* user4p() const VL_MT_STABLE { return user4u().toNodep(); }
void user4u(const VNUser& user) { m_user4u=user; m_user4Cnt=VNUser4InUse::s_userCntGbl; }
void user4p(void* userp) { user4u(VNUser{userp}); }
int user4() const { return user4u().toInt(); }
void user4(int val) { user4u(VNUser{val}); }
int user4Inc(int val=1) { int v=user4(); user4(v+val); return v; }
int user4SetOnce() { int v=user4(); if (!v) user4(1); return v; } // Better for cache than user4Inc()
AstNode* user4p() const VL_MT_STABLE { return user4u().toNodep(); }
void user4u(const VNUser& user) { m_user4u = user; m_user4Cnt = VNUser4InUse::s_userCntGbl; }
void user4p(void* userp) { user4u(VNUser{userp}); }
void user4(int val) { user4u(VNUser{val}); }
int user4() const { return user4u().toInt(); }
int user4Inc(int val = 1) { int v = user4(); user4(v + val); return v; }
int user4SetOnce() { int v = user4(); if (!v) user4(1); return v; } // Better for cache than user4Inc()
static void user4ClearTree() { VNUser4InUse::clear(); } // Clear userp()'s across the entire tree
VNUser user5u() const VL_MT_STABLE {
VNUser user5u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser5InUse::s_userBusy, "userp set w/o busy");
return ((m_user5Cnt==VNUser5InUse::s_userCntGbl) ? m_user5u : VNUser{0});
//UASSERT_STATIC(VNUser5InUse::s_userBusy, "user5p used without AstUserInUse");
return ((m_user5Cnt == VNUser5InUse::s_userCntGbl) ? m_user5u : VNUser{0});
}
AstNode* user5p() const VL_MT_STABLE { return user5u().toNodep(); }
void user5u(const VNUser& user) { m_user5u=user; m_user5Cnt=VNUser5InUse::s_userCntGbl; }
void user5p(void* userp) { user5u(VNUser{userp}); }
int user5() const { return user5u().toInt(); }
void user5(int val) { user5u(VNUser{val}); }
int user5Inc(int val=1) { int v=user5(); user5(v+val); return v; }
int user5SetOnce() { int v=user5(); if (!v) user5(1); return v; } // Better for cache than user5Inc()
AstNode* user5p() const VL_MT_STABLE { return user5u().toNodep(); }
void user5u(const VNUser& user) { m_user5u = user; m_user5Cnt = VNUser5InUse::s_userCntGbl; }
void user5p(void* userp) { user5u(VNUser{userp}); }
void user5(int val) { user5u(VNUser{val}); }
int user5() const { return user5u().toInt(); }
int user5Inc(int val = 1) { int v = user5(); user5(v + val); return v; }
int user5SetOnce() { int v = user5(); if (!v) user5(1); return v; } // Better for cache than user5Inc()
static void user5ClearTree() { VNUser5InUse::clear(); } // Clear userp()'s across the entire tree
// clang-format on
@@ -1817,6 +1895,7 @@ public:
// ACCESSORS for specific types
// Alas these can't be virtual or they break when passed a nullptr
inline bool isClassHandleValue() const;
inline bool isZero() const;
inline bool isOne() const;
inline bool isNeqZero() const;
@@ -1856,6 +1935,7 @@ public:
void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate
void dtypeSetEmptyQueue() { dtypep(findEmptyQueueDType()); }
void dtypeSetVoid() { dtypep(findVoidDType()); }
void dtypeSetStream() { dtypep(findStreamDType()); }
// Data type locators
AstNodeDType* findBitDType() const { return findBasicDType(VBasicDTypeKwd::LOGIC); }
@@ -1867,6 +1947,7 @@ public:
AstNodeDType* findCHandleDType() const { return findBasicDType(VBasicDTypeKwd::CHANDLE); }
AstNodeDType* findEmptyQueueDType() const;
AstNodeDType* findVoidDType() const;
AstNodeDType* findStreamDType() const;
AstNodeDType* findQueueIndexDType() const;
AstNodeDType* findBitDType(int width, int widthMin, VSigning numeric) const;
AstNodeDType* findLogicDType(int width, int widthMin, VSigning numeric) const;
@@ -1877,9 +1958,9 @@ public:
static AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
// METHODS - dump and error
void v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex);
void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex);
void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN
VL_REQUIRES(V3Error::s().m_mutex);
VL_RELEASE(V3Error::s().m_mutex);
string warnContextPrimary() const VL_REQUIRES(V3Error::s().m_mutex) {
return fileline()->warnContextPrimary();
}
@@ -1915,14 +1996,8 @@ public:
// Iterate and insert - assumes tree format
virtual void addNextStmt(AstNode* newp,
AstNode* belowp); // When calling, "this" is second argument
virtual void addBeforeStmt(AstNode* newp,
AstNode* belowp); // When calling, "this" is second argument
// METHODS - Iterate on a tree
// Clone or return nullptr if nullptr
static AstNode* cloneTreeNull(AstNode* nodep, bool cloneNextLink) {
return nodep ? nodep->cloneTree(cloneNextLink) : nullptr;
}
AstNode* cloneTree(bool cloneNextLink); // Not const, as sets clonep() on original nodep
bool gateTree() { return gateTreeIter(); } // Is tree isGateOptimizable?
inline bool sameTree(const AstNode* node2p) const; // Does tree of this == node2p?
@@ -2056,24 +2131,34 @@ public:
return nodep && privateTypeTest<T>(nodep) ? reinterpret_cast<const T*>(nodep) : nullptr;
}
// For use via the VN_AS macro only
// For use via privateAs or the VN_DBG_AS macro only
template <typename T, typename E>
static T* privateAs(AstNode* nodep) VL_PURE {
static T* unsafePrivateAs(AstNode* nodep) VL_PURE {
static_assert(!uselessCast<T, E>(), "Unnecessary VN_AS, node known to have target type.");
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_AS, node cannot be this type.");
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
<< "'");
return reinterpret_cast<T*>(nodep);
}
template <typename T, typename E>
static const T* privateAs(const AstNode* nodep) VL_PURE {
static const T* unsafePrivateAs(const AstNode* nodep) VL_PURE {
static_assert(!uselessCast<T, E>(), "Unnecessary VN_AS, node known to have target type.");
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_AS, node cannot be this type.");
return reinterpret_cast<const T*>(nodep);
}
// For use via the VN_AS macro only
template <typename T, typename E>
static T* privateAs(AstNode* nodep) VL_PURE {
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
<< "'");
return reinterpret_cast<const T*>(nodep);
return unsafePrivateAs<T, E>(nodep);
}
template <typename T, typename E>
static const T* privateAs(const AstNode* nodep) VL_PURE {
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
<< "'");
return unsafePrivateAs<T, E>(nodep);
}
// Predicate that returns true if the given 'nodep' might have a descendant of type 'T_Node'.
+12 -4
View File
@@ -41,6 +41,10 @@ bool AstNode::isString() const VL_MT_STABLE {
}
bool AstNode::isSigned() const VL_MT_STABLE { return dtypep() && dtypep()->isSigned(); }
bool AstNode::isClassHandleValue() const {
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isNull())
|| VN_IS(dtypep(), ClassRefDType);
}
bool AstNode::isZero() const {
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqZero());
}
@@ -150,12 +154,15 @@ AstCExpr::AstCExpr(FileLine* fl, const string& textStmt, int setwidth, bool clea
}
AstVarRef::AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access)
: ASTGEN_SUPER_VarRef(fl, varp->name(), varp, access) {}
: ASTGEN_SUPER_VarRef(fl, varp, access) {}
// This form only allowed post-link (see above)
AstVarRef::AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access)
: ASTGEN_SUPER_VarRef(fl, varscp->varp()->name(), varscp->varp(), access) {
: ASTGEN_SUPER_VarRef(fl, varscp->varp(), access) {
varScopep(varscp);
}
string AstVarRef::name() const { return varp() ? varp()->name() : "<null>"; }
bool AstVarRef::same(const AstVarRef* samep) const {
if (varScopep()) {
return (varScopep() == samep->varScopep() && access() == samep->access());
@@ -169,13 +176,14 @@ bool AstVarRef::sameNoLvalue(AstVarRef* samep) const {
return (varScopep() == samep->varScopep());
} else {
return (selfPointer() == samep->selfPointer()
&& (!selfPointer().empty() || !samep->selfPointer().empty())
&& (!selfPointer().isEmpty() || !samep->selfPointer().isEmpty())
&& varp()->name() == samep->varp()->name());
}
}
AstVarXRef::AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access)
: ASTGEN_SUPER_VarXRef(fl, varp->name(), varp, access)
: ASTGEN_SUPER_VarXRef(fl, varp, access)
, m_name{varp->name()}
, m_dotted{dotted} {
dtypeFrom(varp);
}
+35 -13
View File
@@ -126,13 +126,13 @@ public:
const char* charIQWN() const {
return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I");
}
string cType(const string& name, bool forFunc, bool isRef) const VL_MT_STABLE;
string cType(const string& name, bool forFunc, bool isRef) const;
// Represents a C++ LiteralType? (can be constexpr)
bool isLiteralType() const VL_MT_STABLE;
private:
class CTypeRecursed;
CTypeRecursed cTypeRecurse(bool compound) const VL_MT_STABLE;
CTypeRecursed cTypeRecurse(bool compound) const;
};
class AstNodeArrayDType VL_NOT_FINAL : public AstNodeDType {
// Array data type, ie "some_dtype var_name [2:0]"
@@ -199,12 +199,9 @@ class AstNodeUOrStructDType VL_NOT_FINAL : public AstNodeDType {
// A struct or union; common handling
// @astgen op1 := membersp : List[AstMemberDType]
private:
// TYPES
using MemberNameMap = std::map<const std::string, AstMemberDType*>;
// MEMBERS
string m_name; // Name from upper typedef, if any
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
MemberNameMap m_members;
AstNodeModule* m_classOrPackagep = nullptr; // Package it will be emitted with
const int m_uniqueNum;
bool m_packed;
bool m_isFourstate = false; // V3Width computes
@@ -250,12 +247,6 @@ public:
static bool packedUnsup() { return true; }
void isFourstate(bool flag) { m_isFourstate = flag; }
bool isFourstate() const override VL_MT_SAFE { return m_isFourstate; }
void clearCache() { m_members.clear(); }
void repairMemberCache();
AstMemberDType* findMember(const string& name) const {
const auto it = m_members.find(name);
return (it == m_members.end()) ? nullptr : it->second;
}
static int lo() { return 0; }
int hi() const { return dtypep()->width() - 1; } // Packed classes look like arrays
VNumRange declRange() const { return VNumRange{hi(), lo()}; }
@@ -438,6 +429,7 @@ public:
bool isEvent() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::EVENT; }
bool isTriggerVec() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::TRIGGERVEC; }
bool isForkSync() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::FORK_SYNC; }
bool isProcessRef() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::PROCESS_REFERENCE; }
bool isDelayScheduler() const VL_MT_SAFE {
return keyword() == VBasicDTypeKwd::DELAY_SCHEDULER;
}
@@ -887,6 +879,7 @@ public:
if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep();
}
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
AstNodeUOrStructDType* getChildStructp() const;
AstNodeDType* subDTypep() const override VL_MT_STABLE {
return m_refDTypep ? m_refDTypep : childDTypep();
}
@@ -1060,7 +1053,7 @@ private:
// Post-width typedefs are removed and point to type directly
AstNodeDType* m_refDTypep = nullptr; // data type pointed to, BELOW the AstTypedef
string m_name; // Name of an AstTypedef
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
AstNodeModule* m_classOrPackagep = nullptr; // Class/package in which it was defined
public:
AstRefDType(FileLine* fl, const string& name)
: ASTGEN_SUPER_RefDType(fl)
@@ -1075,6 +1068,7 @@ public:
AstRefDType(FileLine* fl, FlagTypeOfExpr, AstNode* typeofp)
: ASTGEN_SUPER_RefDType(fl) {
this->typeofp(typeofp);
if (AstNodeDType* const dtp = VN_CAST(typeofp, NodeDType)) refDTypep(dtp);
}
ASTGEN_MEMBERS_AstRefDType;
// METHODS
@@ -1186,6 +1180,34 @@ public:
int widthTotalBytes() const override { return sizeof(std::map<std::string, std::string>); }
bool isCompound() const override { return true; }
};
class AstStreamDType final : public AstNodeDType {
// Stream data type, used only as data type of stream operations
// Should behave like AstPackArrayDType, but it doesn't have a size
public:
explicit AstStreamDType(FileLine* fl)
: ASTGEN_SUPER_StreamDType(fl) {
dtypep(this);
}
ASTGEN_MEMBERS_AstStreamDType;
void dumpSmall(std::ostream& str) const override;
bool hasDType() const override { return true; }
bool maybePointedTo() const override { return true; }
bool undead() const override { return true; }
AstNodeDType* subDTypep() const override VL_MT_SAFE { return nullptr; }
AstNodeDType* virtRefDTypep() const override { return nullptr; }
void virtRefDTypep(AstNodeDType* nodep) override {}
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
// cppcheck-suppress csyleCast
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
// cppcheck-suppress csyleCast
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
// cppcheck-suppress csyleCast
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
int widthAlignBytes() const override { return 1; }
int widthTotalBytes() const override { return 1; }
bool isCompound() const override { return false; }
};
class AstUnsizedArrayDType final : public AstNodeDType {
// Unsized/open-range Array data type, ie "some_dtype var_name []"
// @astgen op1 := childDTypep : Optional[AstNodeDType] // moved to refDTypep() in V3Width
+96 -53
View File
@@ -214,7 +214,7 @@ class AstNodeFTaskRef VL_NOT_FINAL : public AstNodeExpr {
// @astgen op3 := pinsp : List[AstNodeExpr]
// @astgen op4 := scopeNamep : Optional[AstScopeName]
AstNodeFTask* m_taskp = nullptr; // [AfterLink] Pointer to task referenced
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
AstNodeModule* m_classOrPackagep = nullptr; // Class/package of the task
string m_name; // Name of variable
string m_dotted; // Dotted part of scope the name()ed task/func is under or ""
string m_inlinedDots; // Dotted hierarchy flattened out
@@ -360,14 +360,8 @@ class AstNodeCond VL_NOT_FINAL : public AstNodeTriop {
// @astgen alias op2 := thenp
// @astgen alias op3 := elsep
protected:
AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep)
: AstNodeTriop{t, fl, condp, thenp, elsep} {
if (thenp) {
dtypeFrom(thenp);
} else if (elsep) {
dtypeFrom(elsep);
}
}
AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp,
AstNodeExpr* elsep);
public:
ASTGEN_MEMBERS_AstNodeCond;
@@ -449,21 +443,19 @@ class AstNodeVarRef VL_NOT_FINAL : public AstNodeExpr {
VAccess m_access; // Left hand side assignment
AstVar* m_varp; // [AfterLink] Pointer to variable itself
AstVarScope* m_varScopep = nullptr; // Varscope for hierarchy
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
string m_name; // Name of variable
string m_selfPointer; // Output code object pointer (e.g.: 'this')
AstNodeModule* m_classOrPackagep = nullptr; // Class/package of the variable
VSelfPointerText m_selfPointer
= VSelfPointerText{VSelfPointerText::Empty()}; // Output code object
// pointer (e.g.: 'this')
protected:
AstNodeVarRef(VNType t, FileLine* fl, const string& name, const VAccess& access)
AstNodeVarRef(VNType t, FileLine* fl, const VAccess& access)
: AstNodeExpr{t, fl}
, m_access{access}
, m_name{name} {
, m_access{access} {
varp(nullptr);
}
AstNodeVarRef(VNType t, FileLine* fl, const string& name, AstVar* varp, const VAccess& access)
AstNodeVarRef(VNType t, FileLine* fl, AstVar* varp, const VAccess& access)
: AstNodeExpr{t, fl}
, m_access{access}
, m_name{name} {
, m_access{access} {
// May have varp==nullptr
this->varp(varp);
}
@@ -474,8 +466,6 @@ public:
const char* broken() const override;
int instrCount() const override { return widthInstrs(); }
void cloneRelink() override;
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
void name(const string& name) override { m_name = name; }
VAccess access() const { return m_access; }
void access(const VAccess& flag) { m_access = flag; } // Avoid using this; Set in constructor
AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable
@@ -485,9 +475,11 @@ public:
}
AstVarScope* varScopep() const { return m_varScopep; }
void varScopep(AstVarScope* varscp) { m_varScopep = varscp; }
string selfPointer() const { return m_selfPointer; }
void selfPointer(const string& value) { m_selfPointer = value; }
string selfPointerProtect(bool useSelfForThis) const;
const VSelfPointerText& selfPointer() const { return m_selfPointer; }
void selfPointer(const VSelfPointerText& selfPointer) { m_selfPointer = selfPointer; }
string selfPointerProtect(bool useSelfForThis) const {
return selfPointer().protect(useSelfForThis, protect());
}
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
// Know no children, and hot function, so skip iterator for speed
@@ -593,21 +585,13 @@ class AstCMethodHard final : public AstNodeExpr {
string m_name; // Name of method
bool m_pure = false; // Pure optimizable
public:
AstCMethodHard(FileLine* fl, AstNodeExpr* fromp, VFlagChildDType, const string& name,
AstNodeExpr* pinsp = nullptr)
: ASTGEN_SUPER_CMethodHard(fl)
, m_name{name} {
// TODO: this constructor is exactly the same as the other, bar the ignored tag argument
this->fromp(fromp);
this->addPinsp(pinsp);
dtypep(nullptr); // V3Width will resolve
}
AstCMethodHard(FileLine* fl, AstNodeExpr* fromp, const string& name,
AstNodeExpr* pinsp = nullptr)
: ASTGEN_SUPER_CMethodHard(fl)
, m_name{name} {
this->fromp(fromp);
this->addPinsp(pinsp);
setPurity();
}
ASTGEN_MEMBERS_AstCMethodHard;
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
@@ -617,11 +601,13 @@ public:
return (m_name == asamep->m_name);
}
bool isPure() const override { return m_pure; }
void pure(bool flag) { m_pure = flag; }
int instrCount() const override;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
private:
void setPurity();
};
class AstCast final : public AstNodeExpr {
// Cast to appropriate data type
@@ -721,7 +707,7 @@ class AstClassOrPackageRef final : public AstNodeExpr {
private:
string m_name;
// Node not NodeModule to appease some early parser usage
AstNode* m_classOrPackageNodep; // Package hierarchy
AstNode* m_classOrPackageNodep; // Pointer to class/package referenced
public:
AstClassOrPackageRef(FileLine* fl, const string& name, AstNode* classOrPackageNodep,
AstPin* paramsp)
@@ -975,6 +961,20 @@ public:
, m_num(this, 1, on) {
dtypeSetBit();
}
class All0 {};
AstConst(FileLine* fl, All0)
: ASTGEN_SUPER_Const(fl)
, m_num(this, "'0") {
initWithNumber();
fl->warnOff(V3ErrorCode::NEWERSTD, true);
}
class All1 {};
AstConst(FileLine* fl, All1)
: ASTGEN_SUPER_Const(fl)
, m_num(this, "'1") {
initWithNumber();
fl->warnOff(V3ErrorCode::NEWERSTD, true);
}
class Null {};
AstConst(FileLine* fl, Null)
: ASTGEN_SUPER_Const(fl)
@@ -1010,6 +1010,34 @@ public:
// May return nullptr on parse failure.
static AstConst* parseParamLiteral(FileLine* fl, const string& literal);
};
class AstCvtDynArrayToPacked final : public AstNodeExpr {
// Cast from dynamic queue data type to packed array
// @astgen op1 := fromp : AstNodeExpr
public:
AstCvtDynArrayToPacked(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
: ASTGEN_SUPER_CvtDynArrayToPacked(fl) {
this->fromp(fromp);
dtypeFrom(dtp);
}
ASTGEN_MEMBERS_AstCvtDynArrayToPacked;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
};
class AstCvtPackedToDynArray final : public AstNodeExpr {
// Cast from packed array to dynamic queue data type
// @astgen op1 := fromp : AstNodeExpr
public:
AstCvtPackedToDynArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
: ASTGEN_SUPER_CvtPackedToDynArray(fl) {
this->fromp(fromp);
dtypeFrom(dtp);
}
ASTGEN_MEMBERS_AstCvtPackedToDynArray;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
};
class AstDot final : public AstNodeExpr {
// A dot separating paths in an AstVarXRef, AstFuncRef or AstTaskRef
// These are eliminated in the link stage
@@ -1048,7 +1076,7 @@ public:
};
class AstEnumItemRef final : public AstNodeExpr {
AstEnumItem* m_itemp; // [AfterLink] Pointer to item
AstNodeModule* m_classOrPackagep; // Package hierarchy
AstNodeModule* m_classOrPackagep; // Class/package in which it was defined
public:
AstEnumItemRef(FileLine* fl, AstEnumItem* itemp, AstNodeModule* classOrPackagep)
: ASTGEN_SUPER_EnumItemRef(fl)
@@ -1092,7 +1120,8 @@ public:
// METHODS
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return false; }
bool cleanOut() const override { return true; }
bool isPure() const override { return false; }
bool same(const AstNode*) const override { return true; }
};
class AstFError final : public AstNodeExpr {
@@ -1299,7 +1328,9 @@ public:
bool cleanOut() const override { return true; }
};
class AstImplication final : public AstNodeExpr {
// Verilog |-> |=>
// Verilog Implication Operator
// Nonoverlapping "|=>"
// Overlapping "|->" (doesn't currently use this - might make new Ast type)
// @astgen op1 := lhsp : AstNodeExpr
// @astgen op2 := rhsp : AstNodeExpr
// @astgen op3 := sentreep : Optional[AstSenTree]
@@ -1414,6 +1445,7 @@ class AstMemberSel final : public AstNodeExpr {
// @astgen op1 := fromp : AstNodeExpr
// Don't need the class we are extracting from, as the "fromp()"'s datatype can get us to it
string m_name;
VAccess m_access; // Read or write, as in AstNodeVarRef
AstVar* m_varp = nullptr; // Post link, variable within class that is target of selection
public:
AstMemberSel(FileLine* fl, AstNodeExpr* fromp, VFlagChildDType, const string& name)
@@ -1434,10 +1466,12 @@ public:
void dump(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; }
void name(const string& name) override { m_name = name; }
VAccess access() const { return m_access; }
void access(const VAccess& flag) { m_access = flag; }
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return false; }
bool same(const AstNode* samep) const override { return true; } // dtype comparison does it
bool cleanOut() const override { return true; }
bool same(const AstNode* samep) const override;
int instrCount() const override { return widthInstrs(); }
AstVar* varp() const { return m_varp; }
void varp(AstVar* nodep) { m_varp = nodep; }
@@ -1897,7 +1931,7 @@ public:
bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstStructSel final : public AstNodeExpr {
// Unpacked struct member access
// Unpacked struct/union member access
// Parents: math|stmt
// Children: varref, math
// @astgen op1 := fromp : AstNodeExpr
@@ -1915,7 +1949,10 @@ public:
void name(const string& name) override { m_name = name; }
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return false; }
bool cleanOut() const override {
// Not a union
return VN_IS(fromp()->dtypep()->skipRefp(), StructDType);
}
bool same(const AstNode* samep) const override {
const AstStructSel* const sp = static_cast<const AstStructSel*>(samep);
return m_name == sp->m_name;
@@ -2108,14 +2145,14 @@ class AstWith final : public AstNodeExpr {
// Children: expression (equation establishing the with)
// @astgen op1 := indexArgRefp : AstLambdaArgRef
// @astgen op2 := valueArgRefp : AstLambdaArgRef
// @astgen op3 := exprp : AstNodeExpr
// @astgen op3 := exprp : List[AstNode]
public:
AstWith(FileLine* fl, AstLambdaArgRef* indexArgRefp, AstLambdaArgRef* valueArgRefp,
AstNodeExpr* exprp)
: ASTGEN_SUPER_With(fl) {
this->indexArgRefp(indexArgRefp);
this->valueArgRefp(valueArgRefp);
this->exprp(exprp);
this->addExprp(exprp);
}
ASTGEN_MEMBERS_AstWith;
bool same(const AstNode* /*samep*/) const override { return true; }
@@ -4008,16 +4045,19 @@ public:
// === AstNodeCCall ===
class AstCCall final : public AstNodeCCall {
// C++ function call
string m_selfPointer; // Output code object pointer (e.g.: 'this')
VSelfPointerText m_selfPointer
= VSelfPointerText{VSelfPointerText::Empty()}; // Output code object
// pointer (e.g.: 'this')
public:
AstCCall(FileLine* fl, AstCFunc* funcp, AstNodeExpr* argsp = nullptr)
: ASTGEN_SUPER_CCall(fl, funcp, argsp) {}
ASTGEN_MEMBERS_AstCCall;
string selfPointer() const { return m_selfPointer; }
void selfPointer(const string& value) { m_selfPointer = value; }
string selfPointerProtect(bool useSelfForThis) const;
const VSelfPointerText& selfPointer() const { return m_selfPointer; }
void selfPointer(const VSelfPointerText& selfPointer) { m_selfPointer = selfPointer; }
string selfPointerProtect(bool useSelfForThis) const {
return selfPointer().protect(useSelfForThis, protect());
}
};
class AstCMethodCall final : public AstNodeCCall {
// C++ method call
@@ -4657,7 +4697,7 @@ public:
ASTGEN_MEMBERS_AstCvtPackString;
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAssign(lhs); }
string emitVerilog() override { return "%f$_CAST(%l)"; }
string emitC() override { return "VL_CVT_PACK_STR_N%lq(%lW, %li)"; }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
bool sizeMattersLhs() const override { return false; }
@@ -5335,15 +5375,15 @@ public:
class AstVarRef final : public AstNodeVarRef {
// A reference to a variable (lvalue or rvalue)
public:
AstVarRef(FileLine* fl, const string& name, const VAccess& access)
: ASTGEN_SUPER_VarRef(fl, name, nullptr, access) {}
// This form only allowed post-link because output/wire compression may
// lead to deletion of AstVar's
inline AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access);
// This form only allowed post-link (see above)
inline AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access);
ASTGEN_MEMBERS_AstVarRef;
inline string name() const override; // * = Var name
void dump(std::ostream& str) const override;
const char* broken() const override;
bool same(const AstNode* samep) const override;
inline bool same(const AstVarRef* samep) const;
inline bool sameNoLvalue(AstVarRef* samep) const;
@@ -5355,14 +5395,17 @@ public:
class AstVarXRef final : public AstNodeVarRef {
// A VarRef to something in another module before AstScope.
// Includes pin on a cell, as part of a ASSIGN statement to connect I/Os until AstScope
string m_name;
string m_dotted; // Dotted part of scope the name()'ed reference is under or ""
string m_inlinedDots; // Dotted hierarchy flattened out
public:
AstVarXRef(FileLine* fl, const string& name, const string& dotted, const VAccess& access)
: ASTGEN_SUPER_VarXRef(fl, name, nullptr, access)
: ASTGEN_SUPER_VarXRef(fl, nullptr, access)
, m_name{name}
, m_dotted{dotted} {}
inline AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access);
ASTGEN_MEMBERS_AstVarXRef;
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
void dump(std::ostream& str) const override;
string dotted() const { return m_dotted; }
void dotted(const string& dotted) { m_dotted = dotted; }
+106 -41
View File
@@ -32,7 +32,7 @@
class AstNodeBlock VL_NOT_FINAL : public AstNode {
// A Begin/fork block
// @astgen op1 := stmtsp : List[AstNode]
// @astgen op2 := stmtsp : List[AstNode]
// Parents: statement
private:
string m_name; // Name of block
@@ -81,11 +81,12 @@ private:
bool m_isConstructor : 1; // Class constructor
bool m_isHideLocal : 1; // Verilog local
bool m_isHideProtected : 1; // Verilog protected
bool m_pure : 1; // DPI import pure (vs. virtual pure)
bool m_dpiPure : 1; // DPI import pure (vs. virtual pure)
bool m_pureVirtual : 1; // Pure virtual
bool m_recursive : 1; // Recursive or part of recursion
bool m_underGenerate : 1; // Under generate (for warning)
bool m_virtual : 1; // Virtual method in class
bool m_needProcess : 1; // Implements part of a process that allocates std::process
VLifetime m_lifetime; // Lifetime
protected:
AstNodeFTask(VNType t, FileLine* fl, const string& name, AstNode* stmtsp)
@@ -106,21 +107,25 @@ protected:
, m_isConstructor{false}
, m_isHideLocal{false}
, m_isHideProtected{false}
, m_pure{false}
, m_dpiPure{false}
, m_pureVirtual{false}
, m_recursive{false}
, m_underGenerate{false}
, m_virtual{false} {
, m_virtual{false}
, m_needProcess{false} {
addStmtsp(stmtsp);
cname(name); // Might be overridden by dpi import/export
}
public:
ASTGEN_MEMBERS_AstNodeFTask;
virtual AstNodeFTask* cloneType(const string& name) = 0;
void dump(std::ostream& str = std::cout) const override;
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
bool maybePointedTo() const override { return true; }
bool isGateOptimizable() const override { return !((m_dpiExport || m_dpiImport) && !m_pure); }
bool isGateOptimizable() const override {
return !((m_dpiExport || m_dpiImport) && !m_dpiPure);
}
// {AstFunc only} op1 = Range output variable
void name(const string& name) override { m_name = name; }
string cname() const { return m_cname; }
@@ -160,8 +165,8 @@ public:
void isHideLocal(bool flag) { m_isHideLocal = flag; }
bool isHideProtected() const { return m_isHideProtected; }
void isHideProtected(bool flag) { m_isHideProtected = flag; }
void pure(bool flag) { m_pure = flag; }
bool pure() const { return m_pure; }
void dpiPure(bool flag) { m_dpiPure = flag; }
bool dpiPure() const { return m_dpiPure; }
void pureVirtual(bool flag) { m_pureVirtual = flag; }
bool pureVirtual() const { return m_pureVirtual; }
void recursive(bool flag) { m_recursive = flag; }
@@ -170,9 +175,20 @@ public:
bool underGenerate() const { return m_underGenerate; }
void isVirtual(bool flag) { m_virtual = flag; }
bool isVirtual() const { return m_virtual; }
void setNeedProcess() { m_needProcess = true; }
bool needProcess() const { return m_needProcess; }
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
VLifetime lifetime() const { return m_lifetime; }
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
void propagateAttrFrom(const AstNodeFTask* fromp) {
// Creating a wrapper with e.g. cloneType(); preserve some attributes
classMethod(fromp->classMethod());
isHideLocal(fromp->isHideLocal());
isHideProtected(fromp->isHideProtected());
isVirtual(fromp->isVirtual());
lifetime(fromp->lifetime());
underGenerate(fromp->underGenerate());
}
};
class AstNodeFile VL_NOT_FINAL : public AstNode {
// Emitted Output file
@@ -272,10 +288,13 @@ public:
class AstNodeProcedure VL_NOT_FINAL : public AstNode {
// IEEE procedure: initial, final, always
// @astgen op2 := stmtsp : List[AstNode] // Note: op1 is used in some sub-types only
bool m_suspendable = false; // Is suspendable by a Delay, EventControl, etc.
bool m_suspendable : 1; // Is suspendable by a Delay, EventControl, etc.
bool m_needProcess : 1; // Implements part of a process that allocates std::process
protected:
AstNodeProcedure(VNType t, FileLine* fl, AstNode* stmtsp)
: AstNode{t, fl} {
m_needProcess = false;
m_suspendable = false;
addStmtsp(stmtsp);
}
@@ -286,6 +305,8 @@ public:
bool isJustOneBodyStmt() const { return stmtsp() && !stmtsp()->nextp(); }
bool isSuspendable() const { return m_suspendable; }
void setSuspendable() { m_suspendable = true; }
bool needProcess() const { return m_needProcess; }
void setNeedProcess() { m_needProcess = true; }
};
class AstNodeRange VL_NOT_FINAL : public AstNode {
// A range, sized or unsized
@@ -308,8 +329,6 @@ public:
// METHODS
void addNextStmt(AstNode* newp,
AstNode* belowp) override; // Stop statement searchback here
void addBeforeStmt(AstNode* newp,
AstNode* belowp) override; // Stop statement searchback here
void dump(std::ostream& str = std::cout) const override;
};
class AstNodeAssign VL_NOT_FINAL : public AstNodeStmt {
@@ -571,13 +590,14 @@ private:
bool m_isInline : 1; // Inline function
bool m_isVirtual : 1; // Virtual function
bool m_entryPoint : 1; // User may call into this top level function
bool m_pure : 1; // Pure function
bool m_dpiPure : 1; // Pure DPI function
bool m_dpiContext : 1; // Declared as 'context' DPI import/export function
bool m_dpiExportDispatcher : 1; // This is the DPI export entry point (i.e.: called by user)
bool m_dpiExportImpl : 1; // DPI export implementation (called from DPI dispatcher via lookup)
bool m_dpiImportPrototype : 1; // This is the DPI import prototype (i.e.: provided by user)
bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code
bool m_dpiTraceInit : 1; // DPI trace_init
bool m_needProcess : 1; // Implements part of a process that allocates std::process
public:
AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "")
: ASTGEN_SUPER_CFunc(fl) {
@@ -597,8 +617,9 @@ public:
m_isLoose = false;
m_isInline = false;
m_isVirtual = false;
m_needProcess = false;
m_entryPoint = false;
m_pure = false;
m_dpiPure = false;
m_dpiContext = false;
m_dpiExportDispatcher = false;
m_dpiExportImpl = false;
@@ -665,10 +686,12 @@ public:
void isInline(bool flag) { m_isInline = flag; }
bool isVirtual() const { return m_isVirtual; }
void isVirtual(bool flag) { m_isVirtual = flag; }
bool needProcess() const { return m_needProcess; }
void setNeedProcess() { m_needProcess = true; }
bool entryPoint() const { return m_entryPoint; }
void entryPoint(bool flag) { m_entryPoint = flag; }
bool pure() const { return m_pure; }
void pure(bool flag) { m_pure = flag; }
bool dpiPure() const { return m_dpiPure; }
void dpiPure(bool flag) { m_dpiPure = flag; }
bool dpiContext() const { return m_dpiContext; }
void dpiContext(bool flag) { m_dpiContext = flag; }
bool dpiExportDispatcher() const VL_MT_SAFE { return m_dpiExportDispatcher; }
@@ -688,6 +711,16 @@ public:
&& finalsp() == nullptr;
}
};
class AstCLocalScope final : public AstNode {
// Pack statements into an unnamed scope when generating C++
// @astgen op1 := stmtsp : List[AstNode]
public:
AstCLocalScope(FileLine* fl, AstNode* stmtsp)
: ASTGEN_SUPER_CLocalScope(fl) {
this->addStmtsp(stmtsp);
}
ASTGEN_MEMBERS_AstCLocalScope;
};
class AstCUse final : public AstNode {
// C++ use of a class or #include; indicates need of forward declaration
// Parents: NODEMODULE
@@ -809,6 +842,8 @@ class AstClassExtends final : public AstNode {
// @astgen op1 := childDTypep : Optional[AstNodeDType]
// @astgen op2 := classOrPkgsp : Optional[AstNode]
const bool m_isImplements = false; // class implements
bool m_parameterized = false; // has parameters in its statement
public:
AstClassExtends(FileLine* fl, AstNode* classOrPkgsp, bool isImplements)
: ASTGEN_SUPER_ClassExtends(fl)
@@ -819,8 +854,12 @@ public:
void dump(std::ostream& str) const override;
bool hasDType() const override { return true; }
string verilogKwd() const override { return isImplements() ? "implements" : "extends"; }
AstClass* classp() const; // Class being extended (after link)
// Class being extended (after link and instantiation if needed)
AstClass* classOrNullp() const;
AstClass* classp() const; // Like above, but throws error if nulll
bool isImplements() const { return m_isImplements; }
void parameterized(bool flag) { m_parameterized = flag; }
bool parameterized() const { return m_parameterized; }
};
class AstClocking final : public AstNode {
// Parents: MODULE
@@ -1359,8 +1398,8 @@ public:
string name() const override VL_MT_STABLE { return m_name; } // * = Scope name
void name(const string& name) override { m_name = name; }
void dump(std::ostream& str) const override;
bool same(const AstNode* samep) const override;
string nameDotless() const;
string nameVlSym() const { return string{"vlSymsp->"} + nameDotless(); }
AstNodeModule* modp() const { return m_modp; }
//
AstScope* aboveScopep() const VL_MT_SAFE { return m_aboveScopep; }
@@ -1495,6 +1534,7 @@ class AstTypeTable final : public AstNode {
AstEmptyQueueDType* m_emptyQueuep = nullptr;
AstQueueDType* m_queueIndexp = nullptr;
AstVoidDType* m_voidp = nullptr;
AstStreamDType* m_streamp = nullptr;
AstBasicDType* m_basicps[VBasicDTypeKwd::_ENUM_MAX]{};
//
using DetailedMap = std::map<VBasicTypeKey, AstBasicDType*>;
@@ -1520,6 +1560,7 @@ public:
AstEmptyQueueDType* findEmptyQueueDType(FileLine* fl);
AstQueueDType* findQueueIndexDType(FileLine* fl);
AstVoidDType* findVoidDType(FileLine* fl);
AstStreamDType* findStreamDType(FileLine* fl);
void clearCache();
void repairCache();
void dump(std::ostream& str = std::cout) const override;
@@ -1628,6 +1669,7 @@ class AstVar final : public AstNode {
bool m_usedLoopIdx : 1; // Variable subject of for unrolling
bool m_usedVirtIface : 1; // Signal used through a virtual interface
bool m_funcLocal : 1; // Local variable for a function
bool m_funcLocalSticky : 1; // As m_funcLocal but remains set if var is moved to a static
bool m_funcReturn : 1; // Return variable for a function
bool m_attrScBv : 1; // User force bit vector attribute
bool m_attrIsolateAssign : 1; // User isolate_assignments attribute
@@ -1671,6 +1713,7 @@ class AstVar final : public AstNode {
m_sigUserRdPublic = false;
m_sigUserRWPublic = false;
m_funcLocal = false;
m_funcLocalSticky = false;
m_funcReturn = false;
m_attrScBv = false;
m_attrIsolateAssign = false;
@@ -1758,6 +1801,7 @@ public:
}
ASTGEN_MEMBERS_AstVar;
void dump(std::ostream& str) const override;
bool same(const AstNode* samep) const override;
string name() const override VL_MT_STABLE VL_MT_SAFE { return m_name; } // * = Var name
bool hasDType() const override { return true; }
bool maybePointedTo() const override { return true; }
@@ -1789,7 +1833,7 @@ public:
string dpiTmpVarType(const string& varName) const;
// Return Verilator internal type for argument: CData, SData, IData, WData
string vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc = "",
bool asRef = false) const VL_MT_STABLE;
bool asRef = false) const;
string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc
string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc
string vlPropDecl(const string& propName) const; // Return VerilatorVarProps declaration
@@ -1833,7 +1877,10 @@ public:
void isContinuously(bool flag) { m_isContinuously = flag; }
void isStatic(bool flag) { m_isStatic = flag; }
void isIfaceParent(bool flag) { m_isIfaceParent = flag; }
void funcLocal(bool flag) { m_funcLocal = flag; }
void funcLocal(bool flag) {
m_funcLocal = flag;
if (flag) m_funcLocalSticky = true;
}
void funcReturn(bool flag) { m_funcReturn = flag; }
void hasStrengthAssignment(bool flag) { m_hasStrengthAssignment = flag; }
bool hasStrengthAssignment() { return m_hasStrengthAssignment; }
@@ -1886,9 +1933,7 @@ public:
bool isClassMember() const { return varType() == VVarType::MEMBER; }
bool isStatementTemp() const { return (varType() == VVarType::STMTTEMP); }
bool isXTemp() const { return (varType() == VVarType::XTEMP); }
bool isParam() const VL_MT_SAFE {
return (varType() == VVarType::LPARAM || varType() == VVarType::GPARAM);
}
bool isParam() const { return varType().isParam(); }
bool isGParam() const { return (varType() == VVarType::GPARAM); }
bool isGenVar() const { return (varType() == VVarType::GENVAR); }
bool isBitLogic() const {
@@ -1915,6 +1960,7 @@ public:
bool isStatic() const VL_MT_SAFE { return m_isStatic; }
bool isLatched() const { return m_isLatched; }
bool isFuncLocal() const { return m_funcLocal; }
bool isFuncLocalSticky() const { return m_funcLocalSticky; }
bool isFuncReturn() const { return m_funcReturn; }
bool isPullup() const { return m_isPullup; }
bool isPulldown() const { return m_isPulldown; }
@@ -1928,18 +1974,25 @@ public:
string verilogKwd() const override;
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
VLifetime lifetime() const { return m_lifetime; }
void propagateAttrFrom(AstVar* fromp) {
void propagateAttrFrom(const AstVar* fromp) {
// This is getting connected to fromp; keep attributes
// Note the method below too
if (fromp->attrFileDescr()) attrFileDescr(true);
if (fromp->attrIsolateAssign()) attrIsolateAssign(true);
if (fromp->isContinuously()) isContinuously(true);
}
void propagateWrapAttrFrom(const AstVar* fromp) {
// Creating a function wrapper; keep attributes
propagateAttrFrom(fromp);
direction(fromp->direction());
declDirection(fromp->declDirection());
lifetime(fromp->lifetime());
}
bool gateMultiInputOptimizable() const {
// Ok to gate optimize; must return false if propagateAttrFrom would do anything
return !isUsedClock();
}
void combineType(AstVar* typevarp) {
void combineType(const AstVar* typevarp) {
// This is same as typevarp (for combining input & reg decls)
// "this" is the input var. typevarp is the reg var.
propagateAttrFrom(typevarp);
@@ -2000,6 +2053,7 @@ public:
bool maybePointedTo() const override { return true; }
string name() const override VL_MT_STABLE { return scopep()->name() + "->" + varp()->name(); }
void dump(std::ostream& str) const override;
bool same(const AstNode* samep) const override;
bool hasDType() const override { return true; }
AstVar* varp() const VL_MT_STABLE { return m_varp; } // [After Link] Pointer to variable
AstScope* scopep() const VL_MT_STABLE { return m_scopep; } // Pointer to scope it's under
@@ -2012,7 +2066,7 @@ public:
class AstBegin final : public AstNodeBlock {
// A Begin/end named block, only exists shortly after parsing until linking
// Parents: statement
// @astgen op2 := genforp : Optional[AstNode]
// @astgen op1 := genforp : Optional[AstNode]
bool m_generate; // Underneath a generate
const bool m_implied; // Not inserted by user
@@ -2031,6 +2085,7 @@ public:
};
class AstFork final : public AstNodeBlock {
// A fork named block
// @astgen op1 := initsp : List[AstNode]
// Parents: statement
// Children: statements
private:
@@ -2056,6 +2111,24 @@ public:
}
ASTGEN_MEMBERS_AstFunc;
bool hasDType() const override { return true; }
AstNodeFTask* cloneType(const string& name) override {
return new AstFunc{fileline(), name, nullptr, nullptr};
}
};
class AstLet final : public AstNodeFTask {
// Verilog "let" statement
// Parents: MODULE
// stmtp is always a StmtExpr as Let always returns AstNodeExpr
public:
AstLet(FileLine* fl, const string& name)
: ASTGEN_SUPER_Let(fl, name, nullptr) {}
ASTGEN_MEMBERS_AstLet;
bool hasDType() const override { return true; }
const char* broken() const override {
BROKEN_RTN(!VN_IS(stmtsp(), StmtExpr));
return nullptr;
}
AstNodeFTask* cloneType(const string& name) override { return new AstLet{fileline(), name}; }
};
class AstProperty final : public AstNodeFTask {
// A property inside a module
@@ -2064,6 +2137,9 @@ public:
: ASTGEN_SUPER_Property(fl, name, stmtp) {}
ASTGEN_MEMBERS_AstProperty;
bool hasDType() const override { return true; }
AstNodeFTask* cloneType(const string& name) override {
return new AstProperty{fileline(), name, nullptr};
}
};
class AstTask final : public AstNodeFTask {
// A task inside a module
@@ -2071,6 +2147,9 @@ public:
AstTask(FileLine* fl, const string& name, AstNode* stmtp)
: ASTGEN_SUPER_Task(fl, name, stmtp) {}
ASTGEN_MEMBERS_AstTask;
AstNodeFTask* cloneType(const string& name) override {
return new AstTask{fileline(), name, nullptr};
}
};
// === AstNodeFile ===
@@ -2109,17 +2188,13 @@ public:
// === AstNodeModule ===
class AstClass final : public AstNodeModule {
// @astgen op4 := extendsp : List[AstClassExtends]
// TYPES
using MemberNameMap = std::map<const std::string, AstNode*>;
// MEMBERS
MemberNameMap m_members; // Members or method children
AstClassPackage* m_classOrPackagep = nullptr; // Class package this is under
AstClassPackage* m_classOrPackagep = nullptr; // Package it will be emitted with
bool m_extended = false; // Is extension or extended by other classes
bool m_interfaceClass = false; // Interface class
bool m_needRNG = false; // Need RNG, uses srandom/randomize
bool m_virtual = false; // Virtual class
bool m_parameterized = false; // Parameterized class
void insertCache(AstNode* nodep);
public:
AstClass(FileLine* fl, const string& name)
@@ -2134,16 +2209,7 @@ public:
AstClassPackage* classOrPackagep() const VL_MT_SAFE { return m_classOrPackagep; }
void classOrPackagep(AstClassPackage* classpackagep) { m_classOrPackagep = classpackagep; }
AstNode* membersp() const { return stmtsp(); }
void addMembersp(AstNode* nodep) {
insertCache(nodep);
addStmtsp(nodep);
}
void clearCache() { m_members.clear(); }
void repairCache();
AstNode* findMember(const string& name) const {
const auto it = m_members.find(name);
return (it == m_members.end()) ? nullptr : it->second;
}
void addMembersp(AstNode* nodep) { addStmtsp(nodep); }
bool isExtended() const { return m_extended; }
void isExtended(bool flag) { m_extended = flag; }
bool isInterfaceClass() const { return m_interfaceClass; }
@@ -2159,6 +2225,7 @@ public:
static bool isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp);
// Return the lowest class extended from, or this class
AstClass* baseMostClassp();
static bool isCacheableChild(const AstNode* nodep);
};
class AstClassPackage final : public AstNodeModule {
// The static information portion of a class (treated similarly to a package)
@@ -3240,8 +3307,6 @@ public:
int instrCount() const override { return INSTR_COUNT_BRANCH; }
bool same(const AstNode* /*samep*/) const override { return true; }
// Stop statement searchback here
void addBeforeStmt(AstNode* newp, AstNode* belowp) override;
// Stop statement searchback here
void addNextStmt(AstNode* newp, AstNode* belowp) override;
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
};
+161 -96
View File
@@ -25,6 +25,7 @@
#include "V3Hasher.h"
#include "V3PartitionGraph.h" // Just for mtask dumping
#include "V3String.h"
#include "V3Width.h"
#include "V3Ast__gen_macros.h" // Generated by 'astgen'
@@ -63,7 +64,7 @@ void AstNodeFTaskRef::cloneRelink() {
bool AstNodeFTaskRef::isPure() const {
// TODO: For non-DPI functions we could traverse the AST of function's body to determine
// pureness.
return this->taskp() && this->taskp()->dpiImport() && this->taskp()->pure();
return this->taskp() && this->taskp()->dpiImport() && this->taskp()->dpiPure();
}
bool AstNodeFTaskRef::isGateOptimizable() const { return m_taskp && m_taskp->isGateOptimizable(); }
@@ -83,12 +84,6 @@ void AstNodeVarRef::cloneRelink() {
}
}
string AstNodeVarRef::selfPointerProtect(bool useSelfForThis) const {
const string& sp
= useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer();
return VIdProtect::protectWordsIf(sp, protect());
}
void AstAddrOfCFunc::cloneRelink() {
if (m_funcp && m_funcp->clonep()) m_funcp = m_funcp->clonep();
}
@@ -103,29 +98,8 @@ int AstNodeSel::bitConst() const {
return (constp ? constp->toSInt() : 0);
}
void AstNodeUOrStructDType::repairMemberCache() {
clearCache();
for (AstMemberDType* itemp = membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) {
if (m_members.find(itemp->name()) != m_members.end()) {
itemp->v3error("Duplicate declaration of member name: " << itemp->prettyNameQ());
} else {
m_members.emplace(itemp->name(), itemp);
}
}
}
const char* AstNodeUOrStructDType::broken() const {
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
std::unordered_set<AstMemberDType*> exists;
for (AstMemberDType* itemp = membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) {
exists.insert(itemp);
}
for (MemberNameMap::const_iterator it = m_members.begin(); it != m_members.end(); ++it) {
if (VL_UNCOVERABLE(exists.find(it->second) == exists.end())) {
this->v3error("Internal: Structure member broken: " << it->first);
return "member broken";
}
}
return nullptr;
}
@@ -147,14 +121,22 @@ const char* AstNodeCCall::broken() const {
BROKEN_RTN(m_funcp && !m_funcp->brokeExists());
return nullptr;
}
bool AstNodeCCall::isPure() const { return funcp()->pure(); }
bool AstNodeCCall::isPure() const { return funcp()->dpiPure(); }
string AstCCall::selfPointerProtect(bool useSelfForThis) const {
const string& sp
= useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer();
return VIdProtect::protectWordsIf(sp, protect());
AstNodeCond::AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp,
AstNodeExpr* elsep)
: AstNodeTriop{t, fl, condp, thenp, elsep} {
UASSERT_OBJ(thenp, this, "No thenp expression");
UASSERT_OBJ(elsep, this, "No elsep expression");
if (thenp->isClassHandleValue() && elsep->isClassHandleValue()) {
// Get the most-deriving class type that both arguments can be casted to.
AstNodeDType* const commonClassTypep = V3Width::getCommonClassTypep(thenp, elsep);
UASSERT_OBJ(commonClassTypep, this, "No common base class exists");
dtypep(commonClassTypep);
} else {
dtypeFrom(thenp);
}
}
void AstNodeCond::numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
const V3Number& ths) {
if (lhs.isNeqZero()) {
@@ -391,13 +373,15 @@ string AstVar::verilogKwd() const {
return "wreal";
} else if (varType() == VVarType::IFACEREF) {
return "ifaceref";
} else {
} else if (dtypep()) {
return dtypep()->name();
} else {
return "UNKNOWN";
}
}
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc,
bool asRef) const VL_MT_STABLE {
bool asRef) const {
UASSERT_OBJ(!forReturn, this,
"Internal data is never passed as return, but as first argument");
string ostatic;
@@ -714,12 +698,12 @@ public:
}
};
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef) const VL_MT_STABLE {
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef) const {
const CTypeRecursed info = cTypeRecurse(false);
return info.render(name, isRef);
}
AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const VL_MT_STABLE {
AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
// Legacy compound argument currently just passed through and unused
CTypeRecursed info;
@@ -782,6 +766,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const VL_M
info.m_type = "VlDynamicTriggerScheduler";
} else if (bdtypep->isForkSync()) {
info.m_type = "VlForkSync";
} else if (bdtypep->isProcessRef()) {
info.m_type = "VlProcessRef";
} else if (bdtypep->isEvent()) {
info.m_type = "VlEvent";
} else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width
@@ -1067,6 +1053,15 @@ AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) {
return m_voidp;
}
AstStreamDType* AstTypeTable::findStreamDType(FileLine* fl) {
if (VL_UNLIKELY(!m_streamp)) {
AstStreamDType* const newp = new AstStreamDType{fl};
addTypesp(newp);
m_streamp = newp;
}
return m_streamp;
}
AstQueueDType* AstTypeTable::findQueueIndexDType(FileLine* fl) {
if (VL_UNLIKELY(!m_queueIndexp)) {
AstQueueDType* const newp = new AstQueueDType{fl, AstNode::findUInt32DType(), nullptr};
@@ -1269,44 +1264,17 @@ AstVarScope* AstConstPool::findConst(AstConst* initp, bool mergeDType) {
//======================================================================
// Special walking tree inserters
void AstNode::addBeforeStmt(AstNode* newp, AstNode*) {
UASSERT_OBJ(backp(), newp, "Can't find current statement to addBeforeStmt");
// Look up; virtual call will find where to put it
this->backp()->addBeforeStmt(newp, this);
}
void AstNode::addNextStmt(AstNode* newp, AstNode*) {
UASSERT_OBJ(backp(), newp, "Can't find current statement to addNextStmt");
// Look up; virtual call will find where to put it
this->backp()->addNextStmt(newp, this);
}
void AstNodeStmt::addBeforeStmt(AstNode* newp, AstNode*) {
// Insert newp before current node
this->addHereThisAsNext(newp);
}
void AstNodeStmt::addNextStmt(AstNode* newp, AstNode*) {
// Insert newp after current node
this->addNextHere(newp);
}
void AstWhile::addBeforeStmt(AstNode* newp, AstNode* belowp) {
// Special, as statements need to be put in different places
// Belowp is how we came to recurse up to this point
// Preconditions insert first just before themselves (the normal rule
// for other statement types)
if (belowp == precondsp()) {
// Must have been first statement in precondsp list, so newp is new first statement
belowp->addHereThisAsNext(newp);
} else if (belowp == condp()) {
// Goes before condition, IE in preconditions
addPrecondsp(newp);
} else if (belowp == stmtsp()) {
// Was first statement in body, so new front
belowp->addHereThisAsNext(newp);
} else {
belowp->v3fatalSrc("Doesn't look like this was really under the while");
}
}
void AstWhile::addNextStmt(AstNode* newp, AstNode* belowp) {
// Special, as statements need to be put in different places
// Belowp is how we came to recurse up to this point
@@ -1377,6 +1345,7 @@ void AstNode::dump(std::ostream& str) const {
void AstNodeProcedure::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (isSuspendable()) str << " [SUSP]";
if (needProcess()) str << " [NPRC]";
}
void AstAlways::dump(std::ostream& str) const {
@@ -1440,29 +1409,10 @@ const char* AstClassPackage::broken() const {
void AstClassPackage::cloneRelink() {
if (m_classp && m_classp->clonep()) m_classp = m_classp->clonep();
}
void AstClass::insertCache(AstNode* nodep) {
const bool doit = (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|| (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto())
|| VN_IS(nodep, CFunc));
if (doit) {
if (m_members.find(nodep->name()) != m_members.end()) {
nodep->v3error("Duplicate declaration of member name: " << nodep->prettyNameQ());
} else {
m_members.emplace(nodep->name(), nodep);
}
}
}
void AstClass::repairCache() {
clearCache();
for (auto* itemp = membersp(); itemp; itemp = itemp->nextp()) {
if (const auto* const scopep = VN_CAST(itemp, Scope)) {
for (auto* blockp = scopep->blocksp(); blockp; blockp = blockp->nextp()) {
insertCache(blockp);
}
} else {
insertCache(itemp);
}
}
bool AstClass::isCacheableChild(const AstNode* nodep) {
return (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|| (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto())
|| VN_IS(nodep, CFunc));
}
AstClass* AstClass::baseMostClassp() {
AstClass* basep = this;
@@ -1499,13 +1449,20 @@ void AstClassExtends::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (isImplements()) str << " [IMPLEMENTS]";
}
AstClass* AstClassExtends::classp() const {
const AstClassRefDType* refp = VN_CAST(dtypep(), ClassRefDType);
if (VL_UNLIKELY(!refp)) { // LinkDot uses this for 'super.'
refp = VN_AS(childDTypep(), ClassRefDType);
AstClass* AstClassExtends::classOrNullp() const {
const AstNodeDType* const dtp = dtypep() ? dtypep() : childDTypep();
const AstClassRefDType* const refp = VN_CAST(dtp, ClassRefDType);
if (refp && !refp->paramsp()) {
// Class already resolved
return refp->classp();
} else {
return nullptr;
}
UASSERT_OBJ(refp, this, "class extends non-ref");
return refp->classp();
}
AstClass* AstClassExtends::classp() const {
AstClass* const clsp = classOrNullp();
UASSERT_OBJ(clsp, this, "Extended class is unresolved");
return clsp;
}
void AstClassRefDType::dump(std::ostream& str) const {
this->AstNodeDType::dump(str);
@@ -1669,10 +1626,25 @@ void AstLogOr::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (sideEffect()) str << " [SIDE]";
}
void AstMemberDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "member";
}
AstNodeUOrStructDType* AstMemberDType::getChildStructp() const {
AstNodeDType* subdtp = skipRefp();
while (AstNodeArrayDType* const asubdtp = VN_CAST(subdtp, NodeArrayDType)) {
subdtp = asubdtp->subDTypep();
}
return VN_CAST(subdtp, NodeUOrStructDType); // Maybe nullptr
}
bool AstMemberSel::same(const AstNode* samep) const {
const AstMemberSel* const sp = static_cast<const AstMemberSel*>(samep);
return sp != nullptr && access() == sp->access() && fromp()->same(sp->fromp())
&& name() == sp->name() && varp()->same(sp->varp());
}
void AstMemberSel::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
str << " -> ";
@@ -1829,6 +1801,7 @@ void AstNodeUOrStructDType::dump(std::ostream& str) const {
this->AstNodeDType::dump(str);
if (packed()) str << " [PACKED]";
if (isFourstate()) str << " [4STATE]";
if (classOrPackagep()) str << " pkg=" << nodeAddr(classOrPackagep());
}
void AstNodeDType::dump(std::ostream& str) const {
this->AstNode::dump(str);
@@ -2060,6 +2033,10 @@ void AstVoidDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "void";
}
void AstStreamDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "stream";
}
void AstVarScope::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (isTrace()) str << " [T]";
@@ -2071,6 +2048,10 @@ void AstVarScope::dump(std::ostream& str) const {
str << " ->UNLINKED";
}
}
bool AstVarScope::same(const AstNode* samep) const {
const AstVarScope* const asamep = static_cast<const AstVarScope*>(samep);
return varp()->same(asamep->varp()) && scopep()->same(asamep->scopep());
}
void AstNodeVarRef::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (classOrPackagep()) str << " pkg=" << nodeAddr(classOrPackagep());
@@ -2098,6 +2079,10 @@ void AstVarRef::dump(std::ostream& str) const {
str << "UNLINKED";
}
}
const char* AstVarRef::broken() const {
BROKEN_RTN(!varp());
return AstNodeVarRef::broken();
}
bool AstVarRef::same(const AstNode* samep) const {
return same(static_cast<const AstVarRef*>(samep));
}
@@ -2134,12 +2119,23 @@ void AstVar::dump(std::ostream& str) const {
if (!lifetime().isNone()) str << " [" << lifetime().ascii() << "] ";
str << " " << varType();
}
bool AstVar::same(const AstNode* samep) const {
const AstVar* const asamep = static_cast<const AstVar*>(samep);
return name() == asamep->name() && varType() == asamep->varType();
}
void AstScope::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " [abovep=" << reinterpret_cast<const void*>(aboveScopep()) << "]";
str << " [cellp=" << reinterpret_cast<const void*>(aboveCellp()) << "]";
str << " [modp=" << reinterpret_cast<const void*>(modp()) << "]";
}
bool AstScope::same(const AstNode* samep) const {
const AstScope* const asamep = static_cast<const AstScope*>(samep);
return name() == asamep->name()
&& ((!aboveScopep() && !asamep->aboveScopep())
|| (aboveScopep() && asamep->aboveScopep()
&& aboveScopep()->name() == asamep->aboveScopep()->name()));
}
void AstScopeName::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (dpiExport()) str << " [DPIEX]";
@@ -2291,7 +2287,7 @@ void AstCFile::dump(std::ostream& str) const {
void AstCFunc::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (slow()) str << " [SLOW]";
if (pure()) str << " [PURE]";
if (dpiPure()) str << " [DPIPURE]";
if (isStatic()) str << " [STATIC]";
if (dpiExportDispatcher()) str << " [DPIED]";
if (dpiExportImpl()) str << " [DPIEI]";
@@ -2302,6 +2298,7 @@ void AstCFunc::dump(std::ostream& str) const {
if (isDestructor()) str << " [DTOR]";
if (isVirtual()) str << " [VIRT]";
if (isCoroutine()) str << " [CORO]";
if (needProcess()) str << " [NPRC]";
}
const char* AstCAwait::broken() const {
BROKEN_RTN(m_sensesp && !m_sensesp->brokeExists());
@@ -2320,7 +2317,7 @@ void AstCAwait::dump(std::ostream& str) const {
int AstCMethodHard::instrCount() const {
if (AstBasicDType* const basicp = fromp()->dtypep()->basicp()) {
// TODO: add a more structured description of library methods, rather than using string
// matching. See #3715.
// matching. See issue #3715.
if (basicp->isTriggerVec() && m_name == "word") {
// This is an important special case for scheduling so we compute it precisely,
// it is simply a load.
@@ -2329,6 +2326,74 @@ int AstCMethodHard::instrCount() const {
}
return 0;
}
void AstCMethodHard::setPurity() {
static const std::map<std::string, bool> isPureMethod{{"andNot", false},
{"any", true},
{"assign", false},
{"at", true},
{"atBack", true},
{"awaitingCurrentTime", true},
{"clear", false},
{"clearFired", false},
{"commit", false},
{"delay", false},
{"done", false},
{"erase", false},
{"evaluate", false},
{"evaluation", false},
{"exists", true},
{"find", true},
{"find_first", true},
{"find_first_index", true},
{"find_index", true},
{"find_last", true},
{"find_last_index", true},
{"fire", false},
{"first", false},
{"init", false},
{"insert", false},
{"isFired", true},
{"isTriggered", true},
{"join", false},
{"last", false},
{"max", true},
{"min", true},
{"neq", true},
{"next", false},
{"pop", false},
{"pop_back", false},
{"pop_front", false},
{"prev", false},
{"push", false},
{"push_back", false},
{"push_front", false},
{"r_and", true},
{"r_or", true},
{"r_product", true},
{"r_sum", true},
{"r_xor", true},
{"renew", false},
{"renew_copy", false},
{"resume", false},
{"reverse", false},
{"rsort", false},
{"set", false},
{"shuffle", false},
{"size", true},
{"slice", true},
{"sliceBackBack", true},
{"sliceFrontBack", true},
{"sort", false},
{"thisOr", false},
{"trigger", false},
{"unique", true},
{"unique_index", true},
{"word", true}};
auto isPureIt = isPureMethod.find(name());
UASSERT_OBJ(isPureIt != isPureMethod.end(), this, "Unknown purity of method " + name());
m_pure = isPureIt->second;
}
const char* AstCFunc::broken() const {
BROKEN_RTN((m_scopep && !m_scopep->brokeExists()));
return nullptr;
+8 -26
View File
@@ -72,12 +72,12 @@ private:
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
int m_ifDepth = 0; // Current if depth
bool m_keepBegins = false; // True if begins should not be inlined
std::set<AstVar*> m_staticFuncVars; // Static variables from m_ftaskp
// METHODS
string dot(const string& a, const string& b) {
if (a == "") return b;
if (b == "") return a;
return a + "__DOT__" + b;
}
@@ -123,25 +123,6 @@ private:
}
}
void renameAndStaticsRecurse(AstNode* const nodep) {
// Rename references and move InitialStatic items
if (AstVarRef* const varrefp = VN_CAST(nodep, VarRef)) {
const auto it = m_staticFuncVars.find(varrefp->varp());
if (it != m_staticFuncVars.end()) varrefp->name((*it)->name());
}
if (nodep->op1p()) renameAndStaticsRecurse(nodep->op1p());
if (nodep->op2p()) renameAndStaticsRecurse(nodep->op2p());
if (nodep->op3p()) renameAndStaticsRecurse(nodep->op3p());
if (nodep->op4p()) renameAndStaticsRecurse(nodep->op4p());
if (nodep->nextp()) renameAndStaticsRecurse(nodep->nextp());
if (VN_IS(nodep, InitialStatic)) {
nodep->unlinkFrBack();
m_ftaskp->addHereThisAsNext(nodep);
}
}
// VISITORS
void visit(AstFork* nodep) override {
// Keep begins in forks to group their statements together
@@ -197,7 +178,10 @@ private:
m_ftaskp = nodep;
m_liftedp = nullptr;
iterateChildren(nodep);
renameAndStaticsRecurse(nodep);
nodep->foreach([&](AstInitialStatic* const initp) {
initp->unlinkFrBack();
m_ftaskp->addHereThisAsNext(initp);
});
if (m_liftedp) {
// Place lifted nodes at beginning of stmtsp, so Var nodes appear before referenced
if (AstNode* const stmtsp = nodep->stmtsp()) {
@@ -207,7 +191,6 @@ private:
nodep->addStmtsp(m_liftedp);
m_liftedp = nullptr;
}
m_staticFuncVars.clear();
m_ftaskp = nullptr;
}
}
@@ -246,11 +229,11 @@ private:
void visit(AstVar* nodep) override {
// If static variable, move it outside a function.
if (nodep->lifetime().isStatic() && m_ftaskp) {
const std::string newName = m_ftaskp->name() + "__Vstatic__" + nodep->name();
const std::string newName
= m_ftaskp->name() + "__Vstatic__" + dot(m_unnamedScope, nodep->name());
nodep->name(newName);
nodep->unlinkFrBack();
m_ftaskp->addHereThisAsNext(nodep);
m_staticFuncVars.insert(nodep);
nodep->funcLocal(false);
} else if (m_unnamedScope != "") {
// Rename it
@@ -361,7 +344,6 @@ private:
void visit(AstVarRef* nodep) override {
if (nodep->varp()->user1()) { // It was converted
UINFO(9, " relinVarRef " << nodep << endl);
nodep->name(nodep->varp()->name());
}
iterateChildren(nodep);
}
@@ -392,5 +374,5 @@ void V3Begin::debeginAll(AstNetlist* nodep) {
{ BeginVisitor{nodep, &state}; }
if (state.anyFuncInBegin()) { BeginRelinkVisitor{nodep, &state}; }
} // Destruct before checking
V3Global::dumpCheckGlobalTree("begin", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("begin", 0, dumpTreeLevel() >= 3);
}
+4 -2
View File
@@ -45,10 +45,12 @@ private:
// AstFTask::user1() -> int. Number of references
const VNUser1InUse m_inuser1;
// STATE
// STATE - across all visitors
std::vector<AstCFunc*> m_cfuncsp; // List of all tasks
// STATE - for current visit position (use VL_RESTORER)
int m_likely = false; // Excuses for branch likely taken
int m_unlikely = false; // Excuses for branch likely not taken
std::vector<AstCFunc*> m_cfuncsp; // List of all tasks
// METHODS
+31 -12
View File
@@ -147,22 +147,25 @@ bool V3Broken::isLinkable(const AstNode* nodep) { return s_linkableTable.isLinka
// Check every node in tree
class BrokenCheckVisitor final : public VNVisitorConst {
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;
// STATE - across all visitors
// All local variables declared in current function
std::unordered_set<const AstVar*> m_localVars;
std::set<const AstVar*> m_localVars;
// Variable references in current function that do not reference an in-scope local
std::unordered_map<const AstVar*, const AstNodeVarRef*> m_suspectRefs;
std::map<const AstVar*, const AstNodeVarRef*> m_suspectRefs;
// Local variables declared in the scope of the current statement
std::vector<std::unordered_set<const AstVar*>> m_localsStack;
// STATE - for current visit position (use VL_RESTORER)
const AstCFunc* m_cfuncp = nullptr; // Current CFunc, if any
bool m_inScope = false; // Under AstScope
std::set<std::string> m_cFuncNames; // CFunc by name in current class/module
private:
// METHODS
static void checkWidthMin(const AstNode* nodep) {
UASSERT_OBJ(nodep->width() == nodep->widthMin()
|| v3Global.widthMinUsage() != VWidthMinUsage::MATCHES_WIDTH,
@@ -220,6 +223,7 @@ private:
}
return false;
}
// VISITORS
void visit(AstNodeAssign* nodep) override {
processAndIterate(nodep);
UASSERT_OBJ(!(v3Global.assertDTypesResolved() && nodep->brokeLhsMustBeLvalue()
@@ -235,10 +239,20 @@ private:
}
void visit(AstScope* nodep) override {
VL_RESTORER(m_inScope);
{
m_inScope = true;
processAndIterate(nodep);
}
m_inScope = true;
VL_RESTORER(m_cFuncNames);
m_cFuncNames.clear();
processAndIterate(nodep);
}
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_cFuncNames);
m_cFuncNames.clear();
processAndIterate(nodep);
}
void visit(AstNodeUOrStructDType* nodep) override {
VL_RESTORER(m_cFuncNames);
m_cFuncNames.clear();
processAndIterate(nodep);
}
void visit(AstNodeVarRef* nodep) override {
processAndIterate(nodep);
@@ -260,12 +274,19 @@ private:
}
void visit(AstCFunc* nodep) override {
UASSERT_OBJ(!m_cfuncp, nodep, "Nested AstCFunc");
VL_RESTORER(m_cfuncp);
m_cfuncp = nodep;
m_localVars.clear();
m_suspectRefs.clear();
m_localsStack.clear();
pushLocalScope();
// Check for duplicate names, otherwise linker might just ignore them
string nameArgs = nodep->name();
if (!nodep->argTypes().empty()) nameArgs += "(" + nodep->argTypes() + ")";
UASSERT_OBJ(m_cFuncNames.emplace(nameArgs).second, nodep,
"Duplicate cfunc name: '" << nameArgs << "'");
processAndIterate(nodep);
// Check suspect references are all to non-locals
@@ -273,8 +294,6 @@ private:
UASSERT_OBJ(m_localVars.count(pair.first) == 0, pair.second,
"Local variable not in scope where referenced: " << pair.first);
}
m_cfuncp = nullptr;
}
void visit(AstNodeIf* nodep) override {
// Each branch is a separate local variable scope
+3 -3
View File
@@ -117,7 +117,7 @@ public:
callp->argTypes("vlSymsp");
} else {
if (m_type.isCoverage()) callp->argTypes("first");
callp->selfPointer("this");
callp->selfPointer(VSelfPointerText{VSelfPointerText::This()});
}
rootFuncp->addStmtsp(callp->makeStmt());
}
@@ -229,7 +229,7 @@ void V3CCtors::evalAsserts() {
// if (signal & CONST(upper_non_clean_mask)) { fail; }
AstVarRef* const vrefp
= new AstVarRef{varp->fileline(), varp, VAccess::READ};
vrefp->selfPointer("this");
vrefp->selfPointer(VSelfPointerText{VSelfPointerText::This()});
AstNodeExpr* newp = vrefp;
if (varp->isWide()) {
newp = new AstWordSel{
@@ -257,5 +257,5 @@ void V3CCtors::cctorsAll() {
UINFO(2, __FUNCTION__ << ": " << endl);
evalAsserts();
{ CCtorsVisitor{v3Global.rootp()}; }
V3Global::dumpCheckGlobalTree("cctors", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("cctors", 0, dumpTreeLevel() >= 3);
}
+28 -18
View File
@@ -28,9 +28,11 @@
#include "V3CUse.h"
#include "V3Ast.h"
#include "V3FileLine.h"
#include "V3Global.h"
#include <set>
#include <map>
#include <utility>
VL_DEFINE_DEBUG_FUNCTIONS;
@@ -45,32 +47,32 @@ class CUseVisitor final : public VNVisitor {
const VNUser1InUse m_inuser1;
// MEMBERS
bool m_impOnly = false; // In details needed only for implementation
AstNodeModule* const m_modp; // Current module
std::set<std::pair<VUseType, std::string>> m_didUse; // What we already used
std::map<std::string, std::pair<FileLine*, VUseType>> m_didUse; // What we already used
// METHODS
void addNewUse(AstNode* nodep, VUseType useType, const string& name) {
if (m_didUse.emplace(useType, name).second) {
AstCUse* const newp = new AstCUse{nodep->fileline(), useType, name};
m_modp->addStmtsp(newp);
UINFO(8, "Insert " << newp << endl);
auto e = m_didUse.emplace(name, std::make_pair(nodep->fileline(), useType));
if (e.second || ((e.first->second.second & useType) != useType)) {
e.first->second.second = e.first->second.second | useType;
}
}
// VISITORS
void visit(AstClassRefDType* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
if (!m_impOnly) addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
// Need to include extends() when we implement, but no need for pointers to know
VL_RESTORER(m_impOnly);
{
m_impOnly = true;
iterateChildren(nodep->classp()); // This also gets all extend classes
}
addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
}
void visit(AstCFunc* nodep) override {
if (nodep->user1SetOnce()) return;
iterateAndNextNull(nodep->argsp());
iterateAndNextNull(nodep->stmtsp());
}
void visit(AstCCall* nodep) override { return; }
void visit(AstCReturn* nodep) override {
UASSERT(!nodep->user1SetOnce(), "Visited same return twice.");
iterate(nodep->lhsp()->dtypep());
}
void visit(AstNodeDType* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
if (nodep->virtRefDTypep()) iterate(nodep->virtRefDTypep());
if (nodep->virtRefDType2p()) iterate(nodep->virtRefDType2p());
@@ -79,11 +81,13 @@ class CUseVisitor final : public VNVisitor {
if (stypep && stypep->classOrPackagep()) {
addNewUse(nodep, VUseType::INT_INCLUDE, stypep->classOrPackagep()->name());
iterateChildren(stypep);
} else if (AstClassRefDType* const classp = VN_CAST(nodep->skipRefp(), ClassRefDType)) {
addNewUse(nodep, VUseType::INT_FWD_CLASS, classp->name());
}
}
void visit(AstNode* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
if (nodep->dtypep() && !nodep->dtypep()->user1()) iterate(nodep->dtypep());
if (nodep->dtypep()) iterate(nodep->dtypep());
iterateChildren(nodep);
}
void visit(AstCell* nodep) override {
@@ -98,6 +102,12 @@ public:
explicit CUseVisitor(AstNodeModule* modp)
: m_modp(modp) {
iterate(modp);
for (auto& used : m_didUse) {
AstCUse* const newp = new AstCUse{used.second.first, used.second.second, used.first};
m_modp->addStmtsp(newp);
UINFO(8, "Insert " << newp << endl);
}
}
~CUseVisitor() override = default;
VL_UNCOPYABLE(CUseVisitor);
@@ -115,5 +125,5 @@ void V3CUse::cUseAll() {
// for each output file and put under that
CUseVisitor{modp};
}
V3Global::dumpCheckGlobalTree("cuse", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("cuse", 0, dumpTreeLevel() >= 3);
}
+84 -41
View File
@@ -139,32 +139,42 @@ private:
std::array<AstNode*, 1 << CASE_OVERLAP_WIDTH> m_valueItem;
// METHODS
bool caseIsEnumComplete(AstCase* nodep, uint32_t numCases) {
// Return true if case is across an enum, and every value in the case
// statement corresponds to one of the enum values
if (!nodep->uniquePragma() && !nodep->unique0Pragma()) return false;
AstEnumDType* const enumDtp
//! Determine whether we should check case items are complete
//! @return Enum's dtype if should check, nullptr if shouldn't
const AstEnumDType* getEnumCompletionCheckDType(const AstCase* const nodep) {
if (!nodep->uniquePragma() && !nodep->unique0Pragma()) return nullptr;
const AstEnumDType* const enumDtp
= VN_CAST(nodep->exprp()->dtypep()->skipRefToEnump(), EnumDType);
if (!enumDtp) return false; // Case isn't enum
AstBasicDType* const basicp = enumDtp->subDTypep()->basicp();
if (!basicp) return false; // Not simple type (perhaps IEEE illegal)
if (basicp->width() > 32) return false;
// Find all case values into a set
std::set<uint32_t> caseSet;
for (uint32_t i = 0; i < numCases; ++i) { // All case items
if (m_valueItem[i]) caseSet.emplace(i);
}
// Find all enum values into a set
std::set<uint32_t> enumSet;
for (AstEnumItem* itemp = enumDtp->itemsp(); itemp;
if (!enumDtp) return nullptr; // Case isn't enum
const AstBasicDType* const basicp = enumDtp->subDTypep()->basicp();
if (!basicp) return nullptr; // Not simple type (perhaps IEEE illegal)
if (basicp->width() > 32) return nullptr;
return enumDtp;
}
//! @return True if case items are complete, false if there are uncovered enums
bool checkCaseEnumComplete(const AstCase* const nodep, const AstEnumDType* const dtype) {
const uint32_t numCases = 1UL << m_caseWidth;
for (AstEnumItem* itemp = dtype->itemsp(); itemp;
itemp = VN_AS(itemp->nextp(), EnumItem)) {
AstConst* const econstp = VN_AS(itemp->valuep(), Const);
const uint32_t val = econstp->toUInt();
// UINFO(9, "Complete enum item " << val << ": " << itemp << endl);
enumSet.emplace(val);
V3Number nummask{itemp, econstp->width()};
nummask.opBitsNonX(econstp->num());
const uint32_t mask = nummask.toUInt();
V3Number numval{itemp, econstp->width()};
numval.opBitsOne(econstp->num());
const uint32_t val = numval.toUInt();
for (uint32_t i = 0; i < numCases; ++i) {
if ((i & mask) == val) {
if (!m_valueItem[i]) {
nodep->v3warn(CASEINCOMPLETE, "Enum item " << itemp->prettyNameQ()
<< " not covered by case\n");
return false; // enum has uncovered value by case items
}
}
}
}
// If sets match, all covered
return (caseSet == enumSet);
return true; // enum is fully covered
}
bool isCaseTreeFast(AstCase* nodep) {
int width = 0;
@@ -193,6 +203,8 @@ private:
// We can cheat and use uint32_t's because we only support narrow case's
bool reportedOverlap = false;
bool reportedSubcase = false;
bool hasDefaultCase = false;
std::map<AstNode*, AstCaseItem*> caseItemMap; // case condition -> case item
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
itemp = VN_AS(itemp->nextp(), CaseItem)) {
for (AstNode* icondp = itemp->condsp(); icondp; icondp = icondp->nextp()) {
@@ -202,6 +214,7 @@ private:
if (neverItem(nodep, iconstp)) {
// X in casez can't ever be executed
} else {
const bool isCondWildcard = iconstp->num().isAnyXZ();
V3Number nummask{itemp, iconstp->width()};
nummask.opBitsNonX(iconstp->num());
const uint32_t mask = nummask.toUInt();
@@ -210,16 +223,17 @@ private:
const uint32_t val = numval.toUInt();
uint32_t firstOverlap = 0;
bool foundOverlap = false;
AstNode* overlappedCondp = nullptr;
bool foundHit = false;
for (uint32_t i = 0; i < numCases; ++i) {
if ((i & mask) == val) {
if (!m_valueItem[i]) {
m_valueItem[i] = itemp;
m_valueItem[i] = icondp;
caseItemMap[icondp] = itemp;
foundHit = true;
} else if (!foundOverlap) {
} else if (!overlappedCondp) {
firstOverlap = i;
foundOverlap = true;
overlappedCondp = m_valueItem[i];
m_caseNoOverlapsAllCovered = false;
}
}
@@ -227,9 +241,19 @@ private:
if (!nodep->priorityPragma()) {
// If this case statement doesn't have the priority
// keyword, we want to warn on any overlap.
if (!reportedOverlap && foundOverlap) {
icondp->v3warn(CASEOVERLAP, "Case values overlap (example pattern 0x"
<< std::hex << firstOverlap << ")");
if (!reportedOverlap && overlappedCondp) {
std::ostringstream examplePattern;
if (isCondWildcard) {
examplePattern << " (example pattern 0x" << std::hex
<< firstOverlap << ")";
}
icondp->v3warn(CASEOVERLAP,
"Case conditions overlap"
<< examplePattern.str() << "\n"
<< icondp->warnContextPrimary() << '\n'
<< overlappedCondp->warnOther()
<< "... Location of overlapping condition\n"
<< overlappedCondp->warnContextSecondary());
reportedOverlap = true;
}
} else {
@@ -240,7 +264,11 @@ private:
if (!reportedSubcase && !foundHit) {
icondp->v3warn(CASEOVERLAP,
"Case item ignored: every matching value is covered "
"by an earlier item");
"by an earlier condition\n"
<< icondp->warnContextPrimary() << '\n'
<< overlappedCondp->warnOther()
<< "... Location of previous condition\n"
<< overlappedCondp->warnContextPrimary());
reportedSubcase = true;
}
}
@@ -251,17 +279,28 @@ private:
for (uint32_t i = 0; i < numCases; ++i) {
if (!m_valueItem[i]) m_valueItem[i] = itemp;
}
caseItemMap[itemp] = itemp;
hasDefaultCase = true;
}
}
if (!caseIsEnumComplete(nodep, numCases)) {
for (uint32_t i = 0; i < numCases; ++i) {
if (!m_valueItem[i]) {
nodep->v3warn(CASEINCOMPLETE, "Case values incompletely covered "
"(example pattern 0x"
<< std::hex << i << ")");
if (!hasDefaultCase) {
const AstEnumDType* const dtype = getEnumCompletionCheckDType(nodep);
if (dtype) {
if (!checkCaseEnumComplete(nodep, dtype)) {
// checkCaseEnumComplete has already warned of incompletion
m_caseNoOverlapsAllCovered = false;
return false;
}
} else {
for (uint32_t i = 0; i < numCases; ++i) {
if (!m_valueItem[i]) { // has uncovered case
nodep->v3warn(CASEINCOMPLETE, "Case values incompletely covered "
"(example pattern 0x"
<< std::hex << i << ")");
m_caseNoOverlapsAllCovered = false;
return false;
}
}
}
}
@@ -274,8 +313,10 @@ private:
// Convert valueItem from AstCaseItem* to the expression
// Not done earlier, as we may now have a nullptr because it's just a ";" NOP branch
for (uint32_t i = 0; i < numCases; ++i) {
if (AstCaseItem* const itemp = VN_AS(m_valueItem[i], CaseItem)) {
m_valueItem[i] = itemp->stmtsp();
if (AstNode* const condp = m_valueItem[i]) {
AstCaseItem* caseItemp = caseItemMap[condp];
UASSERT(caseItemp, "caseItemp should exist");
m_valueItem[i] = caseItemp->stmtsp();
}
}
return true; // All is fine
@@ -543,10 +584,12 @@ private:
}
}
//--------------------
void visit(AstNode* nodep) override {
if (VN_IS(nodep, Always)) m_alwaysp = nodep;
void visit(AstAlways* nodep) override {
VL_RESTORER(m_alwaysp)
m_alwaysp = nodep;
iterateChildren(nodep);
}
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
@@ -566,7 +609,7 @@ public:
void V3Case::caseAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CaseVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("case", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("case", 0, dumpTreeLevel() >= 3);
}
void V3Case::caseLint(AstNodeCase* nodep) {
UINFO(4, __FUNCTION__ << ": " << endl);
+28 -2
View File
@@ -122,6 +122,31 @@ private:
if (nodep->sizeMattersLhs()) ensureCast(nodep->lhsp());
if (nodep->sizeMattersRhs()) ensureCast(nodep->rhsp());
}
void visit(AstNodeCond* nodep) override {
// All class types are castable to each other. If they are of different types,
// a compilation error will be thrown, so an explicit cast is required. Types were
// already checked by V3Width and dtypep of a condition operator is a type of their
// common base class, so both classes can be safetly casted.
const AstClassRefDType* const thenClassDtypep
= VN_CAST(nodep->thenp()->dtypep(), ClassRefDType);
const AstClassRefDType* const elseClassDtypep
= VN_CAST(nodep->elsep()->dtypep(), ClassRefDType);
const bool castRequired = thenClassDtypep && elseClassDtypep
&& (thenClassDtypep->classp() != elseClassDtypep->classp());
if (castRequired) {
const AstClass* const commonBaseClassp
= VN_AS(nodep->dtypep(), ClassRefDType)->classp();
if (thenClassDtypep->classp() != commonBaseClassp) {
AstNodeExpr* thenp = nodep->thenp()->unlinkFrBack();
nodep->thenp(new AstCCast{thenp->fileline(), thenp, nodep});
}
if (elseClassDtypep->classp() != commonBaseClassp) {
AstNodeExpr* elsep = nodep->elsep()->unlinkFrBack();
nodep->elsep(new AstCCast{elsep->fileline(), elsep, nodep});
}
}
visit(static_cast<AstNodeTriop*>(nodep));
}
void visit(AstNodeTriop* nodep) override {
iterateChildren(nodep);
nodep->user1(nodep->lhsp()->user1() | nodep->rhsp()->user1() | nodep->thsp()->user1());
@@ -161,7 +186,8 @@ private:
&& !VN_IS(backp, NodeCCall) && !VN_IS(backp, CMethodHard) && !VN_IS(backp, SFormatF)
&& !VN_IS(backp, ArraySel) && !VN_IS(backp, StructSel) && !VN_IS(backp, RedXor)
&& (nodep->varp()->basicp() && !nodep->varp()->basicp()->isTriggerVec()
&& !nodep->varp()->basicp()->isForkSync())
&& !nodep->varp()->basicp()->isForkSync()
&& !nodep->varp()->basicp()->isProcessRef())
&& backp->width() && castSize(nodep) != castSize(nodep->varp())) {
// Cast vars to IData first, else below has upper bits wrongly set
// CData x=3; out = (QData)(x<<30);
@@ -208,5 +234,5 @@ public:
void V3Cast::castAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CastVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("cast", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("cast", 0, dumpTreeLevel() >= 3);
}
+10 -6
View File
@@ -27,6 +27,7 @@
#include "V3Ast.h"
#include "V3Global.h"
#include "V3UniqueNames.h"
VL_DEFINE_DEBUG_FUNCTIONS;
@@ -41,6 +42,7 @@ private:
// MEMBERS
string m_prefix; // String prefix to add to name based on hier
V3UniqueNames m_names; // For unique naming of structs and unions
AstNodeModule* m_modp = nullptr; // Current module
AstNodeModule* m_classPackagep = nullptr; // Package moving into
const AstScope* m_classScopep = nullptr; // Package moving scopes into
@@ -62,8 +64,10 @@ private:
AstClassPackage* const packagep
= new AstClassPackage{nodep->fileline(), nodep->origName()};
packagep->name(nodep->name() + "__Vclpkg");
nodep->editCountInc();
nodep->classOrPackagep(packagep);
packagep->classp(nodep);
packagep->timeunit(nodep->timeunit());
v3Global.rootp()->addModulesp(packagep);
// Add package to hierarchy
AstCell* const cellp = new AstCell{packagep->fileline(),
@@ -102,7 +106,6 @@ private:
m_prefix = nodep->name() + "__02e"; // .
iterateChildren(nodep);
}
nodep->repairCache();
}
void visit(AstNodeModule* nodep) override {
// Visit for NodeModules that are not AstClass (AstClass is-a AstNodeModule)
@@ -177,14 +180,15 @@ private:
}
void setStructModulep(AstNodeUOrStructDType* const dtypep) {
// Give it a pointer to its package and a final name
// Give struct a pointer to its package and a final name
dtypep->editCountInc();
dtypep->classOrPackagep(m_classPackagep ? m_classPackagep : m_modp);
dtypep->name(dtypep->name() + (VN_IS(dtypep, UnionDType) ? "__union" : "__struct")
+ cvtToStr(dtypep->uniqueNum()));
dtypep->name(
m_names.get(dtypep->name() + (VN_IS(dtypep, UnionDType) ? "__union" : "__struct")));
for (const AstMemberDType* itemp = dtypep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
AstNodeUOrStructDType* const subp = VN_CAST(itemp->skipRefp(), NodeUOrStructDType);
AstNodeUOrStructDType* const subp = itemp->getChildStructp();
// Recurse only into anonymous unpacked structs inside this definition,
// other unpacked structs will be reached from another typedefs
if (subp && !subp->packed() && subp->name().empty()) setStructModulep(subp);
@@ -244,5 +248,5 @@ public:
void V3Class::classAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ClassVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("class", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("class", 0, dumpTreeLevel() >= 3);
}
+7 -2
View File
@@ -95,6 +95,7 @@ private:
|| VN_IS(nodep->dtypep()->skipRefp(), DynArrayDType)
|| VN_IS(nodep->dtypep()->skipRefp(), ClassRefDType)
|| VN_IS(nodep->dtypep()->skipRefp(), QueueDType)
|| VN_IS(nodep->dtypep()->skipRefp(), StreamDType)
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)
|| VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) {
} else {
@@ -298,7 +299,11 @@ private:
}
void visit(AstWith* nodep) override {
iterateChildren(nodep);
ensureCleanAndNext(nodep->exprp());
setClean(nodep, true);
}
void visit(AstCReturn* nodep) override {
iterateChildren(nodep);
ensureClean(nodep->lhsp());
setClean(nodep, true);
}
void visit(AstIntfRef* nodep) override {
@@ -325,5 +330,5 @@ public:
void V3Clean::cleanAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CleanVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("clean", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("clean", 0, dumpTreeLevel() >= 3);
}
+1 -1
View File
@@ -224,5 +224,5 @@ public:
void V3Clock::clockAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ClockVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("clock", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("clock", 0, dumpTreeLevel() >= 3);
}
+1 -1
View File
@@ -238,5 +238,5 @@ public:
void V3Combine::combineAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
CombineVisitor::apply(nodep);
V3Global::dumpCheckGlobalTree("combine", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("combine", 0, dumpTreeLevel() >= 3);
}
+7 -4
View File
@@ -63,6 +63,7 @@ static void makeVlToString(AstIface* nodep) {
}
static void makeVlToString(AstNodeUOrStructDType* nodep) {
AstNodeModule* const modp = nodep->classOrPackagep();
UASSERT_OBJ(modp, nodep, "Unlinked struct package");
AstCFunc* const funcp
= new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"};
funcp->argTypes("const " + EmitCBase::prefixNameProtect(nodep) + "& obj");
@@ -81,11 +82,13 @@ static void makeVlToString(AstNodeUOrStructDType* nodep) {
}
stmt += VIdProtect::protect(itemp->prettyName()) + ":\" + ";
if (VN_IS(itemp->dtypep()->skipRefp(), BasicDType) && itemp->isWide()) {
stmt += "VL_TO_STRING_W";
stmt += "VL_TO_STRING_W(";
stmt += cvtToStr(itemp->widthWords());
stmt += ", ";
} else {
stmt += "VL_TO_STRING";
stmt += "VL_TO_STRING(";
}
stmt += "(obj." + itemp->nameProtect() + ");\n";
stmt += "obj." + itemp->nameProtect() + ");\n";
funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt});
}
funcp->addStmtsp(new AstCStmt{nodep->fileline(), "out += \"}\";\n"});
@@ -172,5 +175,5 @@ void V3Common::commonAll() {
if (!dtypep->packed()) makeVlToString(dtypep);
}
}
V3Global::dumpCheckGlobalTree("common", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("common", 0, dumpTreeLevel() >= 3);
}
+109 -45
View File
@@ -1799,7 +1799,7 @@ private:
UASSERT_OBJ((rstart + rwidth) == lstart, nodep,
"tried to merge two selects which are not adjacent");
AstSel* const newselp = new AstSel{
lselp->fromp()->fileline(), rselp->fromp()->cloneTree(false), rstart, lwidth + rwidth};
lselp->fromp()->fileline(), rselp->fromp()->unlinkFrBack(), rstart, lwidth + rwidth};
UINFO(5, "merged two adjacent sel " << lselp << " and " << rselp << " to one " << newselp
<< endl);
@@ -1809,6 +1809,7 @@ private:
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
void replaceConcatMerge(AstConcat* nodep) {
// {llp OP lrp, rlp OP rrp} => {llp, rlp} OP {lrp, rrp}, where OP = AND/OR/XOR
AstNodeBiop* const lp = VN_AS(nodep->lhsp(), NodeBiop);
AstNodeBiop* const rp = VN_AS(nodep->rhsp(), NodeBiop);
AstNodeExpr* const llp = lp->lhsp()->cloneTree(false);
@@ -2052,7 +2053,7 @@ private:
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return true;
}
} else if (m_doV && VN_IS(nodep->lhsp(), Concat)) {
} else if (m_doV && VN_IS(nodep->lhsp(), Concat) && nodep->isTreePureRecurse()) {
bool need_temp = false;
if (m_warn && !VN_IS(nodep, AssignDly)) { // Is same var on LHS and RHS?
// Note only do this (need user4) when m_warn, which is
@@ -2145,33 +2146,42 @@ private:
return true;
} else if (m_doV && VN_IS(nodep->rhsp(), StreamR)) {
// The right-streaming operator on rhs of assignment does not
// change the order of bits. Eliminate stream but keep its lhsp
// Unlink the stuff
AstNodeExpr* const srcp = VN_AS(nodep->rhsp(), StreamR)->lhsp()->unlinkFrBack();
AstNode* const sizep = VN_AS(nodep->rhsp(), StreamR)->rhsp()->unlinkFrBack();
AstNodeExpr* const streamp = VN_AS(nodep->rhsp(), StreamR)->unlinkFrBack();
// change the order of bits. Eliminate stream but keep its lhsp.
// Add a cast if needed.
AstStreamR* const streamp = VN_AS(nodep->rhsp(), StreamR)->unlinkFrBack();
AstNodeExpr* srcp = streamp->lhsp()->unlinkFrBack();
AstNodeDType* const srcDTypep = srcp->dtypep();
if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) {
if (nodep->lhsp()->widthMin() > 64) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assignment of stream of dynamic "
"array to a variable of size greater than 64");
}
srcp = new AstCvtDynArrayToPacked{srcp->fileline(), srcp, srcDTypep};
}
nodep->rhsp(srcp);
// Cleanup
VL_DO_DANGLING(sizep->deleteTree(), sizep);
VL_DO_DANGLING(streamp->deleteTree(), streamp);
// Further reduce, any of the nodes may have more reductions.
return true;
} else if (m_doV && VN_IS(nodep->lhsp(), StreamL)) {
// Push the stream operator to the rhs of the assignment statement
const int dWidth = VN_AS(nodep->lhsp(), StreamL)->lhsp()->width();
const int sWidth = nodep->rhsp()->width();
// Unlink the stuff
AstNodeExpr* const dstp = VN_AS(nodep->lhsp(), StreamL)->lhsp()->unlinkFrBack();
AstNodeExpr* streamp = VN_AS(nodep->lhsp(), StreamL)->unlinkFrBack();
AstNodeExpr* streamp = nodep->lhsp()->unlinkFrBack();
AstNodeExpr* const dstp = VN_AS(streamp, StreamL)->lhsp()->unlinkFrBack();
AstNodeExpr* const srcp = nodep->rhsp()->unlinkFrBack();
const int sWidth = srcp->width();
const int dWidth = dstp->width();
// Connect the rhs to the stream operator and update its width
VN_AS(streamp, StreamL)->lhsp(srcp);
streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED);
// Shrink the RHS if necessary
if (sWidth > dWidth) {
if (VN_IS(srcp->dtypep(), DynArrayDType) || VN_IS(srcp->dtypep(), QueueDType)
|| VN_IS(srcp->dtypep(), UnpackArrayDType)) {
streamp->dtypeSetStream();
} else {
streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED);
}
if (dWidth == 0) {
streamp = new AstCvtPackedToDynArray{nodep->fileline(), streamp, dstp->dtypep()};
} else if (sWidth > dWidth) {
streamp = new AstSel{streamp->fileline(), streamp, sWidth - dWidth, dWidth};
}
// Link the nodes back in
nodep->lhsp(dstp);
nodep->rhsp(streamp);
return true;
@@ -2179,23 +2189,35 @@ private:
// The right stream operator on lhs of assignment statement does
// not reorder bits. However, if the rhs is wider than the lhs,
// then we select bits from the left-most, not the right-most.
const int dWidth = VN_AS(nodep->lhsp(), StreamR)->lhsp()->width();
const int sWidth = nodep->rhsp()->width();
// Unlink the stuff
AstNodeExpr* const dstp = VN_AS(nodep->lhsp(), StreamR)->lhsp()->unlinkFrBack();
AstNode* const sizep = VN_AS(nodep->lhsp(), StreamR)->rhsp()->unlinkFrBack();
AstNodeExpr* const streamp = VN_AS(nodep->lhsp(), StreamR)->unlinkFrBack();
AstNodeExpr* const streamp = nodep->lhsp()->unlinkFrBack();
AstNodeExpr* const dstp = VN_AS(streamp, StreamR)->lhsp()->unlinkFrBack();
AstNodeExpr* srcp = nodep->rhsp()->unlinkFrBack();
if (sWidth > dWidth) {
const int sWidth = srcp->width();
const int dWidth = dstp->width();
if (dWidth == 0) {
srcp = new AstCvtPackedToDynArray{nodep->fileline(), srcp, dstp->dtypep()};
} else if (sWidth > dWidth) {
srcp = new AstSel{streamp->fileline(), srcp, sWidth - dWidth, dWidth};
}
nodep->lhsp(dstp);
nodep->rhsp(srcp);
// Cleanup
VL_DO_DANGLING(sizep->deleteTree(), sizep);
VL_DO_DANGLING(streamp->deleteTree(), streamp);
// Further reduce, any of the nodes may have more reductions.
return true;
} else if (m_doV && VN_IS(nodep->rhsp(), StreamL)) {
AstNodeDType* const lhsDtypep = nodep->lhsp()->dtypep();
AstStreamL* streamp = VN_AS(nodep->rhsp(), StreamL);
AstNodeExpr* const srcp = streamp->lhsp();
const AstNodeDType* const srcDTypep = srcp->dtypep();
if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) {
if (lhsDtypep->widthMin() > 64) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assignment of stream of dynamic "
"array to a variable of size greater than 64");
}
srcp->unlinkFrBack();
streamp->lhsp(new AstCvtDynArrayToPacked{srcp->fileline(), srcp, lhsDtypep});
streamp->dtypeFrom(lhsDtypep);
}
} else if (m_doV && replaceAssignMultiSel(nodep)) {
return true;
}
@@ -2283,6 +2305,13 @@ private:
iterateChildren(nodep);
}
}
void visit(AstCLocalScope* nodep) override {
iterateChildren(nodep);
if (!nodep->stmtsp()) {
nodep->unlinkFrBack();
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
}
void visit(AstScope* nodep) override {
// No ASSIGNW removals under scope, we've long eliminated INITIALs
VL_RESTORER(m_wremove);
@@ -3064,7 +3093,7 @@ private:
&& nodep->displayType() == VDisplayType::DT_WRITE)))
return false;
if ((prevp->filep() && !nodep->filep()) || (!prevp->filep() && nodep->filep())
|| !prevp->filep()->sameTree(nodep->filep()))
|| (prevp->filep() && nodep->filep() && !prevp->filep()->sameTree(nodep->filep())))
return false;
if (!prevp->fmtp() || prevp->fmtp()->nextp() || !nodep->fmtp() || nodep->fmtp()->nextp())
return false;
@@ -3175,8 +3204,17 @@ private:
iterateChildren(nodep);
}
void visit(AstFuncRef* nodep) override {
void visit(AstNodeCCall* nodep) override {
iterateChildren(nodep);
m_hasJumpDelay = true; // As don't analyze inside tasks for timing controls
}
void visit(AstNodeFTaskRef* nodep) override {
// Note excludes AstFuncRef as other visitor below
iterateChildren(nodep);
m_hasJumpDelay = true; // As don't analyze inside tasks for timing controls
}
void visit(AstFuncRef* nodep) override {
visit(static_cast<AstNodeFTaskRef*>(nodep));
if (m_params) { // Only parameters force us to do constant function call propagation
replaceWithSimulation(nodep);
}
@@ -3405,9 +3443,9 @@ private:
TREEOPS("AstCond {$lhsp.isNeqZero}", "replaceWIteratedRhs(nodep)");
TREEOP ("AstCond{$condp.castNot, $thenp, $elsep}", "AstCond{$condp->castNot()->lhsp(), $elsep, $thenp}");
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isAllOnes, $elsep}", "AstLogOr {$condp, $elsep}"); // a?1:b == a||b
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isZero}", "AstLogAnd{$condp, $thenp}"); // a?b:0 == a&&b
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isZero, !$elsep.isClassHandleValue}", "AstLogAnd{$condp, $thenp}"); // a?b:0 == a&&b
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isAllOnes}", "AstLogOr {AstNot{$condp}, $thenp}"); // a?b:1 == ~a||b
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isZero, $elsep}", "AstLogAnd{AstNot{$condp}, $elsep}"); // a?0:b == ~a&&b
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isZero, !$thenp.isClassHandleValue, $elsep}", "AstLogAnd{AstNot{$condp}, $elsep}"); // a?0:b == ~a&&b
TREEOP ("AstNodeCond{!$condp.width1, operandBoolShift(nodep->condp())}", "replaceBoolShift(nodep->condp())");
// Prefer constants on left, since that often needs a shift, it lets
// constant red remove the shift
@@ -3650,6 +3688,7 @@ private:
public:
// Processing Mode Enum
enum ProcMode : uint8_t {
PROC_PARAMS_NOWARN,
PROC_PARAMS,
PROC_GENERATE,
PROC_LIVE,
@@ -3665,16 +3704,18 @@ public:
, m_concswapNames{globalPass ? ("__Vconcswap_" + cvtToStr(s_globalPassNum++)) : ""} {
// clang-format off
switch (pmode) {
case PROC_PARAMS: m_doV = true; m_doNConst = true; m_params = true;
m_required = true; break;
case PROC_GENERATE: m_doV = true; m_doNConst = true; m_params = true;
m_required = true; m_doGenerate = true; break;
case PROC_LIVE: break;
case PROC_V_WARN: m_doV = true; m_doNConst = true; m_warn = true; break;
case PROC_V_NOWARN: m_doV = true; m_doNConst = true; break;
case PROC_V_EXPENSIVE: m_doV = true; m_doNConst = true; m_doExpensive = true; break;
case PROC_CPP: m_doV = false; m_doNConst = true; m_doCpp = true; break;
default: v3fatalSrc("Bad case"); break;
case PROC_PARAMS_NOWARN: m_doV = true; m_doNConst = true; m_params = true;
m_required = false; break;
case PROC_PARAMS: m_doV = true; m_doNConst = true; m_params = true;
m_required = true; break;
case PROC_GENERATE: m_doV = true; m_doNConst = true; m_params = true;
m_required = true; m_doGenerate = true; break;
case PROC_LIVE: break;
case PROC_V_WARN: m_doV = true; m_doNConst = true; m_warn = true; break;
case PROC_V_NOWARN: m_doV = true; m_doNConst = true; break;
case PROC_V_EXPENSIVE: m_doV = true; m_doNConst = true; m_doExpensive = true; break;
case PROC_CPP: m_doV = false; m_doNConst = true; m_doCpp = true; break;
default: v3fatalSrc("Bad case"); break;
}
// clang-format on
}
@@ -3722,6 +3763,29 @@ AstNode* V3Const::constifyParamsEdit(AstNode* nodep) {
return nodep;
}
//! Constify this cell node's parameter list if possible
//! @return Pointer to the edited node.
AstNode* V3Const::constifyParamsNoWarnEdit(AstNode* nodep) {
// if (debug() > 0) nodep->dumpTree("- forceConPRE : ");
// Resize even if the node already has a width, because buried in the tree
// we may have a node we just created with signing, etc, that isn't sized yet.
// Make sure we've sized everything first
nodep = V3Width::widthParamsEdit(nodep);
ConstVisitor visitor{ConstVisitor::PROC_PARAMS_NOWARN, /* globalPass: */ false};
if (AstVar* const varp = VN_CAST(nodep, Var)) {
// If a var wants to be constified, it's really a param, and
// we want the value to be constant. We aren't passed just the
// init value because we need widthing above to handle the var's type.
if (varp->valuep()) visitor.mainAcceptEdit(varp->valuep());
} else {
nodep = visitor.mainAcceptEdit(nodep);
}
// Because we do edits, nodep links may get trashed and core dump this.
// if (debug() > 0) nodep->dumpTree("- forceConDONE: ");
return nodep;
}
//! Force this cell node's parameter list to become a constant inside generate.
//! If we are inside a generated "if", "case" or "for", we don't want to
//! trigger warnings when we deal with the width. It is possible that these
@@ -3759,7 +3823,7 @@ void V3Const::constifyAllLint(AstNetlist* nodep) {
ConstVisitor visitor{ConstVisitor::PROC_V_WARN, /* globalPass: */ true};
(void)visitor.mainAcceptEdit(nodep);
} // Destruct before checking
V3Global::dumpCheckGlobalTree("const", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("const", 0, dumpTreeLevel() >= 3);
}
void V3Const::constifyCpp(AstNetlist* nodep) {
@@ -3768,7 +3832,7 @@ void V3Const::constifyCpp(AstNetlist* nodep) {
ConstVisitor visitor{ConstVisitor::PROC_CPP, /* globalPass: */ true};
(void)visitor.mainAcceptEdit(nodep);
} // Destruct before checking
V3Global::dumpCheckGlobalTree("const_cpp", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("const_cpp", 0, dumpTreeLevel() >= 3);
}
AstNode* V3Const::constifyEdit(AstNode* nodep) {
@@ -3792,7 +3856,7 @@ void V3Const::constifyAllLive(AstNetlist* nodep) {
ConstVisitor visitor{ConstVisitor::PROC_LIVE, /* globalPass: */ true};
(void)visitor.mainAcceptEdit(nodep);
} // Destruct before checking
V3Global::dumpCheckGlobalTree("const", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("const", 0, dumpTreeLevel() >= 3);
}
void V3Const::constifyAll(AstNetlist* nodep) {
@@ -3802,7 +3866,7 @@ void V3Const::constifyAll(AstNetlist* nodep) {
ConstVisitor visitor{ConstVisitor::PROC_V_EXPENSIVE, /* globalPass: */ true};
(void)visitor.mainAcceptEdit(nodep);
} // Destruct before checking
V3Global::dumpCheckGlobalTree("const", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("const", 0, dumpTreeLevel() >= 3);
}
AstNode* V3Const::constifyExpensiveEdit(AstNode* nodep) {
+4
View File
@@ -30,6 +30,10 @@ public:
static AstNodeExpr* constifyParamsEdit(AstNodeExpr* exprp) {
return VN_AS(constifyParamsEdit(static_cast<AstNode*>(exprp)), NodeExpr);
}
static AstNode* constifyParamsNoWarnEdit(AstNode* nodep);
static AstNodeExpr* constifyParamsNoWarnEdit(AstNodeExpr* exprp) {
return VN_AS(constifyParamsNoWarnEdit(static_cast<AstNode*>(exprp)), NodeExpr);
}
static AstNode* constifyGenerateParamsEdit(AstNode* nodep);
// Only do constant pushing, without removing dead logic
static void constifyAllLive(AstNetlist* nodep);
+1 -1
View File
@@ -567,5 +567,5 @@ public:
void V3Coverage::coverage(AstNetlist* rootp) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CoverageVisitor{rootp}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("coverage", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("coverage", 0, dumpTreeLevel() >= 3);
}
+1 -1
View File
@@ -117,5 +117,5 @@ public:
void V3CoverageJoin::coverageJoin(AstNetlist* rootp) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CoverageJoinVisitor{rootp}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("coveragejoin", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("coveragejoin", 0, dumpTreeLevel() >= 3);
}
+29 -25
View File
@@ -62,9 +62,10 @@ private:
// TYPES
using AssignMap = std::multimap<AstVarScope*, AstNodeAssign*>;
// STATE
AstNodeModule* m_modp = nullptr; // Current module
AstSelLoopVars* m_selloopvarsp = nullptr; // Current loop vars
// STATE - across all visitors
const bool m_elimUserVars; // Allow removal of user's vars
const bool m_elimDTypes; // Allow removal of DTypes
const bool m_elimCells; // Allow removal of Cells
// List of all encountered to avoid another loop through tree
std::vector<AstVar*> m_varsp;
std::vector<AstNode*> m_dtypesp;
@@ -73,13 +74,14 @@ private:
std::vector<AstCell*> m_cellsp;
std::vector<AstClass*> m_classesp;
std::vector<AstTypedef*> m_typedefsp;
AssignMap m_assignMap; // List of all simple assignments for each variable
const bool m_elimUserVars; // Allow removal of user's vars
const bool m_elimDTypes; // Allow removal of DTypes
const bool m_elimCells; // Allow removal of Cells
bool m_sideEffect = false; // Side effects discovered in assign RHS
// STATE - for current visit position (use VL_RESTORER)
bool m_inAssign = false; // Currently in an assign
AstNodeModule* m_modp = nullptr; // Current module
AstSelLoopVars* m_selloopvarsp = nullptr; // Current loop vars
// METHODS
void checkAll(AstNode* nodep) {
@@ -107,18 +109,16 @@ private:
void visit(AstNodeModule* nodep) override {
if (m_modp) m_modp->user1Inc(); // e.g. Class under Package
VL_RESTORER(m_modp);
{
m_modp = nodep;
if (!nodep->dead()) {
iterateChildren(nodep);
checkAll(nodep);
if (AstClass* const classp = VN_CAST(nodep, Class)) {
if (classp->extendsp()) classp->extendsp()->user1Inc();
if (classp->classOrPackagep()) classp->classOrPackagep()->user1Inc();
m_classesp.push_back(classp);
// TODO we don't reclaim dead classes yet - graph implementation instead?
classp->user1Inc();
}
m_modp = nodep;
if (!nodep->dead()) {
iterateChildren(nodep);
checkAll(nodep);
if (AstClass* const classp = VN_CAST(nodep, Class)) {
if (classp->extendsp()) classp->extendsp()->user1Inc();
if (classp->classOrPackagep()) classp->classOrPackagep()->user1Inc();
m_classesp.push_back(classp);
// TODO we don't reclaim dead classes yet - graph implementation instead?
classp->user1Inc();
}
}
}
@@ -282,8 +282,11 @@ private:
// See if simple assignments to variables may be eliminated because
// that variable is never used.
// Similar code in V3Life
VL_RESTORER(m_sideEffect);
const bool assignInAssign = m_inAssign; // Might be Assign(..., ExprStmt(Assign), ...)
{
VL_RESTORER(m_inAssign);
VL_RESTORER(m_sideEffect);
m_inAssign = true;
m_sideEffect = false;
iterateAndNextNull(nodep->rhsp());
checkAll(nodep);
@@ -299,6 +302,7 @@ private:
}
iterateNull(nodep->timingControlp());
}
if (assignInAssign) m_sideEffect = true; // Parent assign shouldn't optimize
}
//-----
@@ -539,29 +543,29 @@ void V3Dead::deadifyModules(AstNetlist* nodep) {
{
DeadVisitor{nodep, false, false, false, false, !v3Global.opt.topIfacesSupported()};
} // Destruct before checking
V3Global::dumpCheckGlobalTree("deadModules", 0, dumpTree() >= 6);
V3Global::dumpCheckGlobalTree("deadModules", 0, dumpTreeLevel() >= 6);
}
void V3Dead::deadifyDTypes(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor{nodep, false, true, false, false, false}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadDtypes", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("deadDtypes", 0, dumpTreeLevel() >= 3);
}
void V3Dead::deadifyDTypesScoped(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor{nodep, false, true, true, false, false}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0, dumpTreeLevel() >= 3);
}
void V3Dead::deadifyAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor{nodep, true, true, false, true, false}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadAll", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("deadAll", 0, dumpTreeLevel() >= 3);
}
void V3Dead::deadifyAllScoped(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor{nodep, true, true, true, true, false}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadAllScoped", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("deadAllScoped", 0, dumpTreeLevel() >= 3);
}
+28 -20
View File
@@ -92,21 +92,23 @@ private:
const VNUser4InUse m_inuser4;
const VNUser5InUse m_inuser5;
// STATE
// STATE - across all visitors
std::unordered_map<const AstVarScope*, int> m_scopeVecMap; // Next var number for each scope
std::set<AstSenTree*> m_timingDomains; // Timing resume domains
using VarMap = std::map<const std::pair<AstNodeModule*, std::string>, AstVar*>;
VarMap m_modVarMap; // Table of new var names created under module
VDouble0 m_statSharedSet; // Statistic tracking
// STATE - for current visit position (use VL_RESTORER)
AstActive* m_activep = nullptr; // Current activate
const AstCFunc* m_cfuncp = nullptr; // Current public C Function
AstAssignDly* m_nextDlyp = nullptr; // Next delayed assignment in a list of assignments
AstNodeProcedure* m_procp = nullptr; // Current process
std::set<AstSenTree*> m_timingDomains; // Timing resume domains
bool m_inDly = false; // True in delayed assignments
bool m_inLoop = false; // True in for loops
bool m_inInitial = false; // True in static initializers and initial blocks
bool m_inSuspendableOrFork = false; // True in suspendable processes and forks
bool m_ignoreBlkAndNBlk = false; // Suppress delayed assignment BLKANDNBLK
using VarMap = std::map<const std::pair<AstNodeModule*, std::string>, AstVar*>;
VarMap m_modVarMap; // Table of new var names created under module
VDouble0 m_statSharedSet; // Statistic tracking
std::unordered_map<const AstVarScope*, int> m_scopeVecMap; // Next var number for each scope
// METHODS
@@ -424,10 +426,8 @@ private:
}
void visit(AstCFunc* nodep) override {
VL_RESTORER(m_cfuncp);
{
m_cfuncp = nodep;
iterateChildren(nodep);
}
m_cfuncp = nodep;
iterateChildren(nodep);
}
void visit(AstActive* nodep) override {
m_activep = nodep;
@@ -443,10 +443,12 @@ private:
void visit(AstNodeProcedure* nodep) override {
VL_RESTORER(m_inSuspendableOrFork);
m_inSuspendableOrFork = nodep->isSuspendable();
m_procp = nodep;
m_timingDomains.clear();
iterateChildren(nodep);
m_procp = nullptr;
{
VL_RESTORER(m_procp);
m_procp = nodep;
m_timingDomains.clear();
iterateChildren(nodep);
}
if (m_timingDomains.empty()) return;
if (auto* const actp = VN_AS(nodep->user3p(), Active)) {
// Merge all timing domains (and possibly the active's domain) to create a sentree for
@@ -549,7 +551,9 @@ private:
}
if (!lhsp->backp()) VL_DO_DANGLING(pushDeletep(lhsp), lhsp);
} else {
iterateChildren(nodep);
iterate(nodep->lhsp());
m_inDly = false;
iterate(nodep->rhsp());
}
}
@@ -618,10 +622,14 @@ private:
}
void visit(AstWhile* nodep) override {
VL_RESTORER(m_inLoop);
{
m_inLoop = true;
iterateChildren(nodep);
}
m_inLoop = true;
iterateChildren(nodep);
}
void visit(AstExprStmt* nodep) override {
VL_RESTORER(m_inDly);
// Restoring is needed, because AstExprStmt may contain assignments
m_inDly = false;
iterateChildren(nodep);
}
//--------------------
@@ -641,5 +649,5 @@ public:
void V3Delayed::delayedAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DelayedVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("delayed", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("delayed", 0, dumpTreeLevel() >= 3);
}
+1 -1
View File
@@ -166,5 +166,5 @@ public:
void V3Depth::depthAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DepthVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("depth", 0, dumpTree() >= 6);
V3Global::dumpCheckGlobalTree("depth", 0, dumpTreeLevel() >= 6);
}
+1 -1
View File
@@ -127,5 +127,5 @@ public:
void V3DepthBlock::depthBlockAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DepthBlockVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deepblock", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("deepblock", 0, dumpTreeLevel() >= 3);
}
+38 -17
View File
@@ -46,6 +46,10 @@ private:
// TYPES
using FuncMmap = std::multimap<std::string, AstCFunc*>;
struct ScopeSelfPtr final {
VSelfPointerText thisPtr = VSelfPointerText{VSelfPointerText::Empty()};
VSelfPointerText vlSymsPtr = VSelfPointerText{VSelfPointerText::Empty()};
};
// STATE
AstNodeModule* m_modp = nullptr; // Current module
@@ -53,6 +57,7 @@ private:
const AstCFunc* m_funcp = nullptr; // Current function
bool m_modSingleton = false; // m_modp is only instantiated once
FuncMmap m_modFuncs; // Name of public functions added
std::map<const AstScope*, ScopeSelfPtr> m_scopeToSelf; // Scope to self pointers
// METHODS
@@ -68,12 +73,32 @@ private:
return (instances == 1);
}
// Construct a 'this' self pointer for the given scope
VSelfPointerText scopeThis(const AstScope* scopep) {
auto& ret = m_scopeToSelf[scopep];
if (ret.thisPtr.isEmpty()) {
string name = scopep->name();
string::size_type pos;
if ((pos = name.rfind('.')) != string::npos) name.erase(0, pos + 1);
ret.thisPtr = VSelfPointerText{VSelfPointerText::This(), name};
}
return ret.thisPtr;
}
// Construct a 'vlSyms' self pointer for the given scope
VSelfPointerText scopeVlSyms(const AstScope* scopep) {
auto& ret = m_scopeToSelf[scopep];
if (ret.vlSymsPtr.isEmpty()) {
ret.vlSymsPtr = VSelfPointerText{VSelfPointerText::VlSyms(), scopep->nameDotless()};
}
return ret.vlSymsPtr;
}
// Construct the best self pointer to reference an object in 'scopep' from a CFunc in
// 'm_scopep'. Result may be relative ("this->[...]") or absolute ("vlSyms->[...]").
//
// Using relative references allows V3Combine'ing code across multiple instances of the same
// module.
string descopedSelfPointer(const AstScope* scopep) {
VSelfPointerText descopedSelfPointer(const AstScope* scopep) {
UASSERT(scopep, "Var/Func not scoped");
// Static functions can't use relative references via 'this->'
const bool relativeRefOk = !m_funcp->isStatic();
@@ -85,23 +110,20 @@ private:
if (VN_IS(scopep->modp(), Class)) {
// Direct reference to class members are from within the class itself, references from
// outside the class must go via AstMemberSel
return "this";
return VSelfPointerText{VSelfPointerText::This()};
} else if (relativeRefOk && scopep == m_scopep) {
return "this";
return VSelfPointerText{VSelfPointerText::This()};
} else if (relativeRefOk && !m_modSingleton && scopep->aboveScopep() == m_scopep
&& VN_IS(scopep->modp(), Module)) {
// Reference to scope of instance directly under this module, can just "this->cell",
// which can potentially be V3Combined, but note this requires one extra pointer
// dereference which is slower, so we only use it if the source scope is not a
// singleton.
string name = scopep->name();
string::size_type pos;
if ((pos = name.rfind('.')) != string::npos) name.erase(0, pos + 1);
return "this->" + name;
return scopeThis(scopep);
} else {
// Reference to something elsewhere, or relative references are disabled. Use global
// variable
return "(&" + scopep->nameVlSym() + ")";
return scopeVlSyms(scopep);
}
}
@@ -163,11 +185,10 @@ private:
if (moreOfSame) {
AstIf* const ifp = new AstIf{
funcp->fileline(),
new AstEq{
funcp->fileline(), new AstCExpr{funcp->fileline(), "this", 64},
new AstCExpr{funcp->fileline(),
string{"&("} + funcp->scopep()->nameVlSym() + ")",
64}},
new AstEq{funcp->fileline(),
new AstCExpr{funcp->fileline(), "this", 64},
new AstCExpr{funcp->fileline(),
scopeVlSyms(funcp->scopep()).asString(), 64}},
returnp};
newfuncp->addStmtsp(ifp);
} else {
@@ -225,15 +246,15 @@ private:
const AstScope* const scopep = nodep->varScopep()->scopep();
if (varp->isFuncLocal()) {
// Reference to function locals need no self pointer
nodep->selfPointer("");
nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()});
} else if (scopep->modp() == v3Global.rootp()->constPoolp()->modp()) {
// Reference to constant pool value need no self pointer
nodep->selfPointer("");
nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()});
} else {
nodep->selfPointer(descopedSelfPointer(scopep));
}
nodep->varScopep(nullptr);
UINFO(9, " refout " << nodep << " selfPtr=" << nodep->selfPointer() << endl);
UINFO(9, " refout " << nodep << " selfPtr=" << nodep->selfPointer().asString() << endl);
}
void visit(AstCCall* nodep) override {
// UINFO(9, " " << nodep << endl);
@@ -285,5 +306,5 @@ public:
void V3Descope::descopeAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DescopeVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("descope", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("descope", 0, dumpTreeLevel() >= 3);
}
+2 -2
View File
@@ -503,11 +503,11 @@ public:
inline bool inlined() const;
// Methods that allow DfgVertex to participate in error reporting/messaging
void v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) {
void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
m_filelinep->v3errorEnd(str);
}
void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN
VL_REQUIRES(V3Error::s().m_mutex) {
VL_RELEASE(V3Error::s().m_mutex) {
m_filelinep->v3errorEndFatal(str);
}
string warnContextPrimary() const VL_REQUIRES(V3Error::s().m_mutex) {
+15 -11
View File
@@ -334,17 +334,20 @@ class AstToDfgVisitor final : public VNVisitor {
const uint32_t bEnd = b.m_lsb + bWidth;
const uint32_t overlapEnd = std::min(aEnd, bEnd) - 1;
varp->varp()->v3warn( //
MULTIDRIVEN,
"Bits [" //
<< overlapEnd << ":" << b.m_lsb << "] of signal "
<< varp->varp()->prettyNameQ()
<< " have multiple combinational drivers\n"
<< a.m_fileline->warnOther() << "... Location of first driver\n"
<< a.m_fileline->warnContextPrimary() << '\n'
<< b.m_fileline->warnOther() << "... Location of other driver\n"
<< b.m_fileline->warnContextSecondary() << varp->varp()->warnOther()
<< "... Only the first driver will be respected");
if (a.m_fileline->operatorCompare(*b.m_fileline) != 0) {
varp->varp()->v3warn( //
MULTIDRIVEN,
"Bits [" //
<< overlapEnd << ":" << b.m_lsb << "] of signal "
<< varp->varp()->prettyNameQ()
<< " have multiple combinational drivers\n"
<< a.m_fileline->warnOther() << "... Location of first driver\n"
<< a.m_fileline->warnContextPrimary() << '\n'
<< b.m_fileline->warnOther() << "... Location of other driver\n"
<< b.m_fileline->warnContextSecondary()
<< varp->varp()->warnOther()
<< "... Only the first driver will be respected");
}
// If the first driver completely covers the range of the second driver,
// we can just delete the second driver completely, otherwise adjust the
@@ -437,6 +440,7 @@ class AstToDfgVisitor final : public VNVisitor {
void visit(AstCell* nodep) override { markReferenced(nodep); }
void visit(AstNodeProcedure* nodep) override { markReferenced(nodep); }
void visit(AstVar* nodep) override {
if (nodep->isSc()) return;
// No need to (and in fact cannot) handle variables with unsupported dtypes
if (!DfgVertex::isSupportedDType(nodep->dtypep())) return;
// Mark ports as having external references
+6 -6
View File
@@ -237,7 +237,7 @@ void V3DfgOptimizer::extract(AstNetlist* netlistp) {
UINFO(2, __FUNCTION__ << ": " << endl);
// Extract more optimization candidates
DataflowExtractVisitor::apply(netlistp);
V3Global::dumpCheckGlobalTree("dfg-extract", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("dfg-extract", 0, dumpTreeLevel() >= 3);
}
void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
@@ -267,7 +267,7 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
// Build the DFG of this module
const std::unique_ptr<DfgGraph> dfg{V3DfgPasses::astToDfg(*modp, ctx)};
if (dumpDfg() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-input");
if (dumpDfgLevel() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-input");
// Extract the cyclic sub-graphs. We do this because a lot of the optimizations assume a
// DAG, and large, mostly acyclic graphs could not be optimized due to the presence of
@@ -284,7 +284,7 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
// For each cyclic component
for (auto& component : cyclicComponents) {
if (dumpDfg() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
// TODO: Apply optimizations safe for cyclic graphs
// Add back under the main DFG (we will convert everything back in one go)
dfg->addGraph(*component);
@@ -292,7 +292,7 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
// For each acyclic component
for (auto& component : acyclicComponents) {
if (dumpDfg() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
// Optimize the component
V3DfgPasses::optimize(*component, ctx);
// Add back under the main DFG (we will convert everything back in one go)
@@ -300,9 +300,9 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
}
// Convert back to Ast
if (dumpDfg() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-optimized");
if (dumpDfgLevel() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-optimized");
AstModule* const resultModp = V3DfgPasses::dfgToAst(*dfg, ctx);
UASSERT_OBJ(resultModp == modp, modp, "Should be the same module");
}
V3Global::dumpCheckGlobalTree("dfg-optimize", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("dfg-optimize", 0, dumpTreeLevel() >= 3);
}
+3 -3
View File
@@ -285,7 +285,7 @@ void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgOptimizationContext& ctx) {
const auto apply = [&](int dumpLevel, const string& name, std::function<void()> pass) {
pass();
if (dumpDfg() >= dumpLevel) {
if (dumpDfgLevel() >= dumpLevel) {
const string strippedName = VString::removeWhitespace(name);
const string label
= ctx.prefix() + "pass-" + cvtToStr(passNumber) + "-" + strippedName;
@@ -294,7 +294,7 @@ void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgOptimizationContext& ctx) {
++passNumber;
};
if (dumpDfg() >= 8) dfg.dumpDotAllVarConesPrefixed(ctx.prefix() + "input");
if (dumpDfgLevel() >= 8) dfg.dumpDotAllVarConesPrefixed(ctx.prefix() + "input");
apply(3, "input ", [&]() {});
apply(4, "inlineVars ", [&]() { inlineVars(dfg); });
apply(4, "cse0 ", [&]() { cse(dfg, ctx.m_cseContext0); });
@@ -304,5 +304,5 @@ void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgOptimizationContext& ctx) {
apply(4, "cse1 ", [&]() { cse(dfg, ctx.m_cseContext1); });
}
apply(4, "removeVars ", [&]() { removeVars(dfg, ctx.m_removeVarsContext); });
if (dumpDfg() >= 8) dfg.dumpDotAllVarConesPrefixed(ctx.prefix() + "optimized");
if (dumpDfgLevel() >= 8) dfg.dumpDotAllVarConesPrefixed(ctx.prefix() + "optimized");
}
+2 -2
View File
@@ -123,9 +123,9 @@ struct V3DfgPeepholeContext final {
const std::string m_label; // Label to apply to stats
// Enable flags for each optimization
bool m_enabled[VDfgPeepholePattern::_ENUM_END];
std::array<bool, VDfgPeepholePattern::_ENUM_END> m_enabled;
// Count of applications for each optimization (for statistics)
VDouble0 m_count[VDfgPeepholePattern::_ENUM_END];
std::array<VDouble0, VDfgPeepholePattern::_ENUM_END> m_count;
explicit V3DfgPeepholeContext(const std::string& label);
~V3DfgPeepholeContext();
+1 -1
View File
@@ -100,5 +100,5 @@ void V3DupFinder::dumpFile(const string& filename, bool tree) {
}
void V3DupFinder::dumpFilePrefixed(const string& nameComment, bool tree) {
if (dump()) dumpFile(v3Global.debugFilename(nameComment) + ".hash", tree);
if (dumpLevel()) dumpFile(v3Global.debugFilename(nameComment) + ".hash", tree);
}
+10 -15
View File
@@ -79,6 +79,10 @@ string EmitCBaseVisitorConst::cFuncArgs(const AstCFunc* nodep) {
args += prefixNameProtect(EmitCParentModule::get(nodep));
args += "* vlSelf";
}
if (nodep->needProcess()) {
if (!args.empty()) args += ", ";
args += "VlProcessRef vlProcess";
}
if (!nodep->argTypes().empty()) {
if (!args.empty()) args += ", ";
args += nodep->argTypes();
@@ -224,21 +228,12 @@ void EmitCBaseVisitorConst::emitVarDecl(const AstVar* nodep, bool asRef) {
}
void EmitCBaseVisitorConst::emitModCUse(const AstNodeModule* modp, VUseType useType) {
string nl;
for (AstNode* itemp = modp->stmtsp(); itemp; itemp = itemp->nextp()) {
if (AstCUse* const usep = VN_CAST(itemp, CUse)) {
if (usep->useType() == useType) {
if (usep->useType().isInclude()) {
puts("#include \"" + prefixNameProtect(usep) + ".h\"\n");
}
if (usep->useType().isFwdClass()) {
puts("class " + prefixNameProtect(usep) + ";\n");
}
nl = "\n";
}
}
}
puts(nl);
bool nl = false;
forModCUse(modp, useType, [&](string entry) {
puts(entry);
nl = true;
});
if (nl) puts("\n");
}
void EmitCBaseVisitorConst::emitTextSection(const AstNodeModule* modp, VNType type) {
+17 -1
View File
@@ -62,7 +62,7 @@ public:
static string symClassAssign() {
return symClassName() + "* const __restrict vlSymsp VL_ATTR_UNUSED = vlSelf->vlSymsp;\n";
}
static string prefixNameProtect(const AstNode* nodep) VL_MT_STABLE { // C++ name with prefix
static string prefixNameProtect(const AstNode* nodep) { // C++ name with prefix
return v3Global.opt.modPrefix() + "_" + VIdProtect::protect(nodep->name());
}
static bool isAnonOk(const AstVar* varp) {
@@ -113,6 +113,22 @@ public:
void emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp, bool withScope);
void emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp, bool cLinkage = false);
void emitVarDecl(const AstVar* nodep, bool asRef = false);
template <typename F>
static void forModCUse(const AstNodeModule* modp, VUseType useType, F action) {
for (AstNode* itemp = modp->stmtsp(); itemp; itemp = itemp->nextp()) {
if (AstCUse* const usep = VN_CAST(itemp, CUse)) {
if (usep->useType().containsAny(useType)) {
if (usep->useType().containsAny(VUseType::INT_INCLUDE)) {
action("#include \"" + prefixNameProtect(usep) + ".h\"\n");
continue; // Forward declaration is not necessary
}
if (usep->useType().containsAny(VUseType::INT_FWD_CLASS)) {
action("class " + prefixNameProtect(usep) + ";\n");
}
}
}
}
}
void emitModCUse(const AstNodeModule* modp, VUseType useType);
void emitTextSection(const AstNodeModule* modp, VNType type);
+19 -3
View File
@@ -108,7 +108,7 @@ void EmitCFunc::emitOpName(AstNode* nodep, const string& format, AstNode* lhsp,
UASSERT_OBJ(m_wideTempRefp, nodep,
"Wide Op w/ no temp, perhaps missing op in V3EmitC?");
COMMA;
if (!m_wideTempRefp->selfPointer().empty()) {
if (!m_wideTempRefp->selfPointer().isEmpty()) {
emitDereference(m_wideTempRefp->selfPointerProtect(m_useSelfForThis));
}
puts(m_wideTempRefp->varp()->nameProtect());
@@ -314,6 +314,7 @@ void EmitCFunc::displayNode(AstNode* nodep, AstScopeName* scopenamep, const stri
// Convert Verilog display to C printf formats
// "%0t" becomes "%d"
VL_RESTORER(m_emitDispState);
m_emitDispState.clear();
string vfmt;
string::const_iterator pos = vformat.begin();
@@ -417,6 +418,15 @@ void EmitCFunc::emitCCallArgs(const AstNodeCCall* nodep, const string& selfPoint
puts(selfPointer);
comma = true;
}
if (nodep->funcp()->needProcess()) {
if (comma) puts(", ");
if (VN_IS(nodep->backp(), CAwait) || !nodep->funcp()->isCoroutine()) {
puts("vlProcess");
} else {
puts("std::make_shared<VlProcess>()");
}
comma = true;
}
if (!nodep->argTypes().empty()) {
if (comma) puts(", ");
puts(nodep->argTypes());
@@ -443,6 +453,10 @@ void EmitCFunc::emitCvtPackStr(AstNode* nodep) {
putbs("std::string{");
putsQuoted(constp->num().toString());
puts("}");
} else if (VN_IS(nodep->dtypep(), StreamDType)) {
putbs("VL_CVT_PACK_STR_ND(");
iterateAndNextConstNull(nodep);
puts(")");
} else {
putbs("VL_CVT_PACK_STR_N");
emitIQW(nodep);
@@ -503,7 +517,7 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string
if (!assigntop) {
puts(assignString);
} else {
if (!assigntop->selfPointer().empty()) {
if (!assigntop->selfPointer().isEmpty()) {
emitDereference(assigntop->selfPointerProtect(m_useSelfForThis));
}
puts(assigntop->varp()->nameProtect());
@@ -525,7 +539,7 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string
if (!assigntop) {
puts(assignString);
} else {
if (!assigntop->selfPointer().empty()) {
if (!assigntop->selfPointer().isEmpty()) {
emitDereference(assigntop->selfPointerProtect(m_useSelfForThis));
}
puts(assigntop->varp()->nameProtect());
@@ -695,6 +709,8 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP
return "";
} else if (basicp && basicp->isForkSync()) {
return "";
} else if (basicp && basicp->isProcessRef()) {
return "";
} else if (basicp && basicp->isDelayScheduler()) {
return "";
} else if (basicp && basicp->isTriggerScheduler()) {
+120 -34
View File
@@ -22,6 +22,7 @@
#include "V3EmitCConstInit.h"
#include "V3Global.h"
#include "V3MemberMap.h"
#include <algorithm>
#include <map>
@@ -115,6 +116,7 @@ public:
class EmitCFunc VL_NOT_FINAL : public EmitCConstInit {
private:
VMemberMap m_memberMap;
AstVarRef* m_wideTempRefp = nullptr; // Variable that _WW macros should be setting
int m_labelNum = 0; // Next label number
int m_splitSize = 0; // # of cfunc nodes placed into output file
@@ -148,6 +150,22 @@ protected:
bool m_useSelfForThis = false; // Replace "this" with "vlSelf"
const AstNodeModule* m_modp = nullptr; // Current module being emitted
const AstCFunc* m_cfuncp = nullptr; // Current function being emitted
bool m_instantiatesOwnProcess = false;
bool constructorNeedsProcess(const AstClass* const classp) {
const AstNode* const newp = m_memberMap.findMember(classp, "new");
if (!newp) return false;
const AstCFunc* const ctorp = VN_CAST(newp, CFunc);
if (!ctorp) return false;
UASSERT_OBJ(ctorp->isConstructor(), ctorp, "`new` is not a constructor!");
return ctorp->needProcess();
}
bool constructorNeedsProcess(const AstNodeDType* const dtypep) {
if (const AstClassRefDType* const crefdtypep = VN_CAST(dtypep, ClassRefDType))
return constructorNeedsProcess(crefdtypep->classp());
return false;
}
public:
// METHODS
@@ -174,12 +192,10 @@ public:
}
void emitScIQW(AstVar* nodep) {
UASSERT_OBJ(nodep->isSc(), nodep, "emitting SystemC operator on non-SC variable");
// clang-format off
puts(nodep->isScBigUint() ? "SB"
: nodep->isScUint() ? "SU"
: nodep->isScBv() ? "SW"
: (nodep->isScQuad() ? "SQ" : "SI"));
// clang-format on
: (nodep->isScQuad() ? "SQ" : "SI"));
}
void emitDatap(AstNode* nodep) {
// When passing to a function with va_args the compiler doesn't
@@ -212,13 +228,34 @@ public:
comma = true;
}
}
template <typename T>
string optionalProcArg(const T* const nodep) {
return (nodep && constructorNeedsProcess(nodep)) ? "vlProcess, " : "";
}
const AstCNew* getSuperNewCallRecursep(AstNode* const nodep) {
// Get the super.new call
if (!nodep) return nullptr;
if (const AstCNew* const cnewp = VN_CAST(nodep, CNew)) return cnewp;
if (const AstStmtExpr* const stmtp = VN_CAST(nodep, StmtExpr)) {
if (const AstCNew* const cnewp = VN_CAST(stmtp->exprp(), CNew)) return cnewp;
}
if (const AstJumpBlock* const blockp = VN_CAST(nodep, JumpBlock)) {
if (const AstCNew* const cnewp = getSuperNewCallRecursep(blockp->stmtsp())) {
return cnewp;
}
}
if (const AstCNew* const cnewp = getSuperNewCallRecursep(nodep->nextp())) return cnewp;
return nullptr;
}
// VISITORS
using EmitCConstInit::visit;
void visit(AstCFunc* nodep) override {
VL_RESTORER(m_useSelfForThis);
VL_RESTORER(m_cfuncp);
VL_RESTORER(m_instantiatesOwnProcess)
m_cfuncp = nodep;
m_instantiatesOwnProcess = false;
splitSizeInc(nodep);
@@ -231,20 +268,16 @@ public:
if (!nodep->baseCtors().empty()) {
puts(": ");
puts(nodep->baseCtors());
puts("(vlSymsp");
const AstClass* const classp = VN_CAST(nodep->scopep()->modp(), Class);
// Find call to super.new to get the arguments
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
AstNode* exprp;
if (VN_IS(stmtp, StmtExpr)) {
exprp = VN_CAST(stmtp, StmtExpr)->exprp();
} else {
exprp = stmtp;
}
if (AstCNew* const newRefp = VN_CAST(exprp, CNew)) {
putCommaIterateNext(newRefp->argsp(), true);
break;
}
if (classp && constructorNeedsProcess(classp)) {
puts("(vlProcess, vlSymsp");
} else {
puts("(vlSymsp");
}
const AstCNew* const superNewCallp = getSuperNewCallRecursep(nodep->stmtsp());
UASSERT_OBJ(superNewCallp, nodep, "super.new call not found");
putCommaIterateNext(superNewCallp->argsp(), true);
puts(")");
}
puts(" {\n");
@@ -265,6 +298,23 @@ public:
puts(nodep->isLoose() ? "__" : "::");
puts(nodep->nameProtect() + "\\n\"); );\n");
// Instantiate a process class if it's going to be needed somewhere later
nodep->forall([&](const AstNodeCCall* ccallp) -> bool {
if (ccallp->funcp()->needProcess()
&& (ccallp->funcp()->isCoroutine() == VN_IS(ccallp->backp(), CAwait))) {
if (!nodep->needProcess() && !m_instantiatesOwnProcess) {
m_instantiatesOwnProcess = true;
return false;
}
}
return true;
});
if (m_instantiatesOwnProcess) {
AstNode* const vlprocp = new AstCStmt{
nodep->fileline(), "VlProcessRef vlProcess = std::make_shared<VlProcess>();\n"};
nodep->stmtsp()->addHereThisAsNext(vlprocp);
}
for (AstNode* subnodep = nodep->argsp(); subnodep; subnodep = subnodep->nextp()) {
if (AstVar* const varp = VN_CAST(subnodep, Var)) {
if (varp->isFuncReturn()) emitVarDecl(varp);
@@ -295,6 +345,19 @@ public:
emitVarDecl(nodep);
}
void visit(AstCvtDynArrayToPacked* nodep) override {
puts("VL_DYN_TO_");
emitIQW(nodep);
puts("<");
const AstNodeDType* const elemDTypep = nodep->fromp()->dtypep()->subDTypep();
putbs(elemDTypep->cType("", false, false));
puts(">(");
iterateAndNextConstNull(nodep->fromp());
puts(", ");
puts(cvtToStr(elemDTypep->widthMin()));
puts(")");
}
void visit(AstNodeAssign* nodep) override {
bool paren = true;
bool decind = false;
@@ -349,12 +412,28 @@ public:
puts(cvtToStr(nodep->widthMin()) + ",");
iterateAndNextConstNull(nodep->lhsp());
puts(", ");
} else if (const AstCvtPackedToDynArray* const castp
= VN_CAST(nodep->rhsp(), CvtPackedToDynArray)) {
puts("VL_ASSIGN_DYN_Q<");
putbs(castp->dtypep()->subDTypep()->cType("", false, false));
puts(">(");
iterateAndNextConstNull(nodep->lhsp());
puts(", ");
puts(cvtToStr(castp->dtypep()->subDTypep()->widthMin()));
puts(", ");
puts(cvtToStr(castp->fromp()->widthMin()));
puts(", ");
rhs = false;
iterateAndNextConstNull(castp->fromp());
} else if (nodep->isWide() && VN_IS(nodep->lhsp(), VarRef) //
&& !VN_IS(nodep->rhsp(), CExpr) //
&& !VN_IS(nodep->rhsp(), CMethodHard) //
&& !VN_IS(nodep->rhsp(), VarRef) //
&& !VN_IS(nodep->rhsp(), AssocSel) //
&& !VN_IS(nodep->rhsp(), ArraySel)) {
&& !VN_IS(nodep->rhsp(), MemberSel) //
&& !VN_IS(nodep->rhsp(), StructSel) //
&& !VN_IS(nodep->rhsp(), ArraySel) //
&& !VN_IS(nodep->rhsp(), ExprStmt)) {
// Wide functions assign into the array directly, don't need separate assign statement
m_wideTempRefp = VN_AS(nodep->lhsp(), VarRef);
paren = false;
@@ -381,19 +460,17 @@ public:
void visit(AstAssocSel* nodep) override {
iterateAndNextConstNull(nodep->fromp());
putbs(".at(");
AstAssocArrayDType* const adtypep = VN_AS(nodep->fromp()->dtypep(), AssocArrayDType);
AstAssocArrayDType* const adtypep
= VN_AS(nodep->fromp()->dtypep()->skipRefp(), AssocArrayDType);
UASSERT_OBJ(adtypep, nodep, "Associative select on non-associative type");
if (adtypep->keyDTypep()->isWide()) {
emitCvtWideArray(nodep->bitp(), nodep->fromp());
} else {
iterateAndNextConstNull(nodep->bitp());
}
iterateAndNextConstNull(nodep->bitp());
puts(")");
}
void visit(AstWildcardSel* nodep) override {
iterateAndNextConstNull(nodep->fromp());
putbs(".at(");
AstWildcardArrayDType* const adtypep = VN_AS(nodep->fromp()->dtypep(), WildcardArrayDType);
AstWildcardArrayDType* const adtypep
= VN_AS(nodep->fromp()->dtypep()->skipRefp(), WildcardArrayDType);
UASSERT_OBJ(adtypep, nodep, "Wildcard select on non-wildcard-associative type");
iterateAndNextConstNull(nodep->bitp());
puts(")");
@@ -417,7 +494,7 @@ public:
puts(funcNameProtect(funcp));
} else {
// Calling regular method/function
if (!nodep->selfPointer().empty()) {
if (!nodep->selfPointer().isEmpty()) {
emitDereference(nodep->selfPointerProtect(m_useSelfForThis));
}
puts(funcp->nameProtect());
@@ -441,8 +518,9 @@ public:
// super.new case
return;
}
// assignment case
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", vlSymsp");
// assignment case;
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", "
+ optionalProcArg(nodep->dtypep()) + "vlSymsp");
putCommaIterateNext(nodep->argsp(), true);
puts(")");
}
@@ -467,7 +545,7 @@ public:
void visit(AstLambdaArgRef* nodep) override { putbs(nodep->nameProtect()); }
void visit(AstWith* nodep) override {
// With uses a C++11 lambda
putbs("[=](");
putbs("[&](");
if (auto* const argrefp = nodep->indexArgRefp()) {
putbs(argrefp->dtypep()->cType(argrefp->nameProtect(), false, false));
puts(",");
@@ -475,10 +553,9 @@ public:
if (auto* const argrefp = nodep->valueArgRefp()) {
putbs(argrefp->dtypep()->cType(argrefp->nameProtect(), false, false));
}
// Probably fragile, V3Task may need to convert to a AstCReturn
puts(") { return ");
puts(") {\n");
iterateAndNextConstNull(nodep->exprp());
puts("; }\n");
puts("}\n");
}
void visit(AstNodeCase* nodep) override { // LCOV_EXCL_LINE
// In V3Case...
@@ -792,7 +869,7 @@ public:
void visit(AstSysFuncAsTask* nodep) override {
if (!nodep->lhsp()->isWide()) puts("(void)");
iterateAndNextConstNull(nodep->lhsp());
if (!nodep->lhsp()->isWide()) puts(";");
if (!nodep->lhsp()->isWide()) puts(";\n");
}
void visit(AstStackTraceF* nodep) override { puts("VL_STACKTRACE_N()"); }
void visit(AstStackTraceT* nodep) override { puts("VL_STACKTRACE();\n"); }
@@ -831,6 +908,11 @@ public:
iterateAndNextConstNull(nodep->endStmtsp());
puts("}\n");
}
void visit(AstCLocalScope* nodep) override {
puts("{\n");
iterateAndNextConstNull(nodep->stmtsp());
puts("}\n");
}
void visit(AstJumpGo* nodep) override {
puts("goto __Vlabel" + cvtToStr(nodep->labelp()->blockp()->labelNum()) + ";\n");
}
@@ -1025,6 +1107,7 @@ public:
UASSERT_OBJ(!emitSimpleOk(nodep), nodep, "Triop cannot be described in a simple way");
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nodep->thsp());
}
void visit(AstCvtPackString* nodep) override { emitCvtPackStr(nodep->lhsp()); }
void visit(AstRedXor* nodep) override {
if (nodep->lhsp()->isWide()) {
visit(static_cast<AstNodeUniop*>(nodep));
@@ -1043,7 +1126,9 @@ public:
}
void visit(AstCCast* nodep) override {
// Extending a value of the same word width is just a NOP.
if (nodep->size() <= VL_IDATASIZE) {
if (const AstClassRefDType* const classDtypep = VN_CAST(nodep->dtypep(), ClassRefDType)) {
puts("(" + classDtypep->cType("", false, false) + ")(");
} else if (nodep->size() <= VL_IDATASIZE) {
puts("(IData)(");
} else {
puts("(QData)(");
@@ -1085,7 +1170,8 @@ public:
puts(")");
}
void visit(AstNewCopy* nodep) override {
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", ");
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", "
+ optionalProcArg(nodep->dtypep()));
puts("*"); // i.e. make into a reference
iterateAndNextConstNull(nodep->rhsp());
puts(")");
@@ -1179,7 +1265,7 @@ public:
} else if (varp->isIfaceRef()) {
puts(nodep->selfPointerProtect(m_useSelfForThis));
return;
} else if (!nodep->selfPointer().empty()) {
} else if (!nodep->selfPointer().isEmpty()) {
emitDereference(nodep->selfPointerProtect(m_useSelfForThis));
}
puts(nodep->varp()->nameProtect());
+33 -8
View File
@@ -17,11 +17,15 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3EmitC.h"
#include "V3EmitCConstInit.h"
#include "V3Global.h"
#include <algorithm>
#include <cstdint>
#include <set>
#include <string>
#include <vector>
VL_DEFINE_DEBUG_FUNCTIONS;
@@ -219,7 +223,7 @@ class EmitCHeader final : public EmitCConstInit {
emitted.insert(sdtypep);
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
AstNodeUOrStructDType* subp = VN_CAST(itemp->skipRefp(), NodeUOrStructDType);
AstNodeUOrStructDType* const subp = itemp->getChildStructp();
if (subp && !subp->packed()) {
// Recurse if it belongs to the current module
if (subp->classOrPackagep() == modp) {
@@ -235,6 +239,20 @@ class EmitCHeader final : public EmitCConstInit {
puts(itemp->dtypep()->cType(itemp->nameProtect(), false, false));
puts(";\n");
}
puts("\nbool operator==(const " + EmitCBase::prefixNameProtect(sdtypep)
+ "& rhs) const {\n");
puts("return ");
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
if (itemp != sdtypep->membersp()) puts("\n && ");
puts(itemp->nameProtect() + " == " + "rhs." + itemp->nameProtect());
}
puts(";\n");
puts("}\n");
puts("bool operator!=(const " + EmitCBase::prefixNameProtect(sdtypep)
+ "& rhs) const {\n");
puts("return !(*this == rhs);\n}\n");
puts("};\n");
}
void emitStructs(const AstNodeModule* modp) {
@@ -287,11 +305,9 @@ class EmitCHeader final : public EmitCConstInit {
+ ".h\"\n");
}
}
emitModCUse(modp, VUseType::INT_INCLUDE);
// Forward declarations required by this AstNodeModule
puts("\nclass " + symClassName() + ";\n");
emitModCUse(modp, VUseType::INT_FWD_CLASS);
// From `systemc_header
emitTextSection(modp, VNType::atScHdr);
@@ -300,6 +316,7 @@ class EmitCHeader final : public EmitCConstInit {
// Open class body {{{
puts("\nclass ");
if (!VN_IS(modp, Class)) puts("alignas(VL_CACHE_LINE_BYTES) ");
puts(prefixNameProtect(modp));
if (const AstClass* const classp = VN_CAST(modp, Class)) {
puts(" : public ");
@@ -329,11 +346,7 @@ class EmitCHeader final : public EmitCConstInit {
emitTextSection(modp, VNType::atScInt);
// Close class body
if (!VN_IS(modp, Class)) {
puts("} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);\n");
} else {
puts("};\n");
}
puts("};\n");
// }}}
// Emit out of class function declarations
@@ -364,6 +377,18 @@ class EmitCHeader final : public EmitCConstInit {
if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n");
if (v3Global.usesTiming()) puts("#include \"verilated_timing.h\"\n");
std::set<string> cuse_set;
auto add_to_cuse_set = [&](string s) { cuse_set.insert(s); };
forModCUse(modp, VUseType::INT_FWD_CLASS | VUseType::INT_INCLUDE, add_to_cuse_set);
if (const AstClassPackage* const packagep = VN_CAST(modp, ClassPackage)) {
forModCUse(packagep->classp(), VUseType::INT_INCLUDE | VUseType::INT_FWD_CLASS,
add_to_cuse_set);
}
for (const string& s : cuse_set) puts(s);
puts("\n");
emitAll(modp);
if (const AstClassPackage* const packagep = VN_CAST(modp, ClassPackage)) {
+26 -11
View File
@@ -22,6 +22,7 @@
#include "V3EmitCFunc.h"
#include "V3Global.h"
#include "V3String.h"
#include "V3ThreadPool.h"
#include "V3UniqueNames.h"
#include <map>
@@ -53,22 +54,23 @@ class EmitCGatherDependencies final : VNVisitorConst {
} else if (const AstNodeUOrStructDType* const dtypep
= VN_CAST(nodep, NodeUOrStructDType)) {
if (!dtypep->packed()) {
UASSERT_OBJ(dtypep->classOrPackagep(), nodep, "Unlinked struct package");
m_dependencies.insert(EmitCBase::prefixNameProtect(dtypep->classOrPackagep()));
}
}
}
void addSelfDependency(const string& selfPointer, AstNode* nodep) {
if (selfPointer.empty()) {
void addSelfDependency(VSelfPointerText selfPointer, AstNode* nodep) {
if (selfPointer.isEmpty()) {
// No self pointer (e.g.: function locals, const pool values, loose static methods),
// so no dependency
} else if (VString::startsWith(selfPointer, "this")) {
} else if (selfPointer.hasThis()) {
// Dereferencing 'this', we need the definition of this module, which is also the
// module that contains the variable.
addModDependency(EmitCParentModule::get(nodep));
} else {
// Must be an absolute reference
UASSERT_OBJ(selfPointer.find("vlSymsp") != string::npos, nodep,
"Unknown self pointer: '" << selfPointer << "'");
UASSERT_OBJ(selfPointer.isVlSym(), nodep,
"Unknown self pointer: '" << selfPointer.asString() << "'");
// Dereferencing vlSymsp, so we need it's definition...
addSymsDependency();
}
@@ -675,7 +677,7 @@ class EmitCTrace final : EmitCFunc {
string fstvt;
// Doubles have special decoding properties, so must indicate if a double
if (nodep->dtypep()->basicp()->isDouble()) {
if (vartype == VVarType::GPARAM || vartype == VVarType::LPARAM) {
if (vartype.isParam()) {
fstvt = "FST_VT_VCD_REAL_PARAMETER";
} else {
fstvt = "FST_VT_VCD_REAL";
@@ -908,7 +910,7 @@ class EmitCTrace final : EmitCFunc {
~EmitCTrace() override = default;
public:
static void main(AstNodeModule* modp, bool slow, std::deque<AstCFile*>& cfilesr) {
static void main(AstNodeModule* modp, bool slow, std::deque<AstCFile*>& cfilesr) VL_MT_STABLE {
EmitCTrace{modp, slow, cfilesr};
}
};
@@ -921,24 +923,37 @@ void V3EmitC::emitcImp() {
// Make parent module pointers available.
const EmitCParentModule emitCParentModule;
std::list<std::deque<AstCFile*>> cfiles;
std::list<std::future<void>> futures;
// Process each module in turn
for (const AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) {
if (VN_IS(nodep, Class)) continue; // Imped with ClassPackage
const AstNodeModule* const modp = VN_AS(nodep, NodeModule);
cfiles.emplace_back();
EmitCImp::main(modp, /* slow: */ true, cfiles.back());
auto& slowCfilesr = cfiles.back();
futures.push_back(V3ThreadPool::s().enqueue(
[modp, &slowCfilesr]() { EmitCImp::main(modp, /* slow: */ true, slowCfilesr); }));
cfiles.emplace_back();
EmitCImp::main(modp, /* slow: */ false, cfiles.back());
auto& fastCfilesr = cfiles.back();
futures.push_back(V3ThreadPool::s().enqueue(
[modp, &fastCfilesr]() { EmitCImp::main(modp, /* slow: */ false, fastCfilesr); }));
}
// Emit trace routines (currently they can only exist in the top module)
if (v3Global.opt.trace() && !v3Global.opt.lintOnly()) {
cfiles.emplace_back();
EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ true, cfiles.back());
auto& slowCfilesr = cfiles.back();
futures.push_back(V3ThreadPool::s().enqueue([&slowCfilesr]() {
EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ true, slowCfilesr);
}));
cfiles.emplace_back();
EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ false, cfiles.back());
auto& fastCfilesr = cfiles.back();
futures.push_back(V3ThreadPool::s().enqueue([&fastCfilesr]() {
EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ false, fastCfilesr);
}));
}
// Wait for futures
V3ThreadPool::waitForFutures(futures);
for (const auto& collr : cfiles) {
for (const auto cfilep : collr) v3Global.rootp()->addFilesp(cfilep);
}
+9 -1
View File
@@ -51,6 +51,14 @@ private:
// Not defining main_time/vl_time_stamp, so
v3Global.opt.addCFlags("-DVL_TIME_CONTEXT"); // On MSVC++ anyways
// Optional main top name argument, with empty string replacement
string topArg;
string topName = v3Global.opt.mainTopName();
if (!topName.empty()) {
if (topName == "-") topName = "";
topArg = ", \"" + topName + "\"";
}
// Heavily commented output, as users are likely to look at or copy this code
ofp()->putsHeader();
puts("// DESCRIPTION: main() calling loop, created with Verilator --main\n");
@@ -71,7 +79,7 @@ private:
puts("// Construct the Verilated model, from Vtop.h generated from Verilating\n");
puts("const std::unique_ptr<" + topClassName() + "> topp{new " + topClassName()
+ "{contextp.get()}};\n");
+ "{contextp.get()" + topArg + "}};\n");
puts("\n");
puts("// Simulate until $finish\n");
+17 -2
View File
@@ -89,7 +89,7 @@ class EmitCModel final : public EmitCFunc {
puts("\n");
puts("// This class is the main interface to the Verilated model\n");
puts("class " + topClassName() + " VL_NOT_FINAL : ");
puts("class alignas(VL_CACHE_LINE_BYTES) " + topClassName() + " VL_NOT_FINAL : ");
if (optSystemC()) {
// SC_MODULE, but with multiple-inheritance of VerilatedModel
puts("public ::sc_core::sc_module, ");
@@ -234,11 +234,17 @@ class EmitCModel final : public EmitCFunc {
puts("const char* hierName() const override final;\n");
puts("const char* modelName() const override final;\n");
puts("unsigned threads() const override final;\n");
puts("/// Prepare for cloning the model at the process level (e.g. fork in Linux)\n");
puts("/// Release necessary resources. Called before cloning.\n");
puts("void prepareClone() const;\n");
puts("/// Re-init after cloning the model at the process level (e.g. fork in Linux)\n");
puts("/// Re-allocate necessary resources. Called after cloning.\n");
puts("void atClone() const;\n");
if (v3Global.opt.trace()) {
puts("std::unique_ptr<VerilatedTraceConfig> traceConfig() const override final;\n");
}
puts("} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);\n");
puts("};\n");
ofp()->putsEndGuard();
@@ -479,6 +485,15 @@ class EmitCModel final : public EmitCFunc {
+ "\"; }\n");
puts("unsigned " + topClassName() + "::threads() const { return "
+ cvtToStr(std::max(1, v3Global.opt.threads())) + "; }\n");
puts("void " + topClassName()
+ "::prepareClone() const { contextp()->prepareClone(); }\n");
puts("void " + topClassName() + "::atClone() const {\n");
if (v3Global.opt.threads() > 1) {
puts("vlSymsp->__Vm_threadPoolp = static_cast<VlThreadPool*>(");
}
puts("contextp()->threadPoolpOnClone()");
if (v3Global.opt.threads() > 1) puts(")");
puts(";\n}\n");
if (v3Global.opt.trace()) {
puts("std::unique_ptr<VerilatedTraceConfig> " + topClassName()
+4 -3
View File
@@ -440,7 +440,8 @@ void EmitCSyms::emitSymHdr() {
}
puts("\n// SYMS CLASS (contains all model state)\n");
puts("class " + symClassName() + " final : public VerilatedSyms {\n");
puts("class alignas(VL_CACHE_LINE_BYTES)" + symClassName()
+ " final : public VerilatedSyms {\n");
ofp()->putsPrivate(false); // public:
puts("// INTERNAL STATE\n");
@@ -467,7 +468,7 @@ void EmitCSyms::emitSymHdr() {
if (v3Global.opt.mtasks()) {
puts("\n// MULTI-THREADING\n");
puts("VlThreadPool* const __Vm_threadPoolp;\n");
puts("VlThreadPool* __Vm_threadPoolp;\n");
puts("bool __Vm_even_cycle__ico = false;\n");
puts("bool __Vm_even_cycle__act = false;\n");
puts("bool __Vm_even_cycle__nba = false;\n");
@@ -554,7 +555,7 @@ void EmitCSyms::emitSymHdr() {
puts("void " + protect("__Vserialize") + "(VerilatedSerialize& os);\n");
puts("void " + protect("__Vdeserialize") + "(VerilatedDeserialize& os);\n");
}
puts("} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);\n");
puts("};\n");
ofp()->putsEndGuard();
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
+24 -2
View File
@@ -424,6 +424,12 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
iterateAndNextConstNull(nodep->exprsp());
puts(")");
}
void visit(AstExprStmt* nodep) override {
putfs(nodep, "$_EXPRSTMT(\n");
iterateAndNextConstNull(nodep->stmtsp());
putbs(", ");
puts(");\n");
}
void visit(AstCMethodHard* nodep) override {
iterateConst(nodep->fromp());
@@ -434,6 +440,15 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
}
puts(")");
}
void visit(AstCMethodCall* nodep) override {
iterateConst(nodep->fromp());
puts("." + nodep->name() + "(");
for (AstNode* pinp = nodep->argsp(); pinp; pinp = pinp->nextp()) {
if (pinp != nodep->argsp()) puts(", ");
iterateConst(pinp);
}
puts(")");
}
// Operators
virtual void emitVerilogFormat(AstNode* nodep, const string& format, AstNode* lhsp = nullptr,
@@ -649,6 +664,12 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
iterateAndNextConstNull(nodep->pinsp());
puts(")");
}
void visit(AstCCall* nodep) override {
puts(nodep->funcp()->name());
puts("(");
iterateAndNextConstNull(nodep->argsp());
puts(")");
}
void visit(AstArg* nodep) override { iterateAndNextConstNull(nodep->exprp()); }
void visit(AstPrintTimeScale* nodep) override {
puts(nodep->verilogKwd());
@@ -661,10 +682,11 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
putfs(nodep, nodep->varScopep()->prettyName());
} else {
if (nodep->varp()) {
if (nodep->selfPointer().empty()) {
if (nodep->selfPointer().isEmpty()) {
putfs(nodep, nodep->varp()->prettyName());
} else {
putfs(nodep, nodep->selfPointer() + "->");
putfs(nodep, nodep->selfPointer().asString());
putfs(nodep, "->");
puts(nodep->varp()->prettyName());
}
} else {
+4 -4
View File
@@ -188,7 +188,7 @@ class EmitXmlFileVisitor final : public VNVisitorConst {
void visit(AstVar* nodep) override {
const VVarType typ = nodep->varType();
const string kw = nodep->verilogKwd();
const string vt = nodep->dtypep()->name();
const string vt = nodep->dtypep() ? nodep->dtypep()->name() : "";
outputTag(nodep, "");
if (nodep->isIO()) {
puts(" dir=");
@@ -225,7 +225,7 @@ class EmitXmlFileVisitor final : public VNVisitorConst {
void visit(AstPin* nodep) override {
// What we call a pin in verilator is a port in the IEEE spec.
outputTag(nodep, "port"); // IEEE: vpiPort
if (nodep->modVarp()->isIO()) {
if (nodep->modVarp() && nodep->modVarp()->isIO()) {
puts(" direction=\"" + nodep->modVarp()->direction().xmlKwd() + "\"");
}
puts(" portIndex=\"" + cvtToStr(nodep->pinNum()) + "\""); // IEEE: vpiPortIndex
@@ -254,7 +254,7 @@ class EmitXmlFileVisitor final : public VNVisitorConst {
void visit(AstNodeCCall* nodep) override {
outputTag(nodep, "");
puts(" func=");
putsQuoted(nodep->funcp()->name());
putsQuoted(nodep->funcp() ? nodep->funcp()->name() : nodep->name());
outputChildrenEnd(nodep, "");
}
@@ -401,7 +401,7 @@ private:
}
}
void visit(AstCell* nodep) override {
if (nodep->modp()->dead()) return;
if (nodep->modp() && nodep->modp()->dead()) return;
if (!m_hasChildren) m_os << ">\n";
m_os << "<cell " << nodep->fileline()->xmlDetailedLocation() << " name=\"" << nodep->name()
<< "\""
+45 -15
View File
@@ -220,22 +220,20 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra)
tellManual(2);
}
#ifndef V3ERROR_NO_GLOBAL_
if (dumpTree() || debug()) {
if (dumpTreeLevel() || debug()) {
V3Broken::allowMidvisitorCheck(true);
V3ThreadPool::s().requestExclusiveAccess([&]() VL_REQUIRES(m_mutex) {
if (dumpTree()) {
v3Global.rootp()->dumpTreeFile(
v3Global.debugFilename("final.tree", 990));
}
if (debug()) {
execErrorExitCb();
V3Stats::statsFinalAll(v3Global.rootp());
V3Stats::statsReport();
}
// Abort in exclusive access to make sure other threads
// don't change error code
vlAbortOrExit();
});
const V3ThreadPool::ScopedExclusiveAccess exclusiveAccess;
if (dumpTreeLevel()) {
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("final.tree", 990));
}
if (debug()) {
execErrorExitCb();
V3Stats::statsFinalAll(v3Global.rootp());
V3Stats::statsReport();
}
// Abort in exclusive access to make sure other threads
// don't change error code
vlAbortOrExit();
}
#endif
}
@@ -293,3 +291,35 @@ void V3Error::vlAbort() {
VL_GCOV_DUMP();
std::abort();
}
void V3Error::v3errorAcquireLock() VL_ACQUIRE(s().m_mutex) {
#ifndef V3ERROR_NO_GLOBAL_
V3Error::s().m_mutex.lockCheckStopRequest(
[]() -> void { V3ThreadPool::s().waitIfStopRequested(); });
#else
V3Error::s().m_mutex.lock();
#endif
}
std::ostringstream& V3Error::v3errorPrep(V3ErrorCode code) VL_ACQUIRE(s().m_mutex) {
v3errorAcquireLock();
s().v3errorPrep(code);
return v3errorStr();
}
std::ostringstream& V3Error::v3errorPrepFileLine(V3ErrorCode code, const char* file, int line)
VL_ACQUIRE(s().m_mutex) {
v3errorPrep(code) << file << ":" << std::dec << line << ": ";
return v3errorStr();
}
std::ostringstream& V3Error::v3errorStr() VL_REQUIRES(s().m_mutex) { return s().v3errorStr(); }
void V3Error::v3errorEnd(std::ostringstream& sstr, const string& extra) VL_RELEASE(s().m_mutex) {
s().v3errorEnd(sstr, extra);
V3Error::s().m_mutex.unlock();
}
void v3errorEnd(std::ostringstream& sstr) VL_RELEASE(V3Error::s().m_mutex) {
V3Error::v3errorEnd(sstr);
}
void v3errorEndFatal(std::ostringstream& sstr) VL_RELEASE(V3Error::s().m_mutex) {
V3Error::v3errorEnd(sstr);
assert(0); // LCOV_EXCL_LINE
VL_UNREACHABLE;
}

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