Compare commits

...
79 Commits
Author SHA1 Message Date
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
238 changed files with 5412 additions and 1437 deletions
+6 -6
View File
@@ -36,9 +36,9 @@ jobs:
m32: [0, 1] m32: [0, 1]
exclude: exclude:
# Build pull requests only with ubuntu-22.04 and without m32 # 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-18.04' || 'do-not-exclude' }}
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-20.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' }} # - m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
# Build -m32 only on ubuntu-22.04 clang++ # Build -m32 only on ubuntu-22.04 clang++
- {os: ubuntu-18.04, m32: 1} - {os: ubuntu-18.04, m32: 1}
- {os: ubuntu-20.04, m32: 1} - {os: ubuntu-20.04, m32: 1}
@@ -107,9 +107,9 @@ jobs:
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, vltmt-0, vltmt-1] suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, vltmt-0, vltmt-1]
exclude: exclude:
# Build pull requests only with ubuntu-22.04 and without m32 # 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-18.04' || 'do-not-exclude' }}
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-20.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' }} # - m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
# Build -m32 only on ubuntu-22.04 # Build -m32 only on ubuntu-22.04
- {os: ubuntu-18.04, m32: 1} - {os: ubuntu-18.04, m32: 1}
- {os: ubuntu-20.04, m32: 1} - {os: ubuntu-20.04, m32: 1}
+1 -1
View File
@@ -15,7 +15,7 @@
cmake_minimum_required(VERSION 3.15) cmake_minimum_required(VERSION 3.15)
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
project(Verilator project(Verilator
VERSION 5.012 VERSION 5.014
HOMEPAGE_URL https://verilator.org HOMEPAGE_URL https://verilator.org
LANGUAGES CXX LANGUAGES CXX
) )
+49
View File
@@ -8,6 +8,55 @@ The changes in each Verilator version are described below. The
contributors that suggested a given feature are shown in []. Thanks! contributors that suggested a given feature are shown in []. Thanks!
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 Verilator 5.012 2023-06-13
========================== ==========================
+2 -2
View File
@@ -449,8 +449,8 @@ install-msg:
IN_WILD := ${srcdir}/*.in ${srcdir}/*/*.in IN_WILD := ${srcdir}/*.in ${srcdir}/*/*.in
# autoheader might not change config_build.h.in, so touch it # autoheader might not change config_package.h.in, so touch it
${srcdir}/config_build.h: ${srcdir}/config_build.h.in configure ${srcdir}/config_package.h: ${srcdir}/config_package.h.in configure
cd ${srcdir} && autoheader cd ${srcdir} && autoheader
touch $@ touch $@
Makefile: Makefile.in config.status $(IN_WILD) Makefile: Makefile.in config.status $(IN_WILD)
+5 -3
View File
@@ -1,20 +1,22 @@
.. Github doesn't render images unless absolute URL .. Github doesn't render images unless absolute URL
.. Do not know of a conditional tag, "only: github" nor "github display" works .. 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 .. 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 .. image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
:target: https://opensource.org/licenses/Artistic-2.0 :target: https://opensource.org/licenses/Artistic-2.0
.. image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages .. image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
:target: https://repology.org/project/verilator/versions :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 .. image:: https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f
:target: https://www.codacy.com/gh/verilator/verilator :target: https://www.codacy.com/gh/verilator/verilator
.. image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg .. image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg
:target: https://codecov.io/gh/verilator/verilator :target: https://codecov.io/gh/verilator/verilator
.. image:: https://github.com/verilator/verilator/workflows/build/badge.svg .. image:: https://github.com/verilator/verilator/workflows/build/badge.svg
:target: https://github.com/verilator/verilator/actions?query=workflow%3Abuild :target: https://github.com/verilator/verilator/actions?query=workflow%3Abuild
.. image:: https://img.shields.io/docker/pulls/verilator/verilator
:target: https://hub.docker.com/r/verilator/verilator
Welcome to Verilator Welcome to Verilator
+2 -2
View File
@@ -10,11 +10,11 @@
# Then 'make maintainer-dist' # Then 'make maintainer-dist'
#AC_INIT([Verilator],[#.### YYYY-MM-DD]) #AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel]) #AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[5.012 2023-06-13], AC_INIT([Verilator],[5.014 2023-08-06],
[https://verilator.org], [https://verilator.org],
[verilator],[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) 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 # Version
+11 -5
View File
@@ -13,7 +13,9 @@ Aliaksei Chapyzhenka
Ameya Vikram Singh Ameya Vikram Singh
Andreas Kuster Andreas Kuster
Andrei Kostovski Andrei Kostovski
Andrew Miloradovsky
Andrew Nolte Andrew Nolte
Anthony Donlon
Arkadiusz Kozdra Arkadiusz Kozdra
Aylon Chaim Porat Aylon Chaim Porat
Cameron Kirk Cameron Kirk
@@ -38,6 +40,7 @@ Fan Shupei
february cozzocrea february cozzocrea
Felix Neumärker Felix Neumärker
Felix Yan Felix Yan
G-A. Kamendje
Garrett Smith Garrett Smith
Geza Lore Geza Lore
Gianfranco Costamagna Gianfranco Costamagna
@@ -46,7 +49,6 @@ Gökçe Aydos
Graham Rushton Graham Rushton
Guokai Chen Guokai Chen
Gustav Svensk Gustav Svensk
G-A. Kamendje
Harald Heckmann Harald Heckmann
Hennadii Chernyshchyk Hennadii Chernyshchyk
Howard Su Howard Su
@@ -54,9 +56,9 @@ Huang Rui
Huanghuang Zhou Huanghuang Zhou
HungMingWu HungMingWu
HyungKi Jeong HyungKi Jeong
Ilya Barkov
Iru Cai Iru Cai
Ivan Vnučec Ivan Vnučec
Ilya Barkov
Iztok Jeras Iztok Jeras
Jake Merdich Jake Merdich
James Hanlon James Hanlon
@@ -71,17 +73,18 @@ Jeremy Bennett
Jesse Taube Jesse Taube
Jevin Sweval Jevin Sweval
Jiacheng Qian Jiacheng Qian
Jiamin Zhu
Jiuyang Liu Jiuyang Liu
Joey Liu Joey Liu
John Coiner John Coiner
John Demme John Demme
John Wehle John Wehle
Jiamin Zhu
Jonathan Drolet Jonathan Drolet
Jose Loyola Jose Loyola
Joseph Nwabueze
Josep Sans Josep Sans
Joseph Nwabueze
Josh Redford Josh Redford
Julian Daube
Julie Schwartz Julie Schwartz
Julien Margetts Julien Margetts
Kaleb Barrett Kaleb Barrett
@@ -91,6 +94,7 @@ Keith Colbert
Kevin Kiningham Kevin Kiningham
Kritik Bhimani Kritik Bhimani
Krzysztof Bieganski Krzysztof Bieganski
Krzysztof Boronski
Krzysztof Boroński Krzysztof Boroński
Kuba Ober Kuba Ober
Larry Doolittle Larry Doolittle
@@ -162,15 +166,17 @@ Varun Koyyalagunta
Vassilis Papaefstathiou Vassilis Papaefstathiou
Veripool API Bot Veripool API Bot
Victor Besyakov Victor Besyakov
Vito Gamberini
William D. Jones William D. Jones
Wilson Snyder Wilson Snyder
Xi Zhang Xi Zhang
Yinan Xu Yinan Xu
Yoda Lee Yoda Lee
Yossi Nivin Yossi Nivin
Yu-Sheng Lin
Yuri Victorovich Yuri Victorovich
Yutetsu TAKATSUKASA Yutetsu TAKATSUKASA
Yu-Sheng Lin
Yves Mathieu Yves Mathieu
Zhanglei Wang Zhanglei Wang
Zixi Li Zixi Li
أحمد المحمودي
+5 -1
View File
@@ -18,12 +18,16 @@
RST2HTML = rst2html RST2HTML = rst2html
PYTHON3 = python3 PYTHON3 = python3
DOXYGEN = doxygen DOXYGEN = doxygen
SPHINXOPTS ?= -c guide
SPHINXBUILD ?= sphinx-build SPHINXBUILD ?= sphinx-build
SOURCEDIR = guide SOURCEDIR = guide
BUILDDIR = _build BUILDDIR = _build
SPHINXOPTS ?= -c guide
ifneq ($(VERILATOR_ANALYTICS_ID),)
SPHINXOPTS += -D html_theme_options.analytics_id=$(VERILATOR_ANALYTICS_ID)
endif
###################################################################### ######################################################################
.SUFFIXES: .SUFFIXES:
-1
View File
@@ -115,7 +115,6 @@ html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
html_theme_options = { html_theme_options = {
'analytics_id': 'G-D27B0C9QEB',
'logo_only': True, 'logo_only': True,
'style_nav_header_background': "#45acf8", # Default is #2980B9 'style_nav_header_background': "#45acf8", # Default is #2980B9
# 'canonical_url': # 'canonical_url':
+4 -4
View File
@@ -17,10 +17,10 @@ C++11 compiler support
Verilator will require C++20 or newer compilers for both compiling Verilator will require C++20 or newer compilers for both compiling
Verilator and compiling all Verilated models no sooner than January 2025. Verilator and compiling all Verilated models no sooner than January 2025.
Verilated_heavy.h 32-bit compiler support
The legacy "verilated_heavy.h" include was replaced with just including Verilator currently regresses both 64-bit and 32-bit pointer modes (GCC's
"verilated.h". Verilated_heavy.h is planned for removal no sooner than `-m64` and `-m32`). Support for 32-bit `-m32` mode will be deprecated no
July 2022. sooner than January 2024.
Option `-O<letter>` Option `-O<letter>`
The debug `-O<letter>` options have been replaced with The debug `-O<letter>` options have been replaced with
+7 -6
View File
@@ -1539,7 +1539,7 @@ Summary:
Disable all code style related warning messages (note that by default, they are Disable all code style related warning messages (note that by default, they are
already disabled). This is equivalent to ``-Wno-DECLFILENAME -Wno-DEFPARAM already disabled). This is equivalent to ``-Wno-DECLFILENAME -Wno-DEFPARAM
-Wno-EOFNEWLINE -Wno-IMPORTSTAR -Wno-INCABSPATH -Wno-PINCONNECTEMPTY -Wno-EOFNEWLINE -Wno-GENUNNAMED -Wno-IMPORTSTAR -Wno-INCABSPATH -Wno-PINCONNECTEMPTY
-Wno-PINNOCONNECT -Wno-SYNCASYNCNET -Wno-UNDRIVEN -Wno-PINNOCONNECT -Wno-SYNCASYNCNET -Wno-UNDRIVEN
-Wno-UNUSEDGENVAR -Wno-UNUSEDPARAM -Wno-UNUSEDSIGNAL -Wno-UNUSEDGENVAR -Wno-UNUSEDPARAM -Wno-UNUSEDSIGNAL
-Wno-VARHIDDEN``. -Wno-VARHIDDEN``.
@@ -1562,16 +1562,17 @@ Summary:
Enable all lint-related warning messages (note that by default, they are already Enable all lint-related warning messages (note that by default, they are already
enabled), but do not affect style messages. This is equivalent to enabled), but do not affect style messages. This is equivalent to
``-Wwarn-ALWCOMBORDER -Wwarn-BSSPACE -Wwarn-CASEINCOMPLETE ``-Wwarn-ALWCOMBORDER -Wwarn-ASCRANGE -Wwarn-BSSPACE -Wwarn-CASEINCOMPLETE
-Wwarn-CASEOVERLAP -Wwarn-CASEX -Wwarn-CASTCONST -Wwarn-CASEWITHX -Wwarn-CMPCONST -Wwarn-CASEOVERLAP -Wwarn-CASEWITHX -Wwarn-CASEX -Wwarn-CASTCONST -Wwarn-CMPCONST
-Wwarn-COLONPLUS -Wwarn-IMPLICIT -Wwarn-ASCRANGE -Wwarn-COLONPLUS -Wwarn-IMPLICIT -Wwarn-IMPLICITSTATIC -Wwarn-LATCH -Wwarn-MISINDENT
-Wwarn-PINMISSING -Wwarn-REALCVT -Wwarn-UNSIGNED -Wwarn-WIDTH``. -Wwarn-NEWERSTD -Wwarn-PINMISSING -Wwarn-REALCVT -Wwarn-STATICVAR -Wwarn-UNSIGNED
-Wwarn-WIDTHTRUNC -Wwarn-WIDTHEXPAND -Wwarn-WIDTHXZEXPAND``.
.. option:: -Wwarn-style .. option:: -Wwarn-style
Enable all code style-related warning messages. This is equivalent to Enable all code style-related warning messages. This is equivalent to
``-Wwarn ASSIGNDLY -Wwarn-DECLFILENAME -Wwarn-DEFPARAM -Wwarn-EOFNEWLINE ``-Wwarn ASSIGNDLY -Wwarn-DECLFILENAME -Wwarn-DEFPARAM -Wwarn-EOFNEWLINE
-Wwarn-INCABSPATH -Wwarn-PINNOCONNECT -Wwarn-SYNCASYNCNET -Wwarn-UNDRIVEN -Wwarn-GENUNNAMED -Wwarn-INCABSPATH -Wwarn-PINNOCONNECT -Wwarn-SYNCASYNCNET -Wwarn-UNDRIVEN
-Wwarn-UNUSEDGENVAR -Wwarn-UNUSEDPARAM -Wwarn-UNUSEDSIGNAL -Wwarn-VARHIDDEN``. -Wwarn-UNUSEDGENVAR -Wwarn-UNUSEDPARAM -Wwarn-UNUSEDSIGNAL -Wwarn-VARHIDDEN``.
.. option:: --x-assign 0 .. option:: --x-assign 0
+1 -1
View File
@@ -486,7 +486,7 @@ From the sc_main.cpp file, you'd then:
#include "Vour.h" #include "Vour.h"
#include "Vour_our.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. In this example, clk is a bool you can read or set as any other variable.
+1 -1
View File
@@ -500,7 +500,7 @@ feedback-directed optimization. See the appropriate compiler
documentation. documentation.
.. _Runtime Debugging .. _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 To support this, none of the files that Verilator produces will reference
any configure-generated build-system-specific files, such as 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 to reduce confusion.) The disadvantage of this approach is that
:file:`include/verilatedos.h` must self-detect the requirements of the :file:`include/verilatedos.h` must self-detect the requirements of the
target system, rather than using configure. target system, rather than using configure.
+187
View File
@@ -670,6 +670,52 @@ List Of Warnings
used as a clock. 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 .. option:: HIERBLOCK
Warns that the top module is marked as a hierarchy block by the Warns that the top module is marked as a hierarchy block by the
@@ -876,6 +922,102 @@ List Of Warnings
Ignoring this warning will only suppress the lint check; it will Ignoring this warning will only suppress the lint check; it will
simulate correctly. 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 .. option:: LITENDIAN
@@ -897,6 +1039,51 @@ List Of Warnings
unsupported. Verilator uses only the typical delay value. 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 .. option:: MODDUP
.. TODO better example .. TODO better example
+1 -1
View File
@@ -2123,7 +2123,7 @@ Or in a hand-written C++ wrapper:
:: ::
#ifdef TEST_VERBOSE #ifdef TEST_VERBOSE
cout << "Read a=" << a << endl; std::cout << "Read a=" << a << std::endl;
#endif #endif
A filename that should be used to check the output results is given with A filename that should be used to check the output results is given with
+5 -1
View File
@@ -56,6 +56,7 @@ Das
Dcache Dcache
Deadman Deadman
Debacker Debacker
Deepa
Defparams Defparams
Denio Denio
Deprecations Deprecations
@@ -246,6 +247,7 @@ Olof
Olofsson Olofsson
Oyvind Oyvind
PLI PLI
Palaniappan
Patricio Patricio
Petr Petr
Piechotka Piechotka
@@ -376,6 +378,7 @@ Wfuture
Whatson Whatson
Wildman Wildman
Wim Wim
Wmisleading
Wno Wno
Wojciech Wojciech
Wolfel Wolfel
@@ -410,6 +413,7 @@ architected
args args
arrarys arrarys
assertOn assertOn
astgen
async async
ato ato
atoi atoi
@@ -905,6 +909,7 @@ undef
undefineall undefineall
undriven undriven
ungetc ungetc
unhandled
uniquified uniquified
unistd unistd
unlink unlink
@@ -971,4 +976,3 @@ ypq
yurivict yurivict
zdave zdave
Øyvind Øyvind
+6
View File
@@ -1896,6 +1896,12 @@ std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_PURE {
return std::string{destout, len}; 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 VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE {
std::string lstring = lhs; std::string lstring = lhs;
const int32_t rhs_s = rhs; // To signed value const int32_t rhs_s = rhs; // To signed value
+4 -4
View File
@@ -210,8 +210,8 @@ private:
std::string result = prefix + "*" + suffix; std::string result = prefix + "*" + suffix;
// cout << "\nch pre="<<prefix<<" s="<<suffix<<"\nch a="<<old<<"\nch b="<<add // std::cout << "\nch pre=" << prefix << " s=" << suffix << "\nch a="
// <<"\ncho="<<result<<endl; // << old << "\nch b=" << add << "\ncho=" << result << std::endl;
return result; return result;
} }
bool itemMatchesString(VerilatedCovImpItem* itemp, const std::string& match) bool itemMatchesString(VerilatedCovImpItem* itemp, const std::string& match)
@@ -316,7 +316,7 @@ public:
valps[2] = page_default.c_str(); valps[2] = page_default.c_str();
// Keys -> strings // 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) { for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
if (ckeyps[i] && ckeyps[i][0]) keys[i] = ckeyps[i]; if (ckeyps[i] && ckeyps[i][0]) keys[i] = ckeyps[i];
} }
@@ -337,7 +337,7 @@ public:
const std::string key = keys[i]; const std::string key = keys[i];
if (!keys[i].empty()) { if (!keys[i].empty()) {
const std::string val = valps[i]; 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_keys[addKeynum] = valueIndex(key);
m_insertp->m_vals[addKeynum] = valueIndex(val); m_insertp->m_vals[addKeynum] = valueIndex(val);
++addKeynum; ++addKeynum;
+1
View File
@@ -2196,6 +2196,7 @@ extern IData VL_DIST_UNIFORM(IData& seedr, IData ustart, IData uend) VL_MT_SAFE;
// Conversion functions // Conversion functions
extern std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_PURE; 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 { inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE {
VlWide<VL_WQ_WORDS_E> lw; VlWide<VL_WQ_WORDS_E> lw;
VL_SET_WQ(lw, lhs); 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
-30
View File
@@ -86,36 +86,6 @@ public:
#endif #endif
}; };
//===================================================================
// 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"); }
//============================================================================= //=============================================================================
// VlCoroutineHandle is a non-copyable (but movable) coroutine handle. On resume, the handle is // VlCoroutineHandle is a non-copyable (but movable) coroutine handle. On resume, the handle is
// cleared, as we assume that either the coroutine has finished and deleted itself, or, if it got // cleared, as we assume that either the coroutine has finished and deleted itself, or, if it got
+91 -4
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_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 #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 // Activity trigger vector
@@ -487,7 +517,7 @@ public:
template <typename Func> template <typename Func>
VlQueue unique(Func with_func) const { VlQueue unique(Func with_func) const {
VlQueue out; VlQueue out;
std::set<T_Value> saw; std::set<decltype(with_func(0, m_deque[0]))> saw;
for (const auto& i : m_deque) { for (const auto& i : m_deque) {
const auto i_mapped = with_func(0, i); const auto i_mapped = with_func(0, i);
const auto it = saw.find(i_mapped); const auto it = saw.find(i_mapped);
@@ -516,7 +546,7 @@ public:
VlQueue<IData> unique_index(Func with_func) const { VlQueue<IData> unique_index(Func with_func) const {
VlQueue<IData> out; VlQueue<IData> out;
IData index = 0; 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) { for (const auto& i : m_deque) {
const auto i_mapped = with_func(index, i); const auto i_mapped = with_func(index, i);
auto it = saw.find(i_mapped); auto it = saw.find(i_mapped);
@@ -830,6 +860,22 @@ public:
} }
return out; 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> unique_index() const {
VlQueue<T_Key> out; VlQueue<T_Key> out;
std::set<T_Key> saw; std::set<T_Key> saw;
@@ -843,6 +889,21 @@ public:
return out; return out;
} }
template <typename Func> 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> find(Func with_func) const {
VlQueue<T_Value> out; VlQueue<T_Value> out;
for (const auto& i : m_map) for (const auto& i : m_map)
@@ -903,6 +964,16 @@ public:
}); });
return VlQueue<T_Value>::cons(it->second); 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 { VlQueue<T_Value> max() const {
if (m_map.empty()) return VlQueue<T_Value>(); if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::max_element( const auto it = std::max_element(
@@ -912,6 +983,16 @@ public:
}); });
return VlQueue<T_Value>::cons(it->second); 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 r_sum() const {
T_Value out(0); // Type must have assignment operator T_Value out(0); // Type must have assignment operator
@@ -1457,8 +1538,14 @@ public:
// For 'if (ptr)...' // For 'if (ptr)...'
operator bool() const { return m_objp; } operator bool() const { return m_objp; }
// In SV A == B iff both are handles to the same object (IEEE 1800-2017 8.4) // 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; }; template <typename T_OtherClass>
bool operator!=(const VlClassRef& rhs) const { return m_objp != rhs.m_objp; }; 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> template <typename T, typename U>
+1 -1
View File
@@ -2307,7 +2307,7 @@ PLI_INT32 vpi_flush(void) {
VerilatedVpiImp::assertOneCheck(); VerilatedVpiImp::assertOneCheck();
VL_VPI_ERROR_RESET_(); VL_VPI_ERROR_RESET_();
Verilated::runFlushCallbacks(); Verilated::runFlushCallbacks();
return 0; return 0; // Gcc coverage bug // LCOV_EXCL_LINE
} }
PLI_INT32 vpi_mcd_flush(PLI_UINT32 mcd) { PLI_INT32 vpi_mcd_flush(PLI_UINT32 mcd) {
+4
View File
@@ -119,6 +119,10 @@
// Allowed on: function, method. (-fthread-safety) // Allowed on: function, method. (-fthread-safety)
#define VL_RETURN_CAPABILITY(x) \ #define VL_RETURN_CAPABILITY(x) \
VL_CLANG_ATTR(lock_returned(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))
// Defaults for unsupported compiler features // Defaults for unsupported compiler features
#ifndef VL_ATTR_ALWINLINE #ifndef VL_ATTR_ALWINLINE
+1 -1
View File
@@ -1,6 +1,6 @@
*~ *~
*.old *.old
config_build.h config_package.h
Makefile Makefile
Makefile_obj Makefile_obj
.objcache* .objcache*
+4 -2
View File
@@ -82,6 +82,7 @@ set(HEADERS
V3File.h V3File.h
V3FileLine.h V3FileLine.h
V3Force.h V3Force.h
V3Fork.h
V3FunctionTraits.h V3FunctionTraits.h
V3Gate.h V3Gate.h
V3Global.h V3Global.h
@@ -227,6 +228,7 @@ set(COMMON_SOURCES
V3File.cpp V3File.cpp
V3FileLine.cpp V3FileLine.cpp
V3Force.cpp V3Force.cpp
V3Fork.cpp
V3Gate.cpp V3Gate.cpp
V3Global.cpp V3Global.cpp
V3Graph.cpp V3Graph.cpp
@@ -322,7 +324,7 @@ file(TO_NATIVE_PATH ${srcdir}/../bin/verilator_includer VERILATOR_INCLUDER)
file(TO_NATIVE_PATH ${srcdir}/vlcovgen VLCOVGEN) file(TO_NATIVE_PATH ${srcdir}/vlcovgen VLCOVGEN)
file(TO_NATIVE_PATH ${srcdir}/config_rev CONFIG_REV) 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( add_custom_command(
OUTPUT V3Ast__gen_forward_class_decls.h V3Dfg__gen_forward_class_decls.h OUTPUT V3Ast__gen_forward_class_decls.h V3Dfg__gen_forward_class_decls.h
@@ -414,7 +416,7 @@ foreach(astgen_name ${ASTGENERATED_NAMES})
MAIN_DEPENDENCY ${astgen_name}.cpp MAIN_DEPENDENCY ${astgen_name}.cpp
DEPENDS ${ASTGEN} V3Ast.h DEPENDS ${ASTGEN} V3Ast.h
COMMAND ${PYTHON3} ARGS 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) list(APPEND GENERATED_FILES ${astgen_name}__gen.cpp)
endforeach() endforeach()
+1 -1
View File
@@ -95,7 +95,7 @@ clean mostlyclean distclean maintainer-clean::
-rm -f .objcache* -rm -f .objcache*
distclean maintainer-clean:: distclean maintainer-clean::
-rm -f Makefile Makefile_obj config_build.h -rm -f Makefile Makefile_obj config_package.h
maintainer-clean:: maintainer-clean::
-rm -f config_rev.h -rm -f config_rev.h
+1
View File
@@ -205,6 +205,7 @@ RAW_OBJS = \
V3File.o \ V3File.o \
V3FileLine.o \ V3FileLine.o \
V3Force.o \ V3Force.o \
V3Fork.o \
V3Gate.o \ V3Gate.o \
V3Global.o \ V3Global.o \
V3Graph.o \ V3Graph.o \
+1 -1
View File
@@ -157,7 +157,7 @@ private:
sentreep->unlinkFrBack(); sentreep->unlinkFrBack();
if (m_procedurep) { if (m_procedurep) {
// To support this need queue of asserts to activate // 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)"); " clocking event inside always (IEEE 1800-2917 16.14.6)");
} }
} }
+11 -8
View File
@@ -125,9 +125,7 @@ string AstNode::encodeNumber(int64_t num) {
} }
} }
string AstNode::nameProtect() const VL_MT_STABLE { string AstNode::nameProtect() const { return VIdProtect::protectIf(name(), protect()); }
return VIdProtect::protectIf(name(), protect());
}
string AstNode::origNameProtect() const { return VIdProtect::protectIf(origName(), protect()); } string AstNode::origNameProtect() const { return VIdProtect::protectIf(origName(), protect()); }
string AstNode::shortName() const { string AstNode::shortName() const {
@@ -285,7 +283,7 @@ string AstNode::vpiName(const string& namein) {
return pretty; return pretty;
} }
string AstNode::prettyTypeName() const VL_MT_STABLE { string AstNode::prettyTypeName() const {
if (name() == "") return typeName(); if (name() == "") return typeName();
return std::string{typeName()} + " '" + prettyName() + "'"; return std::string{typeName()} + " '" + prettyName() + "'";
} }
@@ -1081,10 +1079,12 @@ bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ig
// private: Return true if the two trees are identical // private: Return true if the two trees are identical
if (!node1p && !node2p) return true; if (!node1p && !node2p) return true;
if (!node1p || !node2p) return false; if (!node1p || !node2p) return false;
if (node1p->type() != node2p->type() || node1p->dtypep() != node2p->dtypep() if (node1p->type() != node2p->type()) return false;
|| !node1p->same(node2p) || (gateOnly && !node1p->isGateOptimizable())) { UASSERT_OBJ(
return false; (!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) return (sameTreeIter(node1p->m_op1p, node2p->m_op1p, false, gateOnly)
&& sameTreeIter(node1p->m_op2p, node2p->m_op2p, false, gateOnly) && sameTreeIter(node1p->m_op2p, node2p->m_op2p, false, gateOnly)
&& sameTreeIter(node1p->m_op3p, node2p->m_op3p, false, gateOnly) && sameTreeIter(node1p->m_op3p, node2p->m_op3p, false, gateOnly)
@@ -1406,6 +1406,9 @@ AstNodeDType* AstNode::findQueueIndexDType() const {
AstNodeDType* AstNode::findVoidDType() const { AstNodeDType* AstNode::findVoidDType() const {
return v3Global.rootp()->typeTablep()->findVoidDType(fileline()); return v3Global.rootp()->typeTablep()->findVoidDType(fileline());
} }
AstNodeDType* AstNode::findStreamDType() const {
return v3Global.rootp()->typeTablep()->findStreamDType(fileline());
}
//###################################################################### //######################################################################
// VNDeleter // VNDeleter
+34 -11
View File
@@ -82,6 +82,17 @@ using MTaskIdSet = std::set<int>; // Set of mtaskIds for Var sorting
// Ast<nodetypename>*' is returned. // Ast<nodetypename>*' is returned.
#define VN_AS(nodep, nodetypename) (AstNode::privateAs<Ast##nodetypename, decltype(nodep)>(nodep)) #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) // (V)erilator (N)ode deleted: Pointer to deleted AstNode (for assertions only)
#define VN_DELETED(nodep) VL_UNLIKELY((uint64_t)(nodep) == 0x1) #define VN_DELETED(nodep) VL_UNLIKELY((uint64_t)(nodep) == 0x1)
@@ -1686,7 +1697,7 @@ public:
static constexpr int INSTR_COUNT_PLI = 20; // PLI routines static constexpr int INSTR_COUNT_PLI = 20; // PLI routines
// ACCESSORS // ACCESSORS
virtual string name() const VL_MT_STABLE { return ""; } virtual string name() const { return ""; }
virtual string origName() const { return ""; } virtual string origName() const { return ""; }
virtual void name(const string& name) { virtual void name(const string& name) {
this->v3fatalSrc("name() called on object without name() method"); this->v3fatalSrc("name() called on object without name() method");
@@ -1694,7 +1705,7 @@ public:
virtual void tag(const string& text) {} virtual void tag(const string& text) {}
virtual string tag() const { return ""; } virtual string tag() const { return ""; }
virtual string verilogKwd() 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 origNameProtect() const; // origName with --protect-id applied
string shortName() const; // Name with __PVT__ removed for concatenating scopes string shortName() const; // Name with __PVT__ removed for concatenating scopes
static string dedotName(const string& namein); // Name with dots removed static string dedotName(const string& namein); // Name with dots removed
@@ -1707,10 +1718,10 @@ public:
static string encodeName(const string& namein); static string encodeName(const string& namein);
static string encodeNumber(int64_t num); // Encode number into internal C representation 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 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()); } string prettyNameQ() const { return prettyNameQ(name()); }
// "VARREF" for error messages (NOT dtype's pretty 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(); } virtual string prettyOperatorName() const { return "operator " + prettyTypeName(); }
FileLine* fileline() const VL_MT_SAFE { return m_fileline; } FileLine* fileline() const VL_MT_SAFE { return m_fileline; }
void fileline(FileLine* fl) { m_fileline = fl; } void fileline(FileLine* fl) { m_fileline = fl; }
@@ -1867,6 +1878,7 @@ public:
void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate
void dtypeSetEmptyQueue() { dtypep(findEmptyQueueDType()); } void dtypeSetEmptyQueue() { dtypep(findEmptyQueueDType()); }
void dtypeSetVoid() { dtypep(findVoidDType()); } void dtypeSetVoid() { dtypep(findVoidDType()); }
void dtypeSetStream() { dtypep(findStreamDType()); }
// Data type locators // Data type locators
AstNodeDType* findBitDType() const { return findBasicDType(VBasicDTypeKwd::LOGIC); } AstNodeDType* findBitDType() const { return findBasicDType(VBasicDTypeKwd::LOGIC); }
@@ -1878,6 +1890,7 @@ public:
AstNodeDType* findCHandleDType() const { return findBasicDType(VBasicDTypeKwd::CHANDLE); } AstNodeDType* findCHandleDType() const { return findBasicDType(VBasicDTypeKwd::CHANDLE); }
AstNodeDType* findEmptyQueueDType() const; AstNodeDType* findEmptyQueueDType() const;
AstNodeDType* findVoidDType() const; AstNodeDType* findVoidDType() const;
AstNodeDType* findStreamDType() const;
AstNodeDType* findQueueIndexDType() const; AstNodeDType* findQueueIndexDType() const;
AstNodeDType* findBitDType(int width, int widthMin, VSigning numeric) const; AstNodeDType* findBitDType(int width, int widthMin, VSigning numeric) const;
AstNodeDType* findLogicDType(int width, int widthMin, VSigning numeric) const; AstNodeDType* findLogicDType(int width, int widthMin, VSigning numeric) const;
@@ -2065,24 +2078,34 @@ public:
return nodep && privateTypeTest<T>(nodep) ? reinterpret_cast<const T*>(nodep) : nullptr; 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> 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(!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."); 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); return reinterpret_cast<T*>(nodep);
} }
template <typename T, typename E> 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(!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."); 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, UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
"AstNode is not of expected type, but instead has type '" << nodep->typeName() "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'. // Predicate that returns true if the given 'nodep' might have a descendant of type 'T_Node'.
+7 -3
View File
@@ -154,12 +154,15 @@ AstCExpr::AstCExpr(FileLine* fl, const string& textStmt, int setwidth, bool clea
} }
AstVarRef::AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access) 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) // This form only allowed post-link (see above)
AstVarRef::AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access) 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); varScopep(varscp);
} }
string AstVarRef::name() const { return varp() ? varp()->name() : "<null>"; }
bool AstVarRef::same(const AstVarRef* samep) const { bool AstVarRef::same(const AstVarRef* samep) const {
if (varScopep()) { if (varScopep()) {
return (varScopep() == samep->varScopep() && access() == samep->access()); return (varScopep() == samep->varScopep() && access() == samep->access());
@@ -179,7 +182,8 @@ bool AstVarRef::sameNoLvalue(AstVarRef* samep) const {
} }
AstVarXRef::AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access) 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} { , m_dotted{dotted} {
dtypeFrom(varp); dtypeFrom(varp);
} }
+33 -13
View File
@@ -126,13 +126,13 @@ public:
const char* charIQWN() const { const char* charIQWN() const {
return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I"); 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) // Represents a C++ LiteralType? (can be constexpr)
bool isLiteralType() const VL_MT_STABLE; bool isLiteralType() const VL_MT_STABLE;
private: private:
class CTypeRecursed; class CTypeRecursed;
CTypeRecursed cTypeRecurse(bool compound) const VL_MT_STABLE; CTypeRecursed cTypeRecurse(bool compound) const;
}; };
class AstNodeArrayDType VL_NOT_FINAL : public AstNodeDType { class AstNodeArrayDType VL_NOT_FINAL : public AstNodeDType {
// Array data type, ie "some_dtype var_name [2:0]" // 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 // A struct or union; common handling
// @astgen op1 := membersp : List[AstMemberDType] // @astgen op1 := membersp : List[AstMemberDType]
private: private:
// TYPES
using MemberNameMap = std::map<const std::string, AstMemberDType*>;
// MEMBERS // MEMBERS
string m_name; // Name from upper typedef, if any string m_name; // Name from upper typedef, if any
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy AstNodeModule* m_classOrPackagep = nullptr; // Package it will be emitted with
MemberNameMap m_members;
const int m_uniqueNum; const int m_uniqueNum;
bool m_packed; bool m_packed;
bool m_isFourstate = false; // V3Width computes bool m_isFourstate = false; // V3Width computes
@@ -250,12 +247,6 @@ public:
static bool packedUnsup() { return true; } static bool packedUnsup() { return true; }
void isFourstate(bool flag) { m_isFourstate = flag; } void isFourstate(bool flag) { m_isFourstate = flag; }
bool isFourstate() const override VL_MT_SAFE { return m_isFourstate; } 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; } static int lo() { return 0; }
int hi() const { return dtypep()->width() - 1; } // Packed classes look like arrays int hi() const { return dtypep()->width() - 1; } // Packed classes look like arrays
VNumRange declRange() const { return VNumRange{hi(), lo()}; } VNumRange declRange() const { return VNumRange{hi(), lo()}; }
@@ -1062,7 +1053,7 @@ private:
// Post-width typedefs are removed and point to type directly // Post-width typedefs are removed and point to type directly
AstNodeDType* m_refDTypep = nullptr; // data type pointed to, BELOW the AstTypedef AstNodeDType* m_refDTypep = nullptr; // data type pointed to, BELOW the AstTypedef
string m_name; // Name of an 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: public:
AstRefDType(FileLine* fl, const string& name) AstRefDType(FileLine* fl, const string& name)
: ASTGEN_SUPER_RefDType(fl) : ASTGEN_SUPER_RefDType(fl)
@@ -1077,6 +1068,7 @@ public:
AstRefDType(FileLine* fl, FlagTypeOfExpr, AstNode* typeofp) AstRefDType(FileLine* fl, FlagTypeOfExpr, AstNode* typeofp)
: ASTGEN_SUPER_RefDType(fl) { : ASTGEN_SUPER_RefDType(fl) {
this->typeofp(typeofp); this->typeofp(typeofp);
if (AstNodeDType* const dtp = VN_CAST(typeofp, NodeDType)) refDTypep(dtp);
} }
ASTGEN_MEMBERS_AstRefDType; ASTGEN_MEMBERS_AstRefDType;
// METHODS // METHODS
@@ -1188,6 +1180,34 @@ public:
int widthTotalBytes() const override { return sizeof(std::map<std::string, std::string>); } int widthTotalBytes() const override { return sizeof(std::map<std::string, std::string>); }
bool isCompound() const override { return true; } 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 { class AstUnsizedArrayDType final : public AstNodeDType {
// Unsized/open-range Array data type, ie "some_dtype var_name []" // Unsized/open-range Array data type, ie "some_dtype var_name []"
// @astgen op1 := childDTypep : Optional[AstNodeDType] // moved to refDTypep() in V3Width // @astgen op1 := childDTypep : Optional[AstNodeDType] // moved to refDTypep() in V3Width
+24 -20
View File
@@ -214,7 +214,7 @@ class AstNodeFTaskRef VL_NOT_FINAL : public AstNodeExpr {
// @astgen op3 := pinsp : List[AstNodeExpr] // @astgen op3 := pinsp : List[AstNodeExpr]
// @astgen op4 := scopeNamep : Optional[AstScopeName] // @astgen op4 := scopeNamep : Optional[AstScopeName]
AstNodeFTask* m_taskp = nullptr; // [AfterLink] Pointer to task referenced 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_name; // Name of variable
string m_dotted; // Dotted part of scope the name()ed task/func is under or "" string m_dotted; // Dotted part of scope the name()ed task/func is under or ""
string m_inlinedDots; // Dotted hierarchy flattened out string m_inlinedDots; // Dotted hierarchy flattened out
@@ -449,21 +449,18 @@ class AstNodeVarRef VL_NOT_FINAL : public AstNodeExpr {
VAccess m_access; // Left hand side assignment VAccess m_access; // Left hand side assignment
AstVar* m_varp; // [AfterLink] Pointer to variable itself AstVar* m_varp; // [AfterLink] Pointer to variable itself
AstVarScope* m_varScopep = nullptr; // Varscope for hierarchy AstVarScope* m_varScopep = nullptr; // Varscope for hierarchy
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy AstNodeModule* m_classOrPackagep = nullptr; // Class/package of the variable
string m_name; // Name of variable
string m_selfPointer; // Output code object pointer (e.g.: 'this') string m_selfPointer; // Output code object pointer (e.g.: 'this')
protected: protected:
AstNodeVarRef(VNType t, FileLine* fl, const string& name, const VAccess& access) AstNodeVarRef(VNType t, FileLine* fl, const VAccess& access)
: AstNodeExpr{t, fl} : AstNodeExpr{t, fl}
, m_access{access} , m_access{access} {
, m_name{name} {
varp(nullptr); 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} : AstNodeExpr{t, fl}
, m_access{access} , m_access{access} {
, m_name{name} {
// May have varp==nullptr // May have varp==nullptr
this->varp(varp); this->varp(varp);
} }
@@ -474,8 +471,6 @@ public:
const char* broken() const override; const char* broken() const override;
int instrCount() const override { return widthInstrs(); } int instrCount() const override { return widthInstrs(); }
void cloneRelink() override; 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; } VAccess access() const { return m_access; }
void access(const VAccess& flag) { m_access = flag; } // Avoid using this; Set in constructor 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 AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable
@@ -721,7 +716,7 @@ class AstClassOrPackageRef final : public AstNodeExpr {
private: private:
string m_name; string m_name;
// Node not NodeModule to appease some early parser usage // Node not NodeModule to appease some early parser usage
AstNode* m_classOrPackageNodep; // Package hierarchy AstNode* m_classOrPackageNodep; // Pointer to class/package referenced
public: public:
AstClassOrPackageRef(FileLine* fl, const string& name, AstNode* classOrPackageNodep, AstClassOrPackageRef(FileLine* fl, const string& name, AstNode* classOrPackageNodep,
AstPin* paramsp) AstPin* paramsp)
@@ -1062,7 +1057,7 @@ public:
}; };
class AstEnumItemRef final : public AstNodeExpr { class AstEnumItemRef final : public AstNodeExpr {
AstEnumItem* m_itemp; // [AfterLink] Pointer to item AstEnumItem* m_itemp; // [AfterLink] Pointer to item
AstNodeModule* m_classOrPackagep; // Package hierarchy AstNodeModule* m_classOrPackagep; // Class/package in which it was defined
public: public:
AstEnumItemRef(FileLine* fl, AstEnumItem* itemp, AstNodeModule* classOrPackagep) AstEnumItemRef(FileLine* fl, AstEnumItem* itemp, AstNodeModule* classOrPackagep)
: ASTGEN_SUPER_EnumItemRef(fl) : ASTGEN_SUPER_EnumItemRef(fl)
@@ -1428,6 +1423,7 @@ class AstMemberSel final : public AstNodeExpr {
// @astgen op1 := fromp : AstNodeExpr // @astgen op1 := fromp : AstNodeExpr
// Don't need the class we are extracting from, as the "fromp()"'s datatype can get us to it // Don't need the class we are extracting from, as the "fromp()"'s datatype can get us to it
string m_name; 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 AstVar* m_varp = nullptr; // Post link, variable within class that is target of selection
public: public:
AstMemberSel(FileLine* fl, AstNodeExpr* fromp, VFlagChildDType, const string& name) AstMemberSel(FileLine* fl, AstNodeExpr* fromp, VFlagChildDType, const string& name)
@@ -1448,10 +1444,12 @@ public:
void dump(std::ostream& str) const override; void dump(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; } string name() const override VL_MT_STABLE { return m_name; }
void name(const string& name) override { m_name = 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 emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; } bool cleanOut() const override { return true; }
bool same(const AstNode* samep) const override { return true; } // dtype comparison does it bool same(const AstNode* samep) const override;
int instrCount() const override { return widthInstrs(); } int instrCount() const override { return widthInstrs(); }
AstVar* varp() const { return m_varp; } AstVar* varp() const { return m_varp; }
void varp(AstVar* nodep) { m_varp = nodep; } void varp(AstVar* nodep) { m_varp = nodep; }
@@ -1911,7 +1909,7 @@ public:
bool same(const AstNode* /*samep*/) const override { return true; } bool same(const AstNode* /*samep*/) const override { return true; }
}; };
class AstStructSel final : public AstNodeExpr { class AstStructSel final : public AstNodeExpr {
// Unpacked struct member access // Unpacked struct/union member access
// Parents: math|stmt // Parents: math|stmt
// Children: varref, math // Children: varref, math
// @astgen op1 := fromp : AstNodeExpr // @astgen op1 := fromp : AstNodeExpr
@@ -1929,7 +1927,10 @@ public:
void name(const string& name) override { m_name = name; } void name(const string& name) override { m_name = name; }
string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() 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 { bool same(const AstNode* samep) const override {
const AstStructSel* const sp = static_cast<const AstStructSel*>(samep); const AstStructSel* const sp = static_cast<const AstStructSel*>(samep);
return m_name == sp->m_name; return m_name == sp->m_name;
@@ -4671,7 +4672,7 @@ public:
ASTGEN_MEMBERS_AstCvtPackString; ASTGEN_MEMBERS_AstCvtPackString;
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAssign(lhs); } void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAssign(lhs); }
string emitVerilog() override { return "%f$_CAST(%l)"; } 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 cleanOut() const override { return true; }
bool cleanLhs() const override { return true; } bool cleanLhs() const override { return true; }
bool sizeMattersLhs() const override { return false; } bool sizeMattersLhs() const override { return false; }
@@ -5349,15 +5350,15 @@ public:
class AstVarRef final : public AstNodeVarRef { class AstVarRef final : public AstNodeVarRef {
// A reference to a variable (lvalue or rvalue) // A reference to a variable (lvalue or rvalue)
public: 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 // This form only allowed post-link because output/wire compression may
// lead to deletion of AstVar's // lead to deletion of AstVar's
inline AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access); inline AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access);
// This form only allowed post-link (see above) // This form only allowed post-link (see above)
inline AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access); inline AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access);
ASTGEN_MEMBERS_AstVarRef; ASTGEN_MEMBERS_AstVarRef;
inline string name() const override; // * = Var name
void dump(std::ostream& str) const override; void dump(std::ostream& str) const override;
const char* broken() const override;
bool same(const AstNode* samep) const override; bool same(const AstNode* samep) const override;
inline bool same(const AstVarRef* samep) const; inline bool same(const AstVarRef* samep) const;
inline bool sameNoLvalue(AstVarRef* samep) const; inline bool sameNoLvalue(AstVarRef* samep) const;
@@ -5369,14 +5370,17 @@ public:
class AstVarXRef final : public AstNodeVarRef { class AstVarXRef final : public AstNodeVarRef {
// A VarRef to something in another module before AstScope. // 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 // 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_dotted; // Dotted part of scope the name()'ed reference is under or ""
string m_inlinedDots; // Dotted hierarchy flattened out string m_inlinedDots; // Dotted hierarchy flattened out
public: public:
AstVarXRef(FileLine* fl, const string& name, const string& dotted, const VAccess& access) 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} {} , m_dotted{dotted} {}
inline AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access); inline AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access);
ASTGEN_MEMBERS_AstVarXRef; ASTGEN_MEMBERS_AstVarXRef;
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
void dump(std::ostream& str) const override; void dump(std::ostream& str) const override;
string dotted() const { return m_dotted; } string dotted() const { return m_dotted; }
void dotted(const string& dotted) { m_dotted = dotted; } void dotted(const string& dotted) { m_dotted = dotted; }
+10 -20
View File
@@ -86,7 +86,7 @@ private:
bool m_recursive : 1; // Recursive or part of recursion bool m_recursive : 1; // Recursive or part of recursion
bool m_underGenerate : 1; // Under generate (for warning) bool m_underGenerate : 1; // Under generate (for warning)
bool m_virtual : 1; // Virtual method in class bool m_virtual : 1; // Virtual method in class
bool m_fromStd : 1; // Part of std bool m_needProcess : 1; // Implements part of a process that allocates std::process
VLifetime m_lifetime; // Lifetime VLifetime m_lifetime; // Lifetime
protected: protected:
AstNodeFTask(VNType t, FileLine* fl, const string& name, AstNode* stmtsp) AstNodeFTask(VNType t, FileLine* fl, const string& name, AstNode* stmtsp)
@@ -112,7 +112,7 @@ protected:
, m_recursive{false} , m_recursive{false}
, m_underGenerate{false} , m_underGenerate{false}
, m_virtual{false} , m_virtual{false}
, m_fromStd{false} { , m_needProcess{false} {
addStmtsp(stmtsp); addStmtsp(stmtsp);
cname(name); // Might be overridden by dpi import/export cname(name); // Might be overridden by dpi import/export
} }
@@ -172,8 +172,8 @@ public:
bool underGenerate() const { return m_underGenerate; } bool underGenerate() const { return m_underGenerate; }
void isVirtual(bool flag) { m_virtual = flag; } void isVirtual(bool flag) { m_virtual = flag; }
bool isVirtual() const { return m_virtual; } bool isVirtual() const { return m_virtual; }
void isFromStd(bool flag) { m_fromStd = flag; } void setNeedProcess() { m_needProcess = true; }
bool isFromStd() const { return m_fromStd; } bool needProcess() const { return m_needProcess; }
void lifetime(const VLifetime& flag) { m_lifetime = flag; } void lifetime(const VLifetime& flag) { m_lifetime = flag; }
VLifetime lifetime() const { return m_lifetime; } VLifetime lifetime() const { return m_lifetime; }
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
@@ -1513,6 +1513,7 @@ class AstTypeTable final : public AstNode {
AstEmptyQueueDType* m_emptyQueuep = nullptr; AstEmptyQueueDType* m_emptyQueuep = nullptr;
AstQueueDType* m_queueIndexp = nullptr; AstQueueDType* m_queueIndexp = nullptr;
AstVoidDType* m_voidp = nullptr; AstVoidDType* m_voidp = nullptr;
AstStreamDType* m_streamp = nullptr;
AstBasicDType* m_basicps[VBasicDTypeKwd::_ENUM_MAX]{}; AstBasicDType* m_basicps[VBasicDTypeKwd::_ENUM_MAX]{};
// //
using DetailedMap = std::map<VBasicTypeKey, AstBasicDType*>; using DetailedMap = std::map<VBasicTypeKey, AstBasicDType*>;
@@ -1538,6 +1539,7 @@ public:
AstEmptyQueueDType* findEmptyQueueDType(FileLine* fl); AstEmptyQueueDType* findEmptyQueueDType(FileLine* fl);
AstQueueDType* findQueueIndexDType(FileLine* fl); AstQueueDType* findQueueIndexDType(FileLine* fl);
AstVoidDType* findVoidDType(FileLine* fl); AstVoidDType* findVoidDType(FileLine* fl);
AstStreamDType* findStreamDType(FileLine* fl);
void clearCache(); void clearCache();
void repairCache(); void repairCache();
void dump(std::ostream& str = std::cout) const override; void dump(std::ostream& str = std::cout) const override;
@@ -1810,7 +1812,7 @@ public:
string dpiTmpVarType(const string& varName) const; string dpiTmpVarType(const string& varName) const;
// Return Verilator internal type for argument: CData, SData, IData, WData // Return Verilator internal type for argument: CData, SData, IData, WData
string vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc = "", 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 vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc
string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc
string vlPropDecl(const string& propName) const; // Return VerilatorVarProps declaration string vlPropDecl(const string& propName) const; // Return VerilatorVarProps declaration
@@ -2135,17 +2137,13 @@ public:
// === AstNodeModule === // === AstNodeModule ===
class AstClass final : public AstNodeModule { class AstClass final : public AstNodeModule {
// @astgen op4 := extendsp : List[AstClassExtends] // @astgen op4 := extendsp : List[AstClassExtends]
// TYPES
using MemberNameMap = std::map<const std::string, AstNode*>;
// MEMBERS // MEMBERS
MemberNameMap m_members; // Members or method children AstClassPackage* m_classOrPackagep = nullptr; // Package it will be emitted with
AstClassPackage* m_classOrPackagep = nullptr; // Class package this is under
bool m_extended = false; // Is extension or extended by other classes bool m_extended = false; // Is extension or extended by other classes
bool m_interfaceClass = false; // Interface class bool m_interfaceClass = false; // Interface class
bool m_needRNG = false; // Need RNG, uses srandom/randomize bool m_needRNG = false; // Need RNG, uses srandom/randomize
bool m_virtual = false; // Virtual class bool m_virtual = false; // Virtual class
bool m_parameterized = false; // Parameterized class bool m_parameterized = false; // Parameterized class
void insertCache(AstNode* nodep);
public: public:
AstClass(FileLine* fl, const string& name) AstClass(FileLine* fl, const string& name)
@@ -2160,16 +2158,7 @@ public:
AstClassPackage* classOrPackagep() const VL_MT_SAFE { return m_classOrPackagep; } AstClassPackage* classOrPackagep() const VL_MT_SAFE { return m_classOrPackagep; }
void classOrPackagep(AstClassPackage* classpackagep) { m_classOrPackagep = classpackagep; } void classOrPackagep(AstClassPackage* classpackagep) { m_classOrPackagep = classpackagep; }
AstNode* membersp() const { return stmtsp(); } AstNode* membersp() const { return stmtsp(); }
void addMembersp(AstNode* nodep) { void addMembersp(AstNode* nodep) { addStmtsp(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;
}
bool isExtended() const { return m_extended; } bool isExtended() const { return m_extended; }
void isExtended(bool flag) { m_extended = flag; } void isExtended(bool flag) { m_extended = flag; }
bool isInterfaceClass() const { return m_interfaceClass; } bool isInterfaceClass() const { return m_interfaceClass; }
@@ -2185,6 +2174,7 @@ public:
static bool isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp); static bool isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp);
// Return the lowest class extended from, or this class // Return the lowest class extended from, or this class
AstClass* baseMostClassp(); AstClass* baseMostClassp();
static bool isCacheableChild(const AstNode* nodep);
}; };
class AstClassPackage final : public AstNodeModule { class AstClassPackage final : public AstNodeModule {
// The static information portion of a class (treated similarly to a package) // The static information portion of a class (treated similarly to a package)
+30 -47
View File
@@ -103,29 +103,8 @@ int AstNodeSel::bitConst() const {
return (constp ? constp->toSInt() : 0); 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 { const char* AstNodeUOrStructDType::broken() const {
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists()); 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; return nullptr;
} }
@@ -399,7 +378,7 @@ string AstVar::verilogKwd() const {
} }
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc, 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, UASSERT_OBJ(!forReturn, this,
"Internal data is never passed as return, but as first argument"); "Internal data is never passed as return, but as first argument");
string ostatic; string ostatic;
@@ -716,12 +695,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); const CTypeRecursed info = cTypeRecurse(false);
return info.render(name, isRef); 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 // Legacy compound argument currently just passed through and unused
CTypeRecursed info; CTypeRecursed info;
@@ -1071,6 +1050,15 @@ AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) {
return m_voidp; 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) { AstQueueDType* AstTypeTable::findQueueIndexDType(FileLine* fl) {
if (VL_UNLIKELY(!m_queueIndexp)) { if (VL_UNLIKELY(!m_queueIndexp)) {
AstQueueDType* const newp = new AstQueueDType{fl, AstNode::findUInt32DType(), nullptr}; AstQueueDType* const newp = new AstQueueDType{fl, AstNode::findUInt32DType(), nullptr};
@@ -1418,29 +1406,10 @@ const char* AstClassPackage::broken() const {
void AstClassPackage::cloneRelink() { void AstClassPackage::cloneRelink() {
if (m_classp && m_classp->clonep()) m_classp = m_classp->clonep(); if (m_classp && m_classp->clonep()) m_classp = m_classp->clonep();
} }
void AstClass::insertCache(AstNode* nodep) { bool AstClass::isCacheableChild(const AstNode* nodep) {
const bool doit = (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef) return (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|| (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto()) || (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto())
|| VN_IS(nodep, CFunc)); || 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);
}
}
} }
AstClass* AstClass::baseMostClassp() { AstClass* AstClass::baseMostClassp() {
AstClass* basep = this; AstClass* basep = this;
@@ -1667,6 +1636,12 @@ AstNodeUOrStructDType* AstMemberDType::getChildStructp() const {
return VN_CAST(subdtp, NodeUOrStructDType); // Maybe nullptr 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 { void AstMemberSel::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str); this->AstNodeExpr::dump(str);
str << " -> "; str << " -> ";
@@ -2055,6 +2030,10 @@ void AstVoidDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str); this->AstNodeDType::dumpSmall(str);
str << "void"; str << "void";
} }
void AstStreamDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "stream";
}
void AstVarScope::dump(std::ostream& str) const { void AstVarScope::dump(std::ostream& str) const {
this->AstNode::dump(str); this->AstNode::dump(str);
if (isTrace()) str << " [T]"; if (isTrace()) str << " [T]";
@@ -2097,6 +2076,10 @@ void AstVarRef::dump(std::ostream& str) const {
str << "UNLINKED"; str << "UNLINKED";
} }
} }
const char* AstVarRef::broken() const {
BROKEN_RTN(!varp());
return AstNodeVarRef::broken();
}
bool AstVarRef::same(const AstNode* samep) const { bool AstVarRef::same(const AstNode* samep) const {
return same(static_cast<const AstVarRef*>(samep)); return same(static_cast<const AstVarRef*>(samep));
} }
+4 -38
View File
@@ -40,39 +40,6 @@ VL_DEFINE_DEBUG_FUNCTIONS;
//###################################################################### //######################################################################
class RenameStaticVisitor final : public VNVisitor {
private:
// STATE
const std::set<AstVar*>& m_staticFuncVarsr; // Static variables from m_ftaskp
AstNodeFTask* const m_ftaskp; // Current function/task
// VISITORS
void visit(AstVarRef* nodep) override {
const auto it = m_staticFuncVarsr.find(nodep->varp());
if (it != m_staticFuncVarsr.end()) nodep->name((*it)->name());
iterateChildren(nodep);
}
void visit(AstInitialStatic* nodep) override {
iterateChildren(nodep);
nodep->unlinkFrBack();
m_ftaskp->addHereThisAsNext(nodep);
}
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
RenameStaticVisitor(std::set<AstVar*>& staticFuncVars, AstNodeFTask* ftaskp, AstNode* nodep)
: m_staticFuncVarsr(staticFuncVars)
, m_ftaskp(ftaskp) {
iterateChildren(nodep);
}
~RenameStaticVisitor() override = default;
};
//######################################################################
class BeginState final { class BeginState final {
private: private:
// NODE STATE // NODE STATE
@@ -105,7 +72,6 @@ private:
string m_unnamedScope; // Name of begin blocks, including unnamed blocks string m_unnamedScope; // Name of begin blocks, including unnamed blocks
int m_ifDepth = 0; // Current if depth int m_ifDepth = 0; // Current if depth
bool m_keepBegins = false; // True if begins should not be inlined bool m_keepBegins = false; // True if begins should not be inlined
std::set<AstVar*> m_staticFuncVars; // Static variables from m_ftaskp
// METHODS // METHODS
@@ -212,7 +178,10 @@ private:
m_ftaskp = nodep; m_ftaskp = nodep;
m_liftedp = nullptr; m_liftedp = nullptr;
iterateChildren(nodep); iterateChildren(nodep);
RenameStaticVisitor{m_staticFuncVars, m_ftaskp, nodep}; nodep->foreach([&](AstInitialStatic* const initp) {
initp->unlinkFrBack();
m_ftaskp->addHereThisAsNext(initp);
});
if (m_liftedp) { if (m_liftedp) {
// Place lifted nodes at beginning of stmtsp, so Var nodes appear before referenced // Place lifted nodes at beginning of stmtsp, so Var nodes appear before referenced
if (AstNode* const stmtsp = nodep->stmtsp()) { if (AstNode* const stmtsp = nodep->stmtsp()) {
@@ -222,7 +191,6 @@ private:
nodep->addStmtsp(m_liftedp); nodep->addStmtsp(m_liftedp);
m_liftedp = nullptr; m_liftedp = nullptr;
} }
m_staticFuncVars.clear();
m_ftaskp = nullptr; m_ftaskp = nullptr;
} }
} }
@@ -266,7 +234,6 @@ private:
nodep->name(newName); nodep->name(newName);
nodep->unlinkFrBack(); nodep->unlinkFrBack();
m_ftaskp->addHereThisAsNext(nodep); m_ftaskp->addHereThisAsNext(nodep);
m_staticFuncVars.insert(nodep);
nodep->funcLocal(false); nodep->funcLocal(false);
} else if (m_unnamedScope != "") { } else if (m_unnamedScope != "") {
// Rename it // Rename it
@@ -377,7 +344,6 @@ private:
void visit(AstVarRef* nodep) override { void visit(AstVarRef* nodep) override {
if (nodep->varp()->user1()) { // It was converted if (nodep->varp()->user1()) { // It was converted
UINFO(9, " relinVarRef " << nodep << endl); UINFO(9, " relinVarRef " << nodep << endl);
nodep->name(nodep->varp()->name());
} }
iterateChildren(nodep); iterateChildren(nodep);
} }
+1 -1
View File
@@ -67,6 +67,7 @@ private:
nodep->editCountInc(); nodep->editCountInc();
nodep->classOrPackagep(packagep); nodep->classOrPackagep(packagep);
packagep->classp(nodep); packagep->classp(nodep);
packagep->timeunit(nodep->timeunit());
v3Global.rootp()->addModulesp(packagep); v3Global.rootp()->addModulesp(packagep);
// Add package to hierarchy // Add package to hierarchy
AstCell* const cellp = new AstCell{packagep->fileline(), AstCell* const cellp = new AstCell{packagep->fileline(),
@@ -105,7 +106,6 @@ private:
m_prefix = nodep->name() + "__02e"; // . m_prefix = nodep->name() + "__02e"; // .
iterateChildren(nodep); iterateChildren(nodep);
} }
nodep->repairCache();
} }
void visit(AstNodeModule* nodep) override { void visit(AstNodeModule* nodep) override {
// Visit for NodeModules that are not AstClass (AstClass is-a AstNodeModule) // Visit for NodeModules that are not AstClass (AstClass is-a AstNodeModule)
+1 -5
View File
@@ -95,6 +95,7 @@ private:
|| VN_IS(nodep->dtypep()->skipRefp(), DynArrayDType) || VN_IS(nodep->dtypep()->skipRefp(), DynArrayDType)
|| VN_IS(nodep->dtypep()->skipRefp(), ClassRefDType) || VN_IS(nodep->dtypep()->skipRefp(), ClassRefDType)
|| VN_IS(nodep->dtypep()->skipRefp(), QueueDType) || VN_IS(nodep->dtypep()->skipRefp(), QueueDType)
|| VN_IS(nodep->dtypep()->skipRefp(), StreamDType)
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType) || VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)
|| VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) { || VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) {
} else { } else {
@@ -234,11 +235,6 @@ private:
operandTriop(nodep); operandTriop(nodep);
setClean(nodep, nodep->cleanOut()); setClean(nodep, nodep->cleanOut());
} }
void visit(AstStructSel* nodep) override {
iterateChildren(nodep);
AstStructDType* dtypep = VN_CAST(nodep->dtypep()->skipRefp(), StructDType);
setClean(nodep, dtypep && !dtypep->packed());
}
void visit(AstUCFunc* nodep) override { void visit(AstUCFunc* nodep) override {
iterateChildren(nodep); iterateChildren(nodep);
computeCppWidth(nodep); computeCppWidth(nodep);
+42 -11
View File
@@ -2166,7 +2166,12 @@ private:
AstNodeExpr* const srcp = nodep->rhsp()->unlinkFrBack(); AstNodeExpr* const srcp = nodep->rhsp()->unlinkFrBack();
// Connect the rhs to the stream operator and update its width // Connect the rhs to the stream operator and update its width
VN_AS(streamp, StreamL)->lhsp(srcp); VN_AS(streamp, StreamL)->lhsp(srcp);
streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED); 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);
}
// Shrink the RHS if necessary // Shrink the RHS if necessary
if (sWidth > dWidth) { if (sWidth > dWidth) {
streamp = new AstSel{streamp->fileline(), streamp, sWidth - dWidth, dWidth}; streamp = new AstSel{streamp->fileline(), streamp, sWidth - dWidth, dWidth};
@@ -3650,6 +3655,7 @@ private:
public: public:
// Processing Mode Enum // Processing Mode Enum
enum ProcMode : uint8_t { enum ProcMode : uint8_t {
PROC_PARAMS_NOWARN,
PROC_PARAMS, PROC_PARAMS,
PROC_GENERATE, PROC_GENERATE,
PROC_LIVE, PROC_LIVE,
@@ -3665,16 +3671,18 @@ public:
, m_concswapNames{globalPass ? ("__Vconcswap_" + cvtToStr(s_globalPassNum++)) : ""} { , m_concswapNames{globalPass ? ("__Vconcswap_" + cvtToStr(s_globalPassNum++)) : ""} {
// clang-format off // clang-format off
switch (pmode) { switch (pmode) {
case PROC_PARAMS: m_doV = true; m_doNConst = true; m_params = true; case PROC_PARAMS_NOWARN: m_doV = true; m_doNConst = true; m_params = true;
m_required = true; break; m_required = false; break;
case PROC_GENERATE: m_doV = true; m_doNConst = true; m_params = true; case PROC_PARAMS: m_doV = true; m_doNConst = true; m_params = true;
m_required = true; m_doGenerate = true; break; m_required = true; break;
case PROC_LIVE: break; case PROC_GENERATE: m_doV = true; m_doNConst = true; m_params = true;
case PROC_V_WARN: m_doV = true; m_doNConst = true; m_warn = true; break; m_required = true; m_doGenerate = true; break;
case PROC_V_NOWARN: m_doV = true; m_doNConst = true; break; case PROC_LIVE: break;
case PROC_V_EXPENSIVE: m_doV = true; m_doNConst = true; m_doExpensive = true; break; case PROC_V_WARN: m_doV = true; m_doNConst = true; m_warn = true; break;
case PROC_CPP: m_doV = false; m_doNConst = true; m_doCpp = true; break; case PROC_V_NOWARN: m_doV = true; m_doNConst = true; break;
default: v3fatalSrc("Bad case"); 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 // clang-format on
} }
@@ -3722,6 +3730,29 @@ AstNode* V3Const::constifyParamsEdit(AstNode* nodep) {
return 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. //! 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 //! 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 //! trigger warnings when we deal with the width. It is possible that these
+4
View File
@@ -30,6 +30,10 @@ public:
static AstNodeExpr* constifyParamsEdit(AstNodeExpr* exprp) { static AstNodeExpr* constifyParamsEdit(AstNodeExpr* exprp) {
return VN_AS(constifyParamsEdit(static_cast<AstNode*>(exprp)), NodeExpr); 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); static AstNode* constifyGenerateParamsEdit(AstNode* nodep);
// Only do constant pushing, without removing dead logic // Only do constant pushing, without removing dead logic
static void constifyAllLive(AstNetlist* nodep); static void constifyAllLive(AstNetlist* nodep);
+2 -2
View File
@@ -123,9 +123,9 @@ struct V3DfgPeepholeContext final {
const std::string m_label; // Label to apply to stats const std::string m_label; // Label to apply to stats
// Enable flags for each optimization // 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) // 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); explicit V3DfgPeepholeContext(const std::string& label);
~V3DfgPeepholeContext(); ~V3DfgPeepholeContext();
+1 -1
View File
@@ -62,7 +62,7 @@ public:
static string symClassAssign() { static string symClassAssign() {
return symClassName() + "* const __restrict vlSymsp VL_ATTR_UNUSED = vlSelf->vlSymsp;\n"; 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()); return v3Global.opt.modPrefix() + "_" + VIdProtect::protect(nodep->name());
} }
static bool isAnonOk(const AstVar* varp) { static bool isAnonOk(const AstVar* varp) {
+5 -1
View File
@@ -419,7 +419,7 @@ void EmitCFunc::emitCCallArgs(const AstNodeCCall* nodep, const string& selfPoint
} }
if (nodep->funcp()->needProcess()) { if (nodep->funcp()->needProcess()) {
if (comma) puts(", "); if (comma) puts(", ");
if (VN_IS(nodep->backp(), CAwait)) { if (VN_IS(nodep->backp(), CAwait) || !nodep->funcp()->isCoroutine()) {
puts("vlProcess"); puts("vlProcess");
} else { } else {
puts("std::make_shared<VlProcess>()"); puts("std::make_shared<VlProcess>()");
@@ -452,6 +452,10 @@ void EmitCFunc::emitCvtPackStr(AstNode* nodep) {
putbs("std::string{"); putbs("std::string{");
putsQuoted(constp->num().toString()); putsQuoted(constp->num().toString());
puts("}"); puts("}");
} else if (VN_IS(nodep->dtypep(), StreamDType)) {
putbs("VL_CVT_PACK_STR_ND(");
iterateAndNextConstNull(nodep);
puts(")");
} else { } else {
putbs("VL_CVT_PACK_STR_N"); putbs("VL_CVT_PACK_STR_N");
emitIQW(nodep); emitIQW(nodep);
+75 -22
View File
@@ -22,6 +22,7 @@
#include "V3EmitCConstInit.h" #include "V3EmitCConstInit.h"
#include "V3Global.h" #include "V3Global.h"
#include "V3MemberMap.h"
#include <algorithm> #include <algorithm>
#include <map> #include <map>
@@ -115,6 +116,7 @@ public:
class EmitCFunc VL_NOT_FINAL : public EmitCConstInit { class EmitCFunc VL_NOT_FINAL : public EmitCConstInit {
private: private:
VMemberMap m_memberMap;
AstVarRef* m_wideTempRefp = nullptr; // Variable that _WW macros should be setting AstVarRef* m_wideTempRefp = nullptr; // Variable that _WW macros should be setting
int m_labelNum = 0; // Next label number int m_labelNum = 0; // Next label number
int m_splitSize = 0; // # of cfunc nodes placed into output file int m_splitSize = 0; // # of cfunc nodes placed into output file
@@ -148,6 +150,22 @@ protected:
bool m_useSelfForThis = false; // Replace "this" with "vlSelf" bool m_useSelfForThis = false; // Replace "this" with "vlSelf"
const AstNodeModule* m_modp = nullptr; // Current module being emitted const AstNodeModule* m_modp = nullptr; // Current module being emitted
const AstCFunc* m_cfuncp = nullptr; // Current function 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: public:
// METHODS // METHODS
@@ -212,13 +230,34 @@ public:
comma = true; 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 // VISITORS
using EmitCConstInit::visit; using EmitCConstInit::visit;
void visit(AstCFunc* nodep) override { void visit(AstCFunc* nodep) override {
VL_RESTORER(m_useSelfForThis); VL_RESTORER(m_useSelfForThis);
VL_RESTORER(m_cfuncp); VL_RESTORER(m_cfuncp);
VL_RESTORER(m_instantiatesOwnProcess)
m_cfuncp = nodep; m_cfuncp = nodep;
m_instantiatesOwnProcess = false;
splitSizeInc(nodep); splitSizeInc(nodep);
@@ -231,20 +270,16 @@ public:
if (!nodep->baseCtors().empty()) { if (!nodep->baseCtors().empty()) {
puts(": "); puts(": ");
puts(nodep->baseCtors()); puts(nodep->baseCtors());
puts("(vlSymsp"); const AstClass* const classp = VN_CAST(nodep->scopep()->modp(), Class);
// Find call to super.new to get the arguments // Find call to super.new to get the arguments
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { if (classp && constructorNeedsProcess(classp)) {
AstNode* exprp; puts("(vlProcess, vlSymsp");
if (VN_IS(stmtp, StmtExpr)) { } else {
exprp = VN_CAST(stmtp, StmtExpr)->exprp(); puts("(vlSymsp");
} else {
exprp = stmtp;
}
if (AstCNew* const newRefp = VN_CAST(exprp, CNew)) {
putCommaIterateNext(newRefp->argsp(), true);
break;
}
} }
const AstCNew* const superNewCallp = getSuperNewCallRecursep(nodep->stmtsp());
UASSERT_OBJ(superNewCallp, nodep, "super.new call not found");
putCommaIterateNext(superNewCallp->argsp(), true);
puts(")"); puts(")");
} }
puts(" {\n"); puts(" {\n");
@@ -265,6 +300,23 @@ public:
puts(nodep->isLoose() ? "__" : "::"); puts(nodep->isLoose() ? "__" : "::");
puts(nodep->nameProtect() + "\\n\"); );\n"); 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()) { for (AstNode* subnodep = nodep->argsp(); subnodep; subnodep = subnodep->nextp()) {
if (AstVar* const varp = VN_CAST(subnodep, Var)) { if (AstVar* const varp = VN_CAST(subnodep, Var)) {
if (varp->isFuncReturn()) emitVarDecl(varp); if (varp->isFuncReturn()) emitVarDecl(varp);
@@ -383,19 +435,17 @@ public:
void visit(AstAssocSel* nodep) override { void visit(AstAssocSel* nodep) override {
iterateAndNextConstNull(nodep->fromp()); iterateAndNextConstNull(nodep->fromp());
putbs(".at("); 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"); UASSERT_OBJ(adtypep, nodep, "Associative select on non-associative type");
if (adtypep->keyDTypep()->isWide()) { iterateAndNextConstNull(nodep->bitp());
emitCvtWideArray(nodep->bitp(), nodep->fromp());
} else {
iterateAndNextConstNull(nodep->bitp());
}
puts(")"); puts(")");
} }
void visit(AstWildcardSel* nodep) override { void visit(AstWildcardSel* nodep) override {
iterateAndNextConstNull(nodep->fromp()); iterateAndNextConstNull(nodep->fromp());
putbs(".at("); 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"); UASSERT_OBJ(adtypep, nodep, "Wildcard select on non-wildcard-associative type");
iterateAndNextConstNull(nodep->bitp()); iterateAndNextConstNull(nodep->bitp());
puts(")"); puts(")");
@@ -443,8 +493,9 @@ public:
// super.new case // super.new case
return; return;
} }
// assignment case // assignment case;
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", vlSymsp"); puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", "
+ optionalProcArg(nodep->dtypep()) + "vlSymsp");
putCommaIterateNext(nodep->argsp(), true); putCommaIterateNext(nodep->argsp(), true);
puts(")"); puts(")");
} }
@@ -1026,6 +1077,7 @@ public:
UASSERT_OBJ(!emitSimpleOk(nodep), nodep, "Triop cannot be described in a simple way"); UASSERT_OBJ(!emitSimpleOk(nodep), nodep, "Triop cannot be described in a simple way");
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nodep->thsp()); emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nodep->thsp());
} }
void visit(AstCvtPackString* nodep) override { emitCvtPackStr(nodep->lhsp()); }
void visit(AstRedXor* nodep) override { void visit(AstRedXor* nodep) override {
if (nodep->lhsp()->isWide()) { if (nodep->lhsp()->isWide()) {
visit(static_cast<AstNodeUniop*>(nodep)); visit(static_cast<AstNodeUniop*>(nodep));
@@ -1088,7 +1140,8 @@ public:
puts(")"); puts(")");
} }
void visit(AstNewCopy* nodep) override { 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 puts("*"); // i.e. make into a reference
iterateAndNextConstNull(nodep->rhsp()); iterateAndNextConstNull(nodep->rhsp());
puts(")"); puts(")");
+6 -7
View File
@@ -147,7 +147,7 @@ class EmitCGatherDependencies final : VNVisitorConst {
} }
public: public:
static const std::set<std::string> gather(AstCFunc* cfuncp) VL_MT_STABLE { static const std::set<std::string> gather(AstCFunc* cfuncp) {
const EmitCGatherDependencies visitor{cfuncp}; const EmitCGatherDependencies visitor{cfuncp};
return std::move(visitor.m_dependencies); return std::move(visitor.m_dependencies);
} }
@@ -566,8 +566,7 @@ class EmitCImp final : EmitCFunc {
~EmitCImp() override = default; ~EmitCImp() override = default;
public: public:
static void main(const AstNodeModule* modp, bool slow, static void main(const AstNodeModule* modp, bool slow, std::deque<AstCFile*>& cfilesr) {
std::deque<AstCFile*>& cfilesr) VL_MT_STABLE {
EmitCImp{modp, slow, cfilesr}; EmitCImp{modp, slow, cfilesr};
} }
}; };
@@ -932,11 +931,11 @@ void V3EmitC::emitcImp() {
const AstNodeModule* const modp = VN_AS(nodep, NodeModule); const AstNodeModule* const modp = VN_AS(nodep, NodeModule);
cfiles.emplace_back(); cfiles.emplace_back();
auto& slowCfilesr = cfiles.back(); auto& slowCfilesr = cfiles.back();
futures.push_back(V3ThreadPool::s().enqueue<void>( futures.push_back(V3ThreadPool::s().enqueue(
[modp, &slowCfilesr]() { EmitCImp::main(modp, /* slow: */ true, slowCfilesr); })); [modp, &slowCfilesr]() { EmitCImp::main(modp, /* slow: */ true, slowCfilesr); }));
cfiles.emplace_back(); cfiles.emplace_back();
auto& fastCfilesr = cfiles.back(); auto& fastCfilesr = cfiles.back();
futures.push_back(V3ThreadPool::s().enqueue<void>( futures.push_back(V3ThreadPool::s().enqueue(
[modp, &fastCfilesr]() { EmitCImp::main(modp, /* slow: */ false, fastCfilesr); })); [modp, &fastCfilesr]() { EmitCImp::main(modp, /* slow: */ false, fastCfilesr); }));
} }
@@ -944,12 +943,12 @@ void V3EmitC::emitcImp() {
if (v3Global.opt.trace() && !v3Global.opt.lintOnly()) { if (v3Global.opt.trace() && !v3Global.opt.lintOnly()) {
cfiles.emplace_back(); cfiles.emplace_back();
auto& slowCfilesr = cfiles.back(); auto& slowCfilesr = cfiles.back();
futures.push_back(V3ThreadPool::s().enqueue<void>([&slowCfilesr]() { futures.push_back(V3ThreadPool::s().enqueue([&slowCfilesr]() {
EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ true, slowCfilesr); EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ true, slowCfilesr);
})); }));
cfiles.emplace_back(); cfiles.emplace_back();
auto& fastCfilesr = cfiles.back(); auto& fastCfilesr = cfiles.back();
futures.push_back(V3ThreadPool::s().enqueue<void>([&fastCfilesr]() { futures.push_back(V3ThreadPool::s().enqueue([&fastCfilesr]() {
EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ false, fastCfilesr); EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ false, fastCfilesr);
})); }));
} }
+12 -14
View File
@@ -222,20 +222,18 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra)
#ifndef V3ERROR_NO_GLOBAL_ #ifndef V3ERROR_NO_GLOBAL_
if (dumpTreeLevel() || debug()) { if (dumpTreeLevel() || debug()) {
V3Broken::allowMidvisitorCheck(true); V3Broken::allowMidvisitorCheck(true);
V3ThreadPool::s().requestExclusiveAccess([&]() VL_REQUIRES(m_mutex) { const V3ThreadPool::ScopedExclusiveAccess exclusiveAccess;
if (dumpTreeLevel()) { if (dumpTreeLevel()) {
v3Global.rootp()->dumpTreeFile( v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("final.tree", 990));
v3Global.debugFilename("final.tree", 990)); }
} if (debug()) {
if (debug()) { execErrorExitCb();
execErrorExitCb(); V3Stats::statsFinalAll(v3Global.rootp());
V3Stats::statsFinalAll(v3Global.rootp()); V3Stats::statsReport();
V3Stats::statsReport(); }
} // Abort in exclusive access to make sure other threads
// Abort in exclusive access to make sure other threads // don't change error code
// don't change error code vlAbortOrExit();
vlAbortOrExit();
});
} }
#endif #endif
} }
+17 -13
View File
@@ -60,6 +60,7 @@ public:
I_TRACING, // Tracing is on/off from /*verilator tracing_on/off*/ I_TRACING, // Tracing is on/off from /*verilator tracing_on/off*/
I_UNUSED, // Unused genvar, parameter or signal message (Backward Compatibility) I_UNUSED, // Unused genvar, parameter or signal message (Backward Compatibility)
// Error codes: // Error codes:
E_LIFETIME, // Error: Reference to a variable might outlive the variable.
E_NEEDTIMINGOPT, // Error: --timing/--no-timing option not specified E_NEEDTIMINGOPT, // Error: --timing/--no-timing option not specified
E_NOTIMING, // Timing control encountered with --no-timing E_NOTIMING, // Timing control encountered with --no-timing
E_PORTSHORT, // Error: Output port is connected to a constant, electrical short E_PORTSHORT, // Error: Output port is connected to a constant, electrical short
@@ -98,6 +99,7 @@ public:
ENUMVALUE, // Error: enum type needs explicit cast ENUMVALUE, // Error: enum type needs explicit cast
EOFNEWLINE, // End-of-file missing newline EOFNEWLINE, // End-of-file missing newline
GENCLK, // Generated Clock. Historical, never issued. GENCLK, // Generated Clock. Historical, never issued.
GENUNNAMED, // Generate unnamed, without label
HIERBLOCK, // Ignored hierarchical block setting HIERBLOCK, // Ignored hierarchical block setting
IFDEPTH, // If statements too deep IFDEPTH, // If statements too deep
IGNOREDRETURN, // Ignoring return value (function as task) IGNOREDRETURN, // Ignoring return value (function as task)
@@ -113,6 +115,7 @@ public:
LATCH, // Latch detected outside of always_latch block LATCH, // Latch detected outside of always_latch block
LITENDIAN, // Little endian, renamed to ASCRANGE LITENDIAN, // Little endian, renamed to ASCRANGE
MINTYPMAXDLY, // Unsupported: min/typ/max delay expressions MINTYPMAXDLY, // Unsupported: min/typ/max delay expressions
MISINDENT, // Misleading indentation
MODDUP, // Duplicate module MODDUP, // Duplicate module
MULTIDRIVEN, // Driven from multiple blocks MULTIDRIVEN, // Driven from multiple blocks
MULTITOP, // Multiple top level modules MULTITOP, // Multiple top level modules
@@ -183,7 +186,7 @@ public:
// Boolean // Boolean
" I_CELLDEFINE", " I_COVERAGE", " I_DEF_NETTYPE_WIRE", " I_LINT", " I_TIMING", " I_TRACING", " I_UNUSED", " I_CELLDEFINE", " I_COVERAGE", " I_DEF_NETTYPE_WIRE", " I_LINT", " I_TIMING", " I_TRACING", " I_UNUSED",
// Errors // Errors
"NEEDTIMINGOPT", "NOTIMING", "PORTSHORT", "TASKNSVAR", "UNSUPPORTED", "LIFETIME", "NEEDTIMINGOPT", "NOTIMING", "PORTSHORT", "TASKNSVAR", "UNSUPPORTED",
// Warnings // Warnings
" EC_FIRST_WARN", " EC_FIRST_WARN",
"ALWCOMBORDER", "ASCRANGE", "ASSIGNDLY", "ASSIGNIN", "BADSTDPRAGMA", "ALWCOMBORDER", "ASCRANGE", "ASSIGNDLY", "ASSIGNIN", "BADSTDPRAGMA",
@@ -191,11 +194,12 @@ public:
"CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA", "CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA",
"CMPCONST", "COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG", "CMPCONST", "COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG",
"DECLFILENAME", "DEFPARAM", "DEPRECATED", "DECLFILENAME", "DEFPARAM", "DEPRECATED",
"ENCAPSULATED", "ENDLABEL", "ENUMVALUE", "EOFNEWLINE", "GENCLK", "HIERBLOCK", "ENCAPSULATED", "ENDLABEL", "ENUMVALUE", "EOFNEWLINE", "GENCLK", "GENUNNAMED",
"HIERBLOCK",
"IFDEPTH", "IGNOREDRETURN", "IFDEPTH", "IGNOREDRETURN",
"IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE", "IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE",
"INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE", "INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE",
"LATCH", "LITENDIAN", "MINTYPMAXDLY", "MODDUP", "LATCH", "LITENDIAN", "MINTYPMAXDLY", "MISINDENT", "MODDUP",
"MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOLATCH", "NULLPORT", "PINCONNECTEMPTY", "MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOLATCH", "NULLPORT", "PINCONNECTEMPTY",
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PROCASSWIRE", "PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PROCASSWIRE",
"PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY", "PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY",
@@ -234,18 +238,18 @@ public:
return (m_e == ALWCOMBORDER || m_e == ASCRANGE || m_e == BSSPACE || m_e == CASEINCOMPLETE return (m_e == ALWCOMBORDER || m_e == ASCRANGE || m_e == BSSPACE || m_e == CASEINCOMPLETE
|| m_e == CASEOVERLAP || m_e == CASEWITHX || m_e == CASEX || m_e == CASTCONST || m_e == CASEOVERLAP || m_e == CASEWITHX || m_e == CASEX || m_e == CASTCONST
|| m_e == CMPCONST || m_e == COLONPLUS || m_e == IMPLICIT || m_e == IMPLICITSTATIC || m_e == CMPCONST || m_e == COLONPLUS || m_e == IMPLICIT || m_e == IMPLICITSTATIC
|| m_e == LATCH || m_e == NEWERSTD || m_e == PINMISSING || m_e == REALCVT || m_e == LATCH || m_e == MISINDENT || m_e == NEWERSTD || m_e == PINMISSING
|| m_e == STATICVAR || m_e == UNSIGNED || m_e == WIDTH || m_e == WIDTHTRUNC || m_e == REALCVT || m_e == STATICVAR || m_e == UNSIGNED || m_e == WIDTH
|| m_e == WIDTHEXPAND || m_e == WIDTHXZEXPAND); || m_e == WIDTHTRUNC || m_e == WIDTHEXPAND || m_e == WIDTHXZEXPAND);
} }
// Warnings that are style only // Warnings that are style only
bool styleError() const VL_MT_SAFE { bool styleError() const VL_MT_SAFE {
return (m_e == ASSIGNDLY // More than style, but for backward compatibility return (m_e == ASSIGNDLY // More than style, but for backward compatibility
|| m_e == BLKSEQ || m_e == DEFPARAM || m_e == DECLFILENAME || m_e == EOFNEWLINE || m_e == BLKSEQ || m_e == DEFPARAM || m_e == DECLFILENAME || m_e == EOFNEWLINE
|| m_e == IMPORTSTAR || m_e == INCABSPATH || m_e == PINCONNECTEMPTY || m_e == GENUNNAMED || m_e == IMPORTSTAR || m_e == INCABSPATH
|| m_e == PINNOCONNECT || m_e == SYNCASYNCNET || m_e == UNDRIVEN || m_e == PINCONNECTEMPTY || m_e == PINNOCONNECT || m_e == SYNCASYNCNET
|| m_e == UNUSEDGENVAR || m_e == UNUSEDPARAM || m_e == UNUSEDSIGNAL || m_e == UNDRIVEN || m_e == UNUSEDGENVAR || m_e == UNUSEDPARAM
|| m_e == VARHIDDEN); || m_e == UNUSEDSIGNAL || m_e == VARHIDDEN);
} }
// Warnings that are unused only // Warnings that are unused only
bool unusedError() const VL_MT_SAFE { bool unusedError() const VL_MT_SAFE {
@@ -335,7 +339,7 @@ public:
v3errorEnd( v3errorEnd(
(v3errorPrep(V3ErrorCode::EC_FATALEXIT), (v3errorPrep(V3ErrorCode::EC_FATALEXIT),
(v3errorStr() << "Exiting due to too many errors encountered; --error-limit=" (v3errorStr() << "Exiting due to too many errors encountered; --error-limit="
<< errorCount() << endl), << errorCount() << std::endl),
v3errorStr())); v3errorStr()));
assert(0); // LCOV_EXCL_LINE assert(0); // LCOV_EXCL_LINE
VL_UNREACHABLE; VL_UNREACHABLE;
@@ -588,12 +592,12 @@ inline void v3errorEndFatal(std::ostringstream& sstr)
#define UINFO(level, stmsg) \ #define UINFO(level, stmsg) \
do { \ do { \
if (VL_UNCOVERABLE(debug() >= (level))) { \ if (VL_UNCOVERABLE(debug() >= (level))) { \
cout << "- " << V3Error::lineStr(__FILE__, __LINE__) << stmsg; \ std::cout << "- " << V3Error::lineStr(__FILE__, __LINE__) << stmsg; \
} \ } \
} while (false) } while (false)
#define UINFONL(level, stmsg) \ #define UINFONL(level, stmsg) \
do { \ do { \
if (VL_UNCOVERABLE(debug() >= (level))) { cout << stmsg; } \ if (VL_UNCOVERABLE(debug() >= (level))) { std::cout << stmsg; } \
} while (false) } while (false)
#ifdef VL_DEBUG #ifdef VL_DEBUG
+2 -2
View File
@@ -489,8 +489,8 @@ private:
void startFilter(const string& command) { void startFilter(const string& command) {
if (command == "") {} // Prevent Unused if (command == "") {} // Prevent Unused
#ifdef INFILTER_PIPE #ifdef INFILTER_PIPE
int fd_stdin[2]; int fd_stdin[2]; // Can't use std::array
int fd_stdout[2]; int fd_stdout[2]; // Can't use std::array
constexpr int P_RD = 0; constexpr int P_RD = 0;
constexpr int P_WR = 1; constexpr int P_WR = 1;
+6
View File
@@ -433,6 +433,12 @@ string FileLine::source() const VL_MT_SAFE {
} // LCOV_EXCL_STOP } // LCOV_EXCL_STOP
return m_contentp->getLine(m_contentLineno); return m_contentp->getLine(m_contentLineno);
} }
string FileLine::sourcePrefix(int toColumn) const VL_MT_SAFE {
const std::string src = source();
if (toColumn > static_cast<int>(src.length())) toColumn = static_cast<int>(src.length());
if (toColumn < 1) return "";
return src.substr(0, toColumn - 1);
}
string FileLine::prettySource() const VL_MT_SAFE { string FileLine::prettySource() const VL_MT_SAFE {
string out = source(); string out = source();
// Drop ignore trailing newline // Drop ignore trailing newline
+1
View File
@@ -241,6 +241,7 @@ public:
// as the parser errors etc generally make more sense pointing at the last parse point // as the parser errors etc generally make more sense pointing at the last parse point
int lineno() const VL_MT_SAFE { return m_lastLineno; } int lineno() const VL_MT_SAFE { return m_lastLineno; }
string source() const VL_MT_SAFE; string source() const VL_MT_SAFE;
string sourcePrefix(int toColumn) const VL_MT_SAFE;
string prettySource() const VL_MT_SAFE; // Source, w/stripped unprintables and newlines string prettySource() const VL_MT_SAFE; // Source, w/stripped unprintables and newlines
FileLine* parent() const VL_MT_SAFE { return m_parent; } FileLine* parent() const VL_MT_SAFE { return m_parent; }
V3LangCode language() const { return singleton().numberToLang(filenameno()); } V3LangCode language() const { return singleton().numberToLang(filenameno()); }
+245
View File
@@ -0,0 +1,245 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Create separate tasks for forked processes that
// can outlive their parents
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-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
//
//*************************************************************************
// V3Fork's Transformations:
//
// Each module:
// Look for FORKs [JOIN_NONE]/[JOIN_ANY]
// FORK(stmts) -> TASK(stmts), FORK(TASKREF(inits))
//
// FORKs that spawn tasks which might outlive their parents require those
// tasks to carry their own frames and as such they require their own
// variable scopes.
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include "V3Fork.h"
#include "V3Ast.h"
#include "V3AstNodeExpr.h"
#include "V3Global.h"
#include <algorithm>
#include <set>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Fork visitor, transforms asynchronous blocks into separate tasks
class ForkVisitor final : public VNVisitor {
private:
// NODE STATE
// AstNode::user1() -> bool, 1 = Node was created as a call to an asynchronous task
// AstVarRef::user2() -> bool, 1 = Node is a class handle reference. The handle gets
// modified in the context of this reference.
const VNUser1InUse m_inuser1;
const VNUser2InUse m_inuser2;
// STATE
AstNodeModule* m_modp = nullptr; // Class/module we are currently under
int m_forkDepth = 0; // Nesting level of asynchronous forks
bool m_newProcess = false; // True if we are directly under an asynchronous fork.
AstVar* m_capturedVarsp = nullptr; // Local copies of captured variables
std::set<AstVar*> m_forkLocalsp; // Variables local to a given fork
AstArg* m_capturedVarRefsp = nullptr; // References to captured variables (as args)
int m_createdTasksCount = 0; // Number of tasks created by this visitor
// METHODS
AstVar* captureRef(AstNodeExpr* refp) {
AstVar* varp = nullptr;
for (varp = m_capturedVarsp; varp; varp = VN_AS(varp->nextp(), Var))
if (varp->name() == refp->name()) break;
if (!varp) {
// Create a local copy for a capture
varp = new AstVar{refp->fileline(), VVarType::BLOCKTEMP, refp->name(), refp->dtypep()};
varp->direction(VDirection::INPUT);
varp->funcLocal(true);
varp->lifetime(VLifetime::AUTOMATIC);
m_capturedVarsp = AstNode::addNext(m_capturedVarsp, varp);
// Use the original ref as an argument for call
AstArg* arg = new AstArg{refp->fileline(), refp->name(), refp->cloneTree(false)};
m_capturedVarRefsp = AstNode::addNext(m_capturedVarRefsp, arg);
}
return varp;
}
AstTask* makeTask(FileLine* fl, AstNode* stmtsp, std::string name) {
stmtsp = AstNode::addNext(static_cast<AstNode*>(m_capturedVarsp), stmtsp);
AstTask* const taskp = new AstTask{fl, name, stmtsp};
++m_createdTasksCount;
return taskp;
}
std::string generateTaskName(AstNode* fromp, std::string kind) {
// TODO: Ensure no collisions occur
return "__V" + kind + (!fromp->name().empty() ? (fromp->name() + "__") : "UNNAMED__")
+ cvtToHex(fromp);
}
void visitTaskifiable(AstNode* nodep) {
if (!m_newProcess || nodep->user1()) {
VL_RESTORER(m_forkDepth);
if (nodep->user1()) {
UASSERT(m_forkDepth > 0, "Wrong fork depth!");
--m_forkDepth;
}
iterateChildren(nodep);
return;
}
VL_RESTORER(m_capturedVarsp);
VL_RESTORER(m_capturedVarRefsp);
VL_RESTORER(m_newProcess);
m_capturedVarsp = nullptr;
m_capturedVarRefsp = nullptr;
m_newProcess = false;
iterateChildren(nodep);
// If there are no captures, there's no need to taskify
if (m_forkLocalsp.empty() && (m_capturedVarsp == nullptr) && !v3Global.opt.fTaskifyAll())
return;
VNRelinker handle;
AstTask* taskp = nullptr;
if (AstBegin* beginp = VN_CAST(nodep, Begin)) {
UASSERT(beginp->stmtsp(), "No stmtsp\n");
const std::string taskName = generateTaskName(beginp, "__FORK_BEGIN_");
taskp
= makeTask(beginp->fileline(), beginp->stmtsp()->unlinkFrBackWithNext(), taskName);
beginp->unlinkFrBack(&handle);
VL_DO_DANGLING(beginp->deleteTree(), beginp);
} else if (AstNodeStmt* stmtp = VN_CAST(nodep, NodeStmt)) {
const std::string taskName = generateTaskName(stmtp, "__FORK_STMT_");
taskp = makeTask(stmtp->fileline(), stmtp->unlinkFrBack(&handle), taskName);
} else if (AstFork* forkp = VN_CAST(nodep, Fork)) {
const std::string taskName = generateTaskName(forkp, "__FORK_NESTED_");
taskp = makeTask(forkp->fileline(), forkp->unlinkFrBack(&handle), taskName);
}
m_modp->addStmtsp(taskp);
AstTaskRef* const taskrefp
= new AstTaskRef{nodep->fileline(), taskp->name(), m_capturedVarRefsp};
AstStmtExpr* const taskcallp = new AstStmtExpr{nodep->fileline(), taskrefp};
// Replaced nodes will be revisited, so we don't need to "lift" the arguments
// as captures in case of nested forks.
handle.relink(taskcallp);
taskcallp->user1(true);
}
// VISITORS
void visit(AstFork* nodep) override {
bool nested = m_newProcess;
VL_RESTORER(m_forkLocalsp);
VL_RESTORER(m_newProcess);
VL_RESTORER(m_forkDepth)
if (!nodep->joinType().join()) {
++m_forkDepth;
m_newProcess = true;
m_forkLocalsp.clear();
// Nested forks get moved into separate tasks
if (nested) {
visitTaskifiable(nodep);
return;
}
} else {
m_newProcess = false;
}
iterateChildren(nodep);
}
void visit(AstBegin* nodep) override { visitTaskifiable(nodep); }
void visit(AstNodeStmt* nodep) override { visitTaskifiable(nodep); }
void visit(AstVar* nodep) override {
if (m_forkDepth) m_forkLocalsp.insert(nodep);
}
void visit(AstVarRef* nodep) override {
// VL_KEEP_THIS ensures that we hold a handle to the class
if (m_forkDepth && !nodep->varp()->isFuncLocal() && nodep->varp()->isClassMember()) return;
if (m_forkDepth && (m_forkLocalsp.count(nodep->varp()) == 0)
&& !nodep->varp()->lifetime().isStatic()) {
if (nodep->access().isWriteOrRW()
&& (!nodep->isClassHandleValue() || nodep->user2())) {
nodep->v3warn(
E_LIFETIME,
"Invalid reference: Process might outlive variable `"
<< nodep->varp()->name() << "`.\n"
<< nodep->varp()->warnMore()
<< "... Suggest use it as read-only to initialize a local copy at the "
"beginning of the process, or declare it as static. It is also "
"possible to refer by reference to objects and their members.");
return;
}
UASSERT_OBJ(
!nodep->varp()->lifetime().isNone(), nodep,
"Variable's lifetime is unknown. Can't determine if a capture is necessary.");
AstVar* const varp = captureRef(nodep);
nodep->varp(varp);
}
}
void visit(AstAssign* nodep) override {
if (VN_IS(nodep->lhsp(), VarRef) && nodep->lhsp()->isClassHandleValue()) {
nodep->lhsp()->user2(true);
}
visit(static_cast<AstNodeStmt*>(nodep));
}
void visit(AstThisRef* nodep) override { return; }
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
m_modp = nodep;
iterateChildren(nodep);
}
void visit(AstNode* nodep) override {
VL_RESTORER(m_newProcess)
VL_RESTORER(m_forkDepth);
if (nodep->user1()) --m_forkDepth;
m_newProcess = false;
iterateChildren(nodep);
}
public:
// CONSTRUCTORS
ForkVisitor(AstNetlist* nodep) { visit(nodep); }
~ForkVisitor() override = default;
// UTILITY
int createdTasksCount() { return m_createdTasksCount; }
};
//######################################################################
// Fork class functions
int V3Fork::makeTasks(AstNetlist* nodep) {
int createdTasksCount;
UINFO(2, __FUNCTION__ << ": " << endl);
{
ForkVisitor fork_visitor(nodep);
createdTasksCount = fork_visitor.createdTasksCount();
}
V3Global::dumpCheckGlobalTree("fork", 0, dumpTreeLevel() >= 3);
return createdTasksCount;
}
+35
View File
@@ -0,0 +1,35 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Create separate tasks for forked processes that
// can outlive their parents
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-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
//
//*************************************************************************
#ifndef VERILATOR_V3FORK_H_
#define VERILATOR_V3FORK_H_
#include "config_build.h"
#include "verilatedos.h"
class AstNetlist;
//============================================================================
class V3Fork final {
public:
// Create tasks out of begin blocks that can outlive processes in which they were forked.
// Return value: number of tasks created
static int makeTasks(AstNetlist* nodep);
};
#endif // Guard
+1 -3
View File
@@ -176,9 +176,7 @@ public:
, m_slow{slow} {} , m_slow{slow} {}
~GateLogicVertex() override = default; ~GateLogicVertex() override = default;
// ACCESSORS // ACCESSORS
string name() const override VL_MT_STABLE { string name() const override { return (cvtToHex(m_nodep) + "@" + scopep()->prettyName()); }
return (cvtToHex(m_nodep) + "@" + scopep()->prettyName());
}
string dotColor() const override { return "purple"; } string dotColor() const override { return "purple"; }
FileLine* fileline() const override { return nodep()->fileline(); } FileLine* fileline() const override { return nodep()->fileline(); }
AstNode* nodep() const { return m_nodep; } AstNode* nodep() const { return m_nodep; }
+2 -2
View File
@@ -19,8 +19,8 @@
// clang-format off // clang-format off
#include "config_build.h" #include "config_build.h"
#ifndef HAVE_CONFIG_BUILD #ifndef HAVE_CONFIG_PACKAGE
# error "Something failed during ./configure as config_build.h is incomplete. Perhaps you used autoreconf, don't." # error "Something failed during ./configure as config_package.h is incomplete. Perhaps you used autoreconf, don't."
#endif #endif
// clang-format on // clang-format on
-1
View File
@@ -384,7 +384,6 @@ private:
nodep->v3fatalSrc("Null connection?"); nodep->v3fatalSrc("Null connection?");
} }
} }
nodep->name(nodep->varp()->name());
} }
void visit(AstVarXRef* nodep) override { void visit(AstVarXRef* nodep) override {
// Track what scope it was originally under so V3LinkDot can resolve it // Track what scope it was originally under so V3LinkDot can resolve it
-6
View File
@@ -147,12 +147,6 @@ private:
iterateAndNextConstNull(nodep->lsbp()); iterateAndNextConstNull(nodep->lsbp());
iterateAndNextConstNull(nodep->widthp()); iterateAndNextConstNull(nodep->widthp());
} }
void visit(AstSliceSel* nodep) override { // LCOV_EXCL_LINE
nodep->v3fatalSrc("AstSliceSel unhandled");
}
void visit(AstMemberSel* nodep) override { // LCOV_EXCL_LINE
nodep->v3fatalSrc("AstMemberSel unhandled");
}
void visit(AstConcat* nodep) override { void visit(AstConcat* nodep) override {
if (m_ignoreRemaining) return; if (m_ignoreRemaining) return;
// Nop. // Nop.
+93 -38
View File
@@ -69,6 +69,7 @@
#include "V3Ast.h" #include "V3Ast.h"
#include "V3Global.h" #include "V3Global.h"
#include "V3Graph.h" #include "V3Graph.h"
#include "V3MemberMap.h"
#include "V3String.h" #include "V3String.h"
#include "V3SymTable.h" #include "V3SymTable.h"
@@ -215,6 +216,7 @@ public:
VSymGraph* symsp() { return &m_syms; } VSymGraph* symsp() { return &m_syms; }
int stepNumber() const { return int(m_step); } int stepNumber() const { return int(m_step); }
bool forPrimary() const { return m_step == LDS_PRIMARY; } bool forPrimary() const { return m_step == LDS_PRIMARY; }
bool forParamed() const { return m_step == LDS_PARAMED; }
bool forPrearray() const { return m_step == LDS_PARAMED || m_step == LDS_PRIMARY; } bool forPrearray() const { return m_step == LDS_PARAMED || m_step == LDS_PRIMARY; }
bool forScopeCreation() const { return m_step == LDS_SCOPED; } bool forScopeCreation() const { return m_step == LDS_SCOPED; }
@@ -751,6 +753,8 @@ class LinkDotFindVisitor final : public VNVisitor {
// METHODS // METHODS
void makeImplicitNew(AstClass* nodep) { void makeImplicitNew(AstClass* nodep) {
AstFunc* const newp = new AstFunc{nodep->fileline(), "new", nullptr, nullptr}; AstFunc* const newp = new AstFunc{nodep->fileline(), "new", nullptr, nullptr};
// If needed, super.new() call is added the 2nd pass of V3LinkDotResolve,
// because base classes are already resolved there.
newp->isConstructor(true); newp->isConstructor(true);
nodep->addMembersp(newp); nodep->addMembersp(newp);
UINFO(8, "Made implicit new for " << nodep->name() << ": " << nodep << endl); UINFO(8, "Made implicit new for " << nodep->name() << ": " << nodep << endl);
@@ -2025,6 +2029,7 @@ private:
// (except the default instances) // (except the default instances)
// They are added to the set only in linkDotPrimary. // They are added to the set only in linkDotPrimary.
bool m_insideClassExtParam = false; // Inside a class from m_extendsParam bool m_insideClassExtParam = false; // Inside a class from m_extendsParam
bool m_explicitSuperNew = false; // Hit a "super.new" call inside a "new" function
struct DotStates { struct DotStates {
DotPosition m_dotPos; // Scope part of dotted resolution DotPosition m_dotPos; // Scope part of dotted resolution
@@ -2060,40 +2065,38 @@ private:
} m_ds; // State to preserve across recursions } m_ds; // State to preserve across recursions
// METHODS - Variables // METHODS - Variables
void createImplicitVar(VSymEnt* /*lookupSymp*/, AstVarRef* nodep, AstNodeModule* modp, AstVar* createImplicitVar(VSymEnt* /*lookupSymp*/, AstParseRef* nodep, AstNodeModule* modp,
VSymEnt* moduleSymp, bool noWarn) { VSymEnt* moduleSymp, bool noWarn) {
// Create implicit after warning // Create implicit after warning
if (!nodep->varp()) { if (!noWarn) {
if (!noWarn) { if (nodep->fileline()->warnIsOff(V3ErrorCode::I_DEF_NETTYPE_WIRE)) {
if (nodep->fileline()->warnIsOff(V3ErrorCode::I_DEF_NETTYPE_WIRE)) { const string suggest = m_statep->suggestSymFallback(moduleSymp, nodep->name(),
const string suggest = m_statep->suggestSymFallback(moduleSymp, nodep->name(), LinkNodeMatcherVar{});
LinkNodeMatcherVar{}); nodep->v3error("Signal definition not found, and implicit disabled with "
nodep->v3error("Signal definition not found, and implicit disabled with " "`default_nettype: "
"`default_nettype: " << nodep->prettyNameQ() << '\n'
<< nodep->prettyNameQ() << '\n' << (suggest.empty() ? "" : nodep->warnMore() + suggest));
<< (suggest.empty() ? "" : nodep->warnMore() + suggest));
}
// Bypass looking for suggestions if IMPLICIT is turned off
// as there could be thousands of these suppressed in large netlists
else if (!nodep->fileline()->warnIsOff(V3ErrorCode::IMPLICIT)) {
const string suggest = m_statep->suggestSymFallback(moduleSymp, nodep->name(),
LinkNodeMatcherVar{});
nodep->v3warn(IMPLICIT,
"Signal definition not found, creating implicitly: "
<< nodep->prettyNameQ() << '\n'
<< (suggest.empty() ? "" : nodep->warnMore() + suggest));
}
} }
AstVar* const newp = new AstVar{nodep->fileline(), VVarType::WIRE, nodep->name(), // Bypass looking for suggestions if IMPLICIT is turned off
VFlagLogicPacked{}, 1}; // as there could be thousands of these suppressed in large netlists
newp->trace(modp->modTrace()); else if (!nodep->fileline()->warnIsOff(V3ErrorCode::IMPLICIT)) {
nodep->varp(newp); const string suggest = m_statep->suggestSymFallback(moduleSymp, nodep->name(),
modp->addStmtsp(newp); LinkNodeMatcherVar{});
// Link it to signal list, must add the variable under the module; nodep->v3warn(IMPLICIT,
// current scope might be lower now "Signal definition not found, creating implicitly: "
m_statep->insertSym(moduleSymp, newp->name(), newp, nullptr /*classOrPackagep*/); << nodep->prettyNameQ() << '\n'
<< (suggest.empty() ? "" : nodep->warnMore() + suggest));
}
} }
AstVar* const newp
= new AstVar{nodep->fileline(), VVarType::WIRE, nodep->name(), VFlagLogicPacked{}, 1};
newp->trace(modp->modTrace());
modp->addStmtsp(newp);
// Link it to signal list, must add the variable under the module;
// current scope might be lower now
m_statep->insertSym(moduleSymp, newp->name(), newp, nullptr /*classOrPackagep*/);
return newp;
} }
AstVar* foundToVarp(const VSymEnt* symp, AstNode* nodep, VAccess access) { AstVar* foundToVarp(const VSymEnt* symp, AstNode* nodep, VAccess access) {
// Return a variable if possible, auto converting a modport to variable // Return a variable if possible, auto converting a modport to variable
@@ -2112,6 +2115,25 @@ private:
return nullptr; return nullptr;
} }
} }
AstNodeStmt* addImplicitSuperNewCall(AstFunc* nodep) {
// Returns the added node
FileLine* const fl = nodep->fileline();
AstDot* const superNewp
= new AstDot{fl, false, new AstParseRef{fl, VParseRefExp::PX_ROOT, "super"},
new AstNew{fl, nullptr}};
AstNodeStmt* const superNewStmtp = superNewp->makeStmt();
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
// super.new shall be the first statement (section 8.15 of IEEE Std 1800-2017)
// but some nodes (such as variable declarations and typedefs) should stay before
if (VN_IS(stmtp, NodeStmt)) {
stmtp->addHereThisAsNext(superNewStmtp);
return superNewStmtp;
}
}
// There were no statements
nodep->addStmtsp(superNewStmtp);
return superNewStmtp;
}
void taskFuncSwapCheck(AstNodeFTaskRef* nodep) { void taskFuncSwapCheck(AstNodeFTaskRef* nodep) {
if (nodep->taskp() && VN_IS(nodep->taskp(), Task) && VN_IS(nodep, FuncRef)) { if (nodep->taskp() && VN_IS(nodep->taskp(), Task) && VN_IS(nodep, FuncRef)) {
nodep->v3error("Illegal call of a task as a function: " << nodep->prettyNameQ()); nodep->v3error("Illegal call of a task as a function: " << nodep->prettyNameQ());
@@ -2120,7 +2142,7 @@ private:
void checkNoDot(AstNode* nodep) { void checkNoDot(AstNode* nodep) {
if (VL_UNLIKELY(m_ds.m_dotPos != DP_NONE)) { if (VL_UNLIKELY(m_ds.m_dotPos != DP_NONE)) {
// UINFO(9, "ds=" << m_ds.ascii() << endl); // UINFO(9, "ds=" << m_ds.ascii() << endl);
nodep->v3error("Syntax Error: Not expecting " << nodep->type() << " under a " nodep->v3error("Syntax error: Not expecting " << nodep->type() << " under a "
<< nodep->backp()->type() << nodep->backp()->type()
<< " in dotted expression"); << " in dotted expression");
m_ds.m_dotErr = true; m_ds.m_dotErr = true;
@@ -2820,11 +2842,11 @@ private:
if (checkImplicit) { if (checkImplicit) {
// Create if implicit, and also if error (so only complain once) // Create if implicit, and also if error (so only complain once)
// Else if a scope is allowed, making a signal won't help error cascade // Else if a scope is allowed, making a signal won't help error cascade
auto varp = createImplicitVar(m_curSymp, nodep, m_modp, m_modSymp, err);
AstVarRef* const newp AstVarRef* const newp
= new AstVarRef{nodep->fileline(), nodep->name(), VAccess::READ}; = new AstVarRef{nodep->fileline(), varp, VAccess::READ};
nodep->replaceWith(newp); nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep); VL_DO_DANGLING(pushDeletep(nodep), nodep);
createImplicitVar(m_curSymp, newp, m_modp, m_modSymp, err);
} }
} }
} }
@@ -3026,7 +3048,8 @@ private:
} }
UASSERT_OBJ(cpackagerefp->classOrPackagep(), m_ds.m_dotp->lhsp(), "Bad package link"); UASSERT_OBJ(cpackagerefp->classOrPackagep(), m_ds.m_dotp->lhsp(), "Bad package link");
nodep->classOrPackagep(cpackagerefp->classOrPackagep()); nodep->classOrPackagep(cpackagerefp->classOrPackagep());
m_ds.m_dotPos = DP_SCOPE; // Class/package :: HERE function() . method_called_on_function_return_value()
m_ds.m_dotPos = DP_MEMBER;
m_ds.m_dotp = nullptr; m_ds.m_dotp = nullptr;
} else if (m_ds.m_dotp && m_ds.m_dotPos == DP_FINAL) { } else if (m_ds.m_dotp && m_ds.m_dotPos == DP_FINAL) {
nodep->dotted(m_ds.m_dotText); // Maybe "" nodep->dotted(m_ds.m_dotText); // Maybe ""
@@ -3129,7 +3152,15 @@ private:
} else if (nodep->name() == "randomize" || nodep->name() == "srandom" } else if (nodep->name() == "randomize" || nodep->name() == "srandom"
|| nodep->name() == "get_randstate" || nodep->name() == "get_randstate"
|| nodep->name() == "set_randstate") { || nodep->name() == "set_randstate") {
// Resolved in V3Width if (AstClass* const classp = VN_CAST(m_modp, Class)) {
nodep->classOrPackagep(classp);
} else {
nodep->v3error("Calling implicit class method "
<< nodep->prettyNameQ() << " without being under class");
nodep->replaceWith(new AstConst{nodep->fileline(), 0});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
} else if (nodep->dotted() == "") { } else if (nodep->dotted() == "") {
if (nodep->pli()) { if (nodep->pli()) {
if (v3Global.opt.bboxSys()) { if (v3Global.opt.bboxSys()) {
@@ -3215,7 +3246,7 @@ private:
if (nodep->user3SetOnce()) return; if (nodep->user3SetOnce()) return;
if (m_ds.m_dotPos if (m_ds.m_dotPos
== DP_SCOPE) { // Already under dot, so this is {modulepart} DOT {modulepart} == DP_SCOPE) { // Already under dot, so this is {modulepart} DOT {modulepart}
nodep->v3error("Syntax Error: Range ':', '+:' etc are not allowed in the instance " nodep->v3error("Syntax error: Range ':', '+:' etc are not allowed in the instance "
"part of a dotted reference"); "part of a dotted reference");
m_ds.m_dotErr = true; m_ds.m_dotErr = true;
return; return;
@@ -3273,7 +3304,16 @@ private:
{ {
m_ftaskp = nodep; m_ftaskp = nodep;
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep); m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
const bool isNew = nodep->name() == "new";
if (isNew) m_explicitSuperNew = false;
iterateChildren(nodep); iterateChildren(nodep);
if (isNew && !m_explicitSuperNew && m_statep->forParamed()) {
const AstClassExtends* const classExtendsp = VN_AS(m_modp, Class)->extendsp();
if (classExtendsp && classExtendsp->classOrNullp()) {
AstNodeStmt* const superNewp = addImplicitSuperNewCall(VN_AS(nodep, Func));
iterate(superNewp);
}
}
} }
m_ds.m_dotSymp = m_curSymp = oldCurSymp; m_ds.m_dotSymp = m_curSymp = oldCurSymp;
m_ftaskp = nullptr; m_ftaskp = nullptr;
@@ -3389,6 +3429,7 @@ private:
checkNoDot(nodep); checkNoDot(nodep);
VL_RESTORER(m_curSymp); VL_RESTORER(m_curSymp);
VL_RESTORER(m_modSymp); VL_RESTORER(m_modSymp);
VL_RESTORER(m_modp);
VL_RESTORER(m_ifClassImpNames); VL_RESTORER(m_ifClassImpNames);
VL_RESTORER(m_insideClassExtParam); VL_RESTORER(m_insideClassExtParam);
{ {
@@ -3457,15 +3498,16 @@ private:
// V3Width when determines types needs to find enum values and such // V3Width when determines types needs to find enum values and such
// so add members pointing to appropriate enum values // so add members pointing to appropriate enum values
{ {
nodep->repairCache(); VMemberMap memberMap;
for (VSymEnt::const_iterator it = m_curSymp->begin(); it != m_curSymp->end(); ++it) { for (VSymEnt::const_iterator it = m_curSymp->begin(); it != m_curSymp->end(); ++it) {
AstNode* const itemp = it->second->nodep(); AstNode* const itemp = it->second->nodep();
if (!nodep->findMember(it->first)) { if (!memberMap.findMember(nodep, it->first)) {
if (AstEnumItem* const aitemp = VN_CAST(itemp, EnumItem)) { if (AstEnumItem* const aitemp = VN_CAST(itemp, EnumItem)) {
AstEnumItemRef* const newp = new AstEnumItemRef{ AstEnumItemRef* const newp = new AstEnumItemRef{
aitemp->fileline(), aitemp, it->second->classOrPackagep()}; aitemp->fileline(), aitemp, it->second->classOrPackagep()};
UINFO(8, "Class import noderef '" << it->first << "' " << newp << endl); UINFO(8, "Class import noderef '" << it->first << "' " << newp << endl);
nodep->addMembersp(newp); nodep->addMembersp(newp);
memberMap.insert(nodep, newp);
} }
} }
} }
@@ -3595,6 +3637,19 @@ private:
UINFO(5, " AstCellArrayRef: " << nodep << " " << m_ds.ascii() << endl); UINFO(5, " AstCellArrayRef: " << nodep << " " << m_ds.ascii() << endl);
// No need to iterate, if we have a UnlinkedVarXRef, we're already done // No need to iterate, if we have a UnlinkedVarXRef, we're already done
} }
void visit(AstStmtExpr* nodep) override {
checkNoDot(nodep);
// Check if nodep represents a super.new call;
if (VN_IS(nodep->exprp(), New)) {
// in this case it was already linked, so it doesn't have a super reference
m_explicitSuperNew = true;
} else if (const AstDot* const dotp = VN_CAST(nodep->exprp(), Dot)) {
if (dotp->lhsp()->name() == "super" && VN_IS(dotp->rhsp(), New)) {
m_explicitSuperNew = true;
}
}
iterateChildren(nodep);
}
void visit(AstNode* nodep) override { void visit(AstNode* nodep) override {
checkNoDot(nodep); checkNoDot(nodep);
+10 -5
View File
@@ -41,7 +41,6 @@ private:
bool m_setContinuously = false; // Set that var has some continuous assignment bool m_setContinuously = false; // Set that var has some continuous assignment
bool m_setStrengthSpecified = false; // Set that var has assignment with strength specified. bool m_setStrengthSpecified = false; // Set that var has assignment with strength specified.
VAccess m_setRefLvalue; // Set VarRefs to lvalues for pin assignments VAccess m_setRefLvalue; // Set VarRefs to lvalues for pin assignments
AstNodeFTask* m_ftaskp = nullptr; // Function or task we're inside
// VISITs // VISITs
// Result handing // Result handing
@@ -55,7 +54,8 @@ private:
// so it is needed to check only if m_setContinuously is true // so it is needed to check only if m_setContinuously is true
if (m_setStrengthSpecified) nodep->varp()->hasStrengthAssignment(true); if (m_setStrengthSpecified) nodep->varp()->hasStrengthAssignment(true);
} }
if (nodep->access().isWriteOrRW() && !m_ftaskp && nodep->varp()->isReadOnly()) { if (nodep->access().isWriteOrRW() && !nodep->varp()->isFuncLocal()
&& nodep->varp()->isReadOnly()) {
nodep->v3warn(ASSIGNIN, nodep->v3warn(ASSIGNIN,
"Assigning to input/const variable: " << nodep->prettyNameQ()); "Assigning to input/const variable: " << nodep->prettyNameQ());
} }
@@ -275,9 +275,14 @@ private:
iterateAndNextNull(nodep->thsp()); iterateAndNextNull(nodep->thsp());
} }
} }
void visit(AstNodeFTask* nodep) override { void visit(AstMemberSel* nodep) override {
VL_RESTORER(m_ftaskp); if (m_setRefLvalue != VAccess::NOCHANGE) {
m_ftaskp = nodep; nodep->access(m_setRefLvalue);
} else {
// It is the only place where the access is set to member select nodes.
// If it doesn't have to be set to WRITE, it means that it is READ.
nodep->access(VAccess::READ);
}
iterateChildren(nodep); iterateChildren(nodep);
} }
void visit(AstNodeFTaskRef* nodep) override { void visit(AstNodeFTaskRef* nodep) override {
+90 -8
View File
@@ -76,6 +76,8 @@ private:
// without suppressing other token's messages as a side effect. // without suppressing other token's messages as a side effect.
// We could have verilog.l create a new one on every token, // We could have verilog.l create a new one on every token,
// but that's a lot more structures than only doing AST nodes. // but that's a lot more structures than only doing AST nodes.
// TODO: Many places copy the filename when suppressing warnings,
// perhaps audit to make consistent and this is no longer needed
if (m_filelines.find(nodep->fileline()) != m_filelines.end()) { if (m_filelines.find(nodep->fileline()) != m_filelines.end()) {
nodep->fileline(new FileLine{nodep->fileline()}); nodep->fileline(new FileLine{nodep->fileline()});
} }
@@ -125,6 +127,58 @@ private:
&& !nodep->stmtsp()->nextp()); // Has only one item && !nodep->stmtsp()->nextp()); // Has only one item
} }
void checkIndent(AstNode* nodep, AstNode* childp) {
// Try very hard to avoid false positives
AstNode* nextp = nodep->nextp();
if (!childp) return;
if (!nextp && VN_IS(nodep, While) && VN_IS(nodep->backp(), Begin))
nextp = nodep->backp()->nextp();
if (!nextp) return;
if (VN_IS(childp, Begin)) return;
FileLine* const nodeFlp = nodep->fileline();
FileLine* const childFlp = childp->fileline();
FileLine* const nextFlp = nextp->fileline();
// UINFO(0, "checkInd " << nodeFlp->firstColumn() << " " << nodep << endl);
// UINFO(0, " child " << childFlp->firstColumn() << " " << childp << endl);
// UINFO(0, " next " << nextFlp->firstColumn() << " " << nextp << endl);
// Same filename, later line numbers (no macro magic going on)
if (nodeFlp->filenameno() != childFlp->filenameno()) return;
if (nodeFlp->filenameno() != nextFlp->filenameno()) return;
if (nodeFlp->lastLineno() >= childFlp->firstLineno()) return;
if (childFlp->lastLineno() >= nextFlp->firstLineno()) return;
// This block has indent 'a'
// Child block has indent 'b' where indent('b') > indent('a')
// Next block has indent 'b'
// (note similar code below)
if (!(nodeFlp->firstColumn() < childFlp->firstColumn()
&& nextFlp->firstColumn() >= childFlp->firstColumn()))
return;
// Might be a tab difference in spaces up to the node prefix, if so
// just ignore this warning
// Note it's correct we look at nodep's column in all of these
const std::string nodePrefix = nodeFlp->sourcePrefix(nodeFlp->firstColumn());
const std::string childPrefix = childFlp->sourcePrefix(nodeFlp->firstColumn());
const std::string nextPrefix = nextFlp->sourcePrefix(nodeFlp->firstColumn());
if (childPrefix != nodePrefix) return;
if (nextPrefix != childPrefix) return;
// Some file lines start after the indentation, so make another check
// using actual file contents
const std::string nodeSource = nodeFlp->source();
const std::string childSource = childFlp->source();
const std::string nextSource = nextFlp->source();
if (!(VString::leadingWhitespaceCount(nodeSource)
< VString::leadingWhitespaceCount(childSource)
&& VString::leadingWhitespaceCount(nextSource)
>= VString::leadingWhitespaceCount(childSource)))
return;
nextp->v3warn(MISINDENT,
"Misleading indentation\n"
<< nextp->warnContextPrimary() << '\n'
<< nodep->warnOther()
<< "... Expected indentation matching this earlier statement's line:\n"
<< nodep->warnContextSecondary());
}
// VISITs // VISITs
void visit(AstNodeFTask* nodep) override { void visit(AstNodeFTask* nodep) override {
if (!nodep->user1SetOnce()) { // Process only once. if (!nodep->user1SetOnce()) { // Process only once.
@@ -316,9 +370,11 @@ private:
// Making an AstAssign (vs AstAssignW) to a wire is an error, suppress it // Making an AstAssign (vs AstAssignW) to a wire is an error, suppress it
FileLine* const newfl = new FileLine{fl}; FileLine* const newfl = new FileLine{fl};
newfl->warnOff(V3ErrorCode::PROCASSWIRE, true); newfl->warnOff(V3ErrorCode::PROCASSWIRE, true);
auto* const assp // Create a ParseRef to the wire. We cannot use the var as it may be deleted if
= new AstAssign{newfl, new AstVarRef{newfl, nodep->name(), VAccess::WRITE}, // it's a port (see t_var_set_link.v)
VN_AS(nodep->valuep()->unlinkFrBack(), NodeExpr)}; auto* const assp = new AstAssign{
newfl, new AstParseRef{newfl, VParseRefExp::PX_TEXT, nodep->name()},
VN_AS(nodep->valuep()->unlinkFrBack(), NodeExpr)};
if (nodep->lifetime().isAutomatic()) { if (nodep->lifetime().isAutomatic()) {
nodep->addNextHere(new AstInitialAutomatic{newfl, assp}); nodep->addNextHere(new AstInitialAutomatic{newfl, assp});
} else { } else {
@@ -327,7 +383,7 @@ private:
} // 4. Under blocks, it's an initial value to be under an assign } // 4. Under blocks, it's an initial value to be under an assign
else { else {
nodep->addNextHere( nodep->addNextHere(
new AstAssign{fl, new AstVarRef{fl, nodep->name(), VAccess::WRITE}, new AstAssign{fl, new AstVarRef{fl, nodep, VAccess::WRITE},
VN_AS(nodep->valuep()->unlinkFrBack(), NodeExpr)}); VN_AS(nodep->valuep()->unlinkFrBack(), NodeExpr)});
} }
} }
@@ -476,6 +532,7 @@ private:
void visit(AstForeach* nodep) override { void visit(AstForeach* nodep) override {
// FOREACH(array, loopvars, body) // FOREACH(array, loopvars, body)
UINFO(9, "FOREACH " << nodep << endl); UINFO(9, "FOREACH " << nodep << endl);
cleanFileline(nodep);
// Separate iteration vars from base from variable // Separate iteration vars from base from variable
// Input: // Input:
// v--- arrayp // v--- arrayp
@@ -510,13 +567,16 @@ private:
iterateChildren(nodep); iterateChildren(nodep);
} }
void visit(AstRepeat* nodep) override { void visit(AstRepeat* nodep) override {
cleanFileline(nodep);
VL_RESTORER(m_insideLoop); VL_RESTORER(m_insideLoop);
{ {
m_insideLoop = true; m_insideLoop = true;
checkIndent(nodep, nodep->stmtsp());
iterateChildren(nodep); iterateChildren(nodep);
} }
} }
void visit(AstDoWhile* nodep) override { void visit(AstDoWhile* nodep) override {
cleanFileline(nodep);
VL_RESTORER(m_insideLoop); VL_RESTORER(m_insideLoop);
{ {
m_insideLoop = true; m_insideLoop = true;
@@ -524,9 +584,11 @@ private:
} }
} }
void visit(AstWhile* nodep) override { void visit(AstWhile* nodep) override {
cleanFileline(nodep);
VL_RESTORER(m_insideLoop); VL_RESTORER(m_insideLoop);
{ {
m_insideLoop = true; m_insideLoop = true;
checkIndent(nodep, nodep->stmtsp());
iterateChildren(nodep); iterateChildren(nodep);
} }
} }
@@ -580,14 +642,25 @@ private:
// The genblk name will get attached to the if true/false LOWER begin block(s) // The genblk name will get attached to the if true/false LOWER begin block(s)
const bool nestedIf = nestedIfBegin(nodep); const bool nestedIf = nestedIfBegin(nodep);
// It's not FOR(BEGIN(...)) but we earlier changed it to BEGIN(FOR(...)) // It's not FOR(BEGIN(...)) but we earlier changed it to BEGIN(FOR(...))
int assignGenBlkNum = -1;
if (nodep->genforp()) { if (nodep->genforp()) {
++m_genblkNum; ++m_genblkNum;
if (nodep->name() == "") nodep->name("genblk" + cvtToStr(m_genblkNum)); if (nodep->name() == "") assignGenBlkNum = m_genblkNum;
} else if (nodep->generate() && nodep->name() == "" && assignGenBlkNum == -1
&& (VN_IS(backp, CaseItem) || VN_IS(backp, GenIf)) && !nestedIf) {
assignGenBlkNum = m_genblkAbove;
} }
if (nodep->generate() && nodep->name() == "" if (assignGenBlkNum != -1) {
&& (VN_IS(backp, CaseItem) || VN_IS(backp, GenIf)) && !nestedIf) { nodep->name("genblk" + cvtToStr(assignGenBlkNum));
nodep->name("genblk" + cvtToStr(m_genblkAbove)); if (nodep->stmtsp()) {
nodep->v3warn(GENUNNAMED,
"Unnamed generate block "
<< nodep->prettyNameQ() << " (IEEE 1800-2017 27.6)"
<< nodep->warnMore()
<< "... Suggest assign a label with 'begin : gen_<label_name>'");
}
} }
if (nodep->name() != "") { if (nodep->name() != "") {
VL_RESTORER(m_genblkAbove); VL_RESTORER(m_genblkAbove);
VL_RESTORER(m_genblkNum); VL_RESTORER(m_genblkNum);
@@ -611,6 +684,7 @@ private:
} }
void visit(AstGenIf* nodep) override { void visit(AstGenIf* nodep) override {
cleanFileline(nodep); cleanFileline(nodep);
checkIndent(nodep, nodep->elsesp() ? nodep->elsesp() : nodep->thensp());
const bool nestedIf const bool nestedIf
= (VN_IS(nodep->backp(), Begin) && nestedIfBegin(VN_CAST(nodep->backp(), Begin))); = (VN_IS(nodep->backp(), Begin) && nestedIfBegin(VN_CAST(nodep->backp(), Begin)));
if (nestedIf) { if (nestedIf) {
@@ -655,6 +729,11 @@ private:
} }
} }
} }
void visit(AstIf* nodep) override {
cleanFileline(nodep);
checkIndent(nodep, nodep->elsesp() ? nodep->elsesp() : nodep->thensp());
iterateChildren(nodep);
}
void visit(AstPrintTimeScale* nodep) override { void visit(AstPrintTimeScale* nodep) override {
// Inlining may change hierarchy, so just save timescale where needed // Inlining may change hierarchy, so just save timescale where needed
cleanFileline(nodep); cleanFileline(nodep);
@@ -683,6 +762,7 @@ private:
nodep->timeunit(m_modp->timeunit()); nodep->timeunit(m_modp->timeunit());
} }
void visit(AstTimeUnit* nodep) override { void visit(AstTimeUnit* nodep) override {
cleanFileline(nodep);
iterateChildren(nodep); iterateChildren(nodep);
nodep->timeunit(m_modp->timeunit()); nodep->timeunit(m_modp->timeunit());
} }
@@ -711,6 +791,7 @@ private:
} }
} }
void visit(AstClocking* nodep) override { void visit(AstClocking* nodep) override {
cleanFileline(nodep);
VL_RESTORER(m_defaultInSkewp); VL_RESTORER(m_defaultInSkewp);
VL_RESTORER(m_defaultOutSkewp); VL_RESTORER(m_defaultOutSkewp);
// Find default input and output skews // Find default input and output skews
@@ -745,6 +826,7 @@ private:
iterateChildren(nodep); iterateChildren(nodep);
} }
void visit(AstClockingItem* nodep) override { void visit(AstClockingItem* nodep) override {
cleanFileline(nodep);
if (nodep->direction() == VDirection::OUTPUT) { if (nodep->direction() == VDirection::OUTPUT) {
if (!nodep->skewp()) { if (!nodep->skewp()) {
if (m_defaultOutSkewp) { if (m_defaultOutSkewp) {
+95
View File
@@ -0,0 +1,95 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Member names for classes/structs
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-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
//
//*************************************************************************
//
// For a class or struct, create a map of names of each member.
// Used for faster lookup to avoid O(n^2) processing times.
//
//*************************************************************************
#ifndef VERILATOR_V3MEMBERMAP_H_
#define VERILATOR_V3MEMBERMAP_H_
#include "config_build.h"
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3Global.h"
#include <map>
//######################################################################
class VMemberMap final {
// MEMBERS
using MemberMap = std::map<std::string, AstNode*>;
using NodeMap = std::map<const AstNode*, MemberMap>;
NodeMap m_map; // Map of nodes being tracked
public:
void clear() { m_map.clear(); }
// Find 'name' under 'nodep', caching nodep's children if needed
AstNode* findMember(const AstNode* nodep, const std::string& name) {
auto nit = m_map.find(nodep);
if (VL_UNLIKELY(nit == m_map.end())) {
scan(nodep);
nit = m_map.find(nodep);
}
const auto mit = nit->second.find(name);
if (mit == nit->second.end()) return nullptr;
return mit->second;
}
// Insert under the given parent node the child's name
void insert(const AstNode* nodep, AstNode* childp) {
auto nit = m_map.find(nodep);
if (nit == m_map.end()) {
scan(nodep);
nit = m_map.find(nodep);
}
memberInsert(nit->second, childp, false);
}
private:
void scan(const AstNode* nodep) {
const auto nitPair = m_map.emplace(nodep, MemberMap{});
MemberMap& mmapr = nitPair.first->second;
if (const AstClass* const anodep = VN_CAST(nodep, Class)) {
for (AstNode* itemp = anodep->membersp(); itemp; itemp = itemp->nextp()) {
if (const AstScope* const scopep = VN_CAST(itemp, Scope)) {
for (AstNode* blockp = scopep->blocksp(); blockp; blockp = blockp->nextp()) {
if (AstClass::isCacheableChild(blockp)) memberInsert(mmapr, blockp);
}
} else {
if (AstClass::isCacheableChild(itemp)) memberInsert(mmapr, itemp);
}
}
} else if (const AstNodeUOrStructDType* const anodep
= VN_CAST(nodep, NodeUOrStructDType)) {
for (AstNode* itemp = anodep->membersp(); itemp; itemp = itemp->nextp()) {
memberInsert(mmapr, itemp);
}
} else {
nodep->v3fatalSrc("Unsupported node type");
}
}
void memberInsert(MemberMap& mmapr, AstNode* childp, bool warn = true) {
const auto mitPair = mmapr.emplace(childp->name(), childp);
if (VL_UNCOVERABLE(!mitPair.second && warn)) {
// Probably an internal error, but we'll make it user friendly if happens
childp->v3error("Duplicate declaration of member name: " << childp->prettyNameQ());
}
}
};
#endif // guard
+4
View File
@@ -109,6 +109,10 @@ public:
bool try_lock() VL_TRY_ACQUIRE(true) VL_MT_SAFE { bool try_lock() VL_TRY_ACQUIRE(true) VL_MT_SAFE {
return V3MutexConfig::s().enable() ? m_mutex.try_lock() : true; return V3MutexConfig::s().enable() ? m_mutex.try_lock() : true;
} }
/// Assume that the mutex is already held. Purely for Clang thread safety analyzer.
void assumeLocked() VL_ASSERT_CAPABILITY(this) VL_MT_SAFE {}
/// Pretend that the mutex is being unlocked. Purely for Clang thread safety analyzer.
void pretendUnlock() VL_RELEASE() VL_MT_SAFE {}
/// Acquire/lock mutex and check for stop request /// 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 tries to lock the mutex and if it fails, it check if stop request was send.
/// It returns after locking mutex. /// It returns after locking mutex.
+1 -6
View File
@@ -89,12 +89,7 @@ private:
rename(nodep, false); rename(nodep, false);
} }
} }
void visit(AstVarRef* nodep) override { void visit(AstVarRef* nodep) override { iterate(nodep->varp()); }
if (nodep->varp()) {
iterate(nodep->varp());
nodep->name(nodep->varp()->name());
}
}
void visit(AstCell* nodep) override { void visit(AstCell* nodep) override {
if (!nodep->user1()) { if (!nodep->user1()) {
rename(nodep, (!nodep->modp()->modPublic() && !VN_IS(nodep->modp(), ClassPackage))); rename(nodep, (!nodep->modp()->modPublic() && !VN_IS(nodep->modp(), ClassPackage)));
+8 -5
View File
@@ -1866,12 +1866,15 @@ V3Number& V3Number::opAdd(const V3Number& lhs, const V3Number& rhs) {
if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX(); if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX();
setZero(); setZero();
// Addem // Addem
int carry = 0; uint64_t carry = 0;
for (int bit = 0; bit < width(); bit++) { for (int word = 0; word < words(); word++) {
const int sum = ((lhs.bitIs1(bit) ? 1 : 0) + (rhs.bitIs1(bit) ? 1 : 0) + carry); const uint64_t lwordval = lhs.m_data.num()[word].m_value;
if (sum & 1) setBit(bit, 1); const uint64_t rwordval = rhs.m_data.num()[word].m_value;
carry = (sum >= 2); const uint64_t sum = lwordval + rwordval + carry;
m_data.num()[word].m_value = sum & 0xffffffffULL;
carry = sum > 0xffffffffULL;
} }
opCleanThis(); // Just in case it produced extra bits in result
return *this; return *this;
} }
V3Number& V3Number::opSub(const V3Number& lhs, const V3Number& rhs) { V3Number& V3Number::opSub(const V3Number& lhs, const V3Number& rhs) {
+1 -1
View File
@@ -43,7 +43,7 @@ class VOptionBool;
// if (int consumed = parser.parse(i, argc, argv)) { // if (int consumed = parser.parse(i, argc, argv)) {
// i += consumed; // i += consumed;
// } else { // error // } else { // error
// cerr << parser.getSuggestion(argv[i]) << endl; // std::cerr << parser.getSuggestion(argv[i]) << std::endl;
// } // }
// } // }
// //
+2 -1
View File
@@ -896,7 +896,7 @@ void V3Options::notify() {
// V3 Options accessors // V3 Options accessors
string V3Options::version() VL_PURE { string V3Options::version() VL_PURE {
string ver = DTVERSION; string ver = PACKAGE_STRING;
ver += " rev " + cvtToStr(DTVERSION_rev); ver += " rev " + cvtToStr(DTVERSION_rev);
return ver; return ver;
} }
@@ -1224,6 +1224,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
DECL_OPTION("-fsubst", FOnOff, &m_fSubst); DECL_OPTION("-fsubst", FOnOff, &m_fSubst);
DECL_OPTION("-fsubst-const", FOnOff, &m_fSubstConst); DECL_OPTION("-fsubst-const", FOnOff, &m_fSubstConst);
DECL_OPTION("-ftable", FOnOff, &m_fTable); DECL_OPTION("-ftable", FOnOff, &m_fTable);
DECL_OPTION("-ftaskify-all-forked", FOnOff, &m_fTaskifyAll).undocumented(); // Debug
DECL_OPTION("-G", CbPartialMatch, [this](const char* optp) { addParameter(optp, false); }); DECL_OPTION("-G", CbPartialMatch, [this](const char* optp) { addParameter(optp, false); });
DECL_OPTION("-gate-stmts", Set, &m_gateStmts); DECL_OPTION("-gate-stmts", Set, &m_gateStmts);
+2
View File
@@ -374,6 +374,7 @@ private:
bool m_fSubst; // main switch: -fno-subst: substitute expression temp values bool m_fSubst; // main switch: -fno-subst: substitute expression temp values
bool m_fSubstConst; // main switch: -fno-subst-const: final constant substitution bool m_fSubstConst; // main switch: -fno-subst-const: final constant substitution
bool m_fTable; // main switch: -fno-table: lookup table creation bool m_fTable; // main switch: -fno-table: lookup table creation
bool m_fTaskifyAll = false; // main switch: --ftaskify-all-forked
// clang-format on // clang-format on
bool m_available = false; // Set to true at the end of option parsing bool m_available = false; // Set to true at the end of option parsing
@@ -633,6 +634,7 @@ public:
bool fSubst() const { return m_fSubst; } bool fSubst() const { return m_fSubst; }
bool fSubstConst() const { return m_fSubstConst; } bool fSubstConst() const { return m_fSubstConst; }
bool fTable() const { return m_fTable; } bool fTable() const { return m_fTable; }
bool fTaskifyAll() const { return m_fTaskifyAll; }
string traceClassBase() const { return m_traceFormat.classBase(); } string traceClassBase() const { return m_traceFormat.classBase(); }
string traceClassLang() const { return m_traceFormat.classBase() + (systemC() ? "Sc" : "C"); } string traceClassLang() const { return m_traceFormat.classBase() + (systemC() ? "Sc" : "C"); }
+28 -2
View File
@@ -394,6 +394,11 @@ class ParamProcessor final : public VNDeleter {
} }
return nullptr; return nullptr;
} }
bool isString(AstNodeDType* nodep) {
if (AstBasicDType* const basicp = VN_CAST(nodep->skipRefToEnump(), BasicDType))
return basicp->isString();
return false;
}
void collectPins(CloneMap* clonemapp, AstNodeModule* modp, bool originalIsCopy) { void collectPins(CloneMap* clonemapp, AstNodeModule* modp, bool originalIsCopy) {
// Grab all I/O so we can remap our pins later // Grab all I/O so we can remap our pins later
for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) { for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
@@ -670,6 +675,19 @@ class ParamProcessor final : public VNDeleter {
return modInfop; return modInfop;
} }
void convertToStringp(AstNode* nodep) {
// Should be called on values of parameters of type string to convert them
// to properly typed string constants.
// Has no effect if the value is not a string constant.
AstConst* const constp = VN_CAST(nodep, Const);
// Check if it wasn't already converted
if (constp && !constp->num().isString()) {
constp->replaceWith(
new AstConst{constp->fileline(), AstConst::String{}, constp->num().toString()});
constp->deleteTree();
}
}
void cellPinCleanup(AstNode* nodep, AstPin* pinp, AstNodeModule* srcModp, string& longnamer, void cellPinCleanup(AstNode* nodep, AstPin* pinp, AstNodeModule* srcModp, string& longnamer,
bool& any_overridesr) { bool& any_overridesr) {
if (!pinp->exprp()) return; // No-connect if (!pinp->exprp()) return; // No-connect
@@ -684,8 +702,16 @@ class ParamProcessor final : public VNDeleter {
any_overridesr = true; any_overridesr = true;
} else { } else {
V3Const::constifyParamsEdit(pinp->exprp()); V3Const::constifyParamsEdit(pinp->exprp());
// String constants are parsed as logic arrays and converted to strings in V3Const.
// At this moment, some constants may have been already converted.
// To correctly compare constants, both should be of the same type,
// so they need to be converted.
if (isString(modvarp->subDTypep())) {
convertToStringp(pinp->exprp());
convertToStringp(modvarp->valuep());
}
AstConst* const exprp = VN_CAST(pinp->exprp(), Const); AstConst* const exprp = VN_CAST(pinp->exprp(), Const);
const AstConst* const origp = VN_CAST(modvarp->valuep(), Const); AstConst* const origp = VN_CAST(modvarp->valuep(), Const);
if (!exprp) { if (!exprp) {
if (debug()) pinp->dumpTree("- "); if (debug()) pinp->dumpTree("- ");
pinp->v3error("Can't convert defparam value to constant: Param " pinp->v3error("Can't convert defparam value to constant: Param "
@@ -709,7 +735,7 @@ class ParamProcessor final : public VNDeleter {
} }
} else if (AstParamTypeDType* const modvarp = pinp->modPTypep()) { } else if (AstParamTypeDType* const modvarp = pinp->modPTypep()) {
AstNodeDType* const exprp = VN_CAST(pinp->exprp(), NodeDType); AstNodeDType* const exprp = VN_CAST(pinp->exprp(), NodeDType);
const AstNodeDType* const origp = modvarp->subDTypep(); const AstNodeDType* const origp = modvarp->skipRefToEnump();
if (!exprp) { if (!exprp) {
pinp->v3error("Parameter type pin value isn't a type: Param " pinp->v3error("Parameter type pin value isn't a type: Param "
<< pinp->prettyNameQ() << " of " << nodep->prettyNameQ()); << pinp->prettyNameQ() << " of " << nodep->prettyNameQ());
+4 -3
View File
@@ -85,9 +85,10 @@ AstArg* V3ParseGrammar::argWrapList(AstNodeExpr* nodep) {
} }
AstNode* V3ParseGrammar::createSupplyExpr(FileLine* fileline, const string& name, int value) { AstNode* V3ParseGrammar::createSupplyExpr(FileLine* fileline, const string& name, int value) {
AstAssignW* assignp = new AstAssignW{fileline, new AstVarRef{fileline, name, VAccess::WRITE}, AstAssignW* assignp
value ? new AstConst{fileline, AstConst::All1{}} = new AstAssignW{fileline, new AstParseRef{fileline, VParseRefExp::PX_TEXT, name},
: new AstConst{fileline, AstConst::All0{}}}; value ? new AstConst{fileline, AstConst::All1{}}
: new AstConst{fileline, AstConst::All0{}}};
AstStrengthSpec* strengthSpecp AstStrengthSpec* strengthSpecp
= new AstStrengthSpec{fileline, VStrength::SUPPLY, VStrength::SUPPLY}; = new AstStrengthSpec{fileline, VStrength::SUPPLY, VStrength::SUPPLY};
assignp->strengthSpecp(strengthSpecp); assignp->strengthSpecp(strengthSpecp);
+1 -1
View File
@@ -119,7 +119,7 @@ public:
if (VL_UNCOVERABLE(symCurrentp()->nodep() != nodep)) { // LCOV_EXCL_START if (VL_UNCOVERABLE(symCurrentp()->nodep() != nodep)) { // LCOV_EXCL_START
if (debug()) { if (debug()) {
showUpward(); showUpward();
dumpSelf(cout, "-mism: "); dumpSelf(std::cout, "-mism: ");
} }
nodep->v3fatalSrc("Symbols suggest ending " << symCurrentp()->nodep()->prettyTypeName() nodep->v3fatalSrc("Symbols suggest ending " << symCurrentp()->nodep()->prettyTypeName()
<< " but parser thinks ending " << " but parser thinks ending "
+2 -2
View File
@@ -518,7 +518,7 @@ class MTaskEdge final : public V3GraphEdge, public MergeCandidate {
// MEMBERS // MEMBERS
// This edge can be in 2 EdgeHeaps, one forward and one reverse. We allocate the heap nodes // This edge can be in 2 EdgeHeaps, one forward and one reverse. We allocate the heap nodes
// directly within the edge as they are always required and this makes association cheap. // directly within the edge as they are always required and this makes association cheap.
EdgeHeap::Node m_edgeHeapNode[GraphWay::NUM_WAYS]; std::array<EdgeHeap::Node, GraphWay::NUM_WAYS> m_edgeHeapNode;
public: public:
// CONSTRUCTORS // CONSTRUCTORS
@@ -1060,7 +1060,7 @@ class PartPropagateCpSelfTest final {
private: private:
// MEMBERS // MEMBERS
V3Graph m_graph; // A graph V3Graph m_graph; // A graph
LogicMTask* m_vx[50]; // All vertices within the graph std::array<LogicMTask*, 50> m_vx; // All vertices within the graph
// CONSTRUCTORS // CONSTRUCTORS
PartPropagateCpSelfTest() = default; PartPropagateCpSelfTest() = default;
+7 -5
View File
@@ -30,6 +30,7 @@
#include "V3Randomize.h" #include "V3Randomize.h"
#include "V3Ast.h" #include "V3Ast.h"
#include "V3MemberMap.h"
VL_DEFINE_DEBUG_FUNCTIONS; VL_DEFINE_DEBUG_FUNCTIONS;
@@ -138,6 +139,7 @@ private:
const VNUser2InUse m_inuser2; const VNUser2InUse m_inuser2;
// STATE // STATE
VMemberMap memberMap; // Member names cached for fast lookup
AstNodeModule* m_modp = nullptr; // Current module AstNodeModule* m_modp = nullptr; // Current module
const AstNodeFTask* m_ftaskp = nullptr; // Current function/task const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
size_t m_enumValueTabCount = 0; // Number of tables with enum values created size_t m_enumValueTabCount = 0; // Number of tables with enum values created
@@ -213,7 +215,7 @@ private:
} }
} }
void addPrePostCall(AstClass* classp, AstFunc* funcp, const string& name) { void addPrePostCall(AstClass* classp, AstFunc* funcp, const string& name) {
if (AstTask* userFuncp = VN_CAST(classp->findMember(name), Task)) { if (AstTask* userFuncp = VN_CAST(memberMap.findMember(classp, name), Task)) {
AstTaskRef* const callp AstTaskRef* const callp
= new AstTaskRef{userFuncp->fileline(), userFuncp->name(), nullptr}; = new AstTaskRef{userFuncp->fileline(), userFuncp->name(), nullptr};
callp->taskp(userFuncp); callp->taskp(userFuncp);
@@ -380,7 +382,8 @@ void V3Randomize::randomizeNetlist(AstNetlist* nodep) {
} }
AstFunc* V3Randomize::newRandomizeFunc(AstClass* nodep) { AstFunc* V3Randomize::newRandomizeFunc(AstClass* nodep) {
AstFunc* funcp = VN_AS(nodep->findMember("randomize"), Func); VMemberMap memberMap;
AstFunc* funcp = VN_AS(memberMap.findMember(nodep, "randomize"), Func);
if (!funcp) { if (!funcp) {
v3Global.useRandomizeMethods(true); v3Global.useRandomizeMethods(true);
AstNodeDType* const dtypep AstNodeDType* const dtypep
@@ -395,7 +398,6 @@ AstFunc* V3Randomize::newRandomizeFunc(AstClass* nodep) {
funcp->classMethod(true); funcp->classMethod(true);
funcp->isVirtual(nodep->isExtended()); funcp->isVirtual(nodep->isExtended());
nodep->addMembersp(funcp); nodep->addMembersp(funcp);
nodep->repairCache();
AstClass* const basep = nodep->baseMostClassp(); AstClass* const basep = nodep->baseMostClassp();
basep->needRNG(true); basep->needRNG(true);
} }
@@ -403,8 +405,9 @@ AstFunc* V3Randomize::newRandomizeFunc(AstClass* nodep) {
} }
AstFunc* V3Randomize::newSRandomFunc(AstClass* nodep) { AstFunc* V3Randomize::newSRandomFunc(AstClass* nodep) {
VMemberMap memberMap;
AstClass* const basep = nodep->baseMostClassp(); AstClass* const basep = nodep->baseMostClassp();
AstFunc* funcp = VN_AS(basep->findMember("srandom"), Func); AstFunc* funcp = VN_AS(memberMap.findMember(basep, "srandom"), Func);
if (!funcp) { if (!funcp) {
v3Global.useRandomizeMethods(true); v3Global.useRandomizeMethods(true);
AstNodeDType* const dtypep AstNodeDType* const dtypep
@@ -418,7 +421,6 @@ AstFunc* V3Randomize::newSRandomFunc(AstClass* nodep) {
funcp->classMethod(true); funcp->classMethod(true);
funcp->isVirtual(false); funcp->isVirtual(false);
basep->addMembersp(funcp); basep->addMembersp(funcp);
basep->repairCache();
funcp->addStmtsp(new AstCStmt{basep->fileline(), "__Vm_rng.srandom(seed);\n"}); funcp->addStmtsp(new AstCStmt{basep->fileline(), "__Vm_rng.srandom(seed);\n"});
basep->needRNG(true); basep->needRNG(true);
} }
+43 -15
View File
@@ -125,8 +125,43 @@ void splitCheck(AstCFunc* ofuncp) {
int funcnum = 0; int funcnum = 0;
int func_stmts = 0; int func_stmts = 0;
const bool is_ofuncp_coroutine = ofuncp->isCoroutine();
AstCFunc* funcp = nullptr; AstCFunc* funcp = nullptr;
const auto createNewSubFuncp = [&]() {
AstCFunc* const subFuncp = new AstCFunc{
ofuncp->fileline(), ofuncp->name() + "__" + cvtToStr(funcnum++), ofuncp->scopep()};
subFuncp->dontCombine(true);
subFuncp->isStatic(false);
subFuncp->isLoose(true);
subFuncp->slow(ofuncp->slow());
subFuncp->declPrivate(ofuncp->declPrivate());
func_stmts = 0;
return subFuncp;
};
const auto finishSubFuncp = [&](AstCFunc* subFuncp) {
ofuncp->scopep()->addBlocksp(subFuncp);
AstCCall* const callp = new AstCCall{subFuncp->fileline(), subFuncp};
callp->dtypeSetVoid();
if (is_ofuncp_coroutine && subFuncp->exists([](const AstCAwait*) {
return true;
})) { // Wrap call with co_await
subFuncp->rtnType("VlCoroutine");
AstCAwait* const awaitp = new AstCAwait{subFuncp->fileline(), callp};
awaitp->dtypeSetVoid();
ofuncp->addStmtsp(awaitp->makeStmt());
} else {
ofuncp->addStmtsp(callp->makeStmt());
}
};
funcp = createNewSubFuncp();
// Unlink all statements, then add item by item to new sub-functions // Unlink all statements, then add item by item to new sub-functions
AstBegin* const tempp = new AstBegin{ofuncp->fileline(), "[EditWrapper]", AstBegin* const tempp = new AstBegin{ofuncp->fileline(), "[EditWrapper]",
ofuncp->stmtsp()->unlinkFrBackWithNext()}; ofuncp->stmtsp()->unlinkFrBackWithNext()};
@@ -135,24 +170,16 @@ void splitCheck(AstCFunc* ofuncp) {
while (tempp->stmtsp()) { while (tempp->stmtsp()) {
AstNode* const itemp = tempp->stmtsp()->unlinkFrBack(); AstNode* const itemp = tempp->stmtsp()->unlinkFrBack();
const int stmts = itemp->nodeCount(); const int stmts = itemp->nodeCount();
if (!funcp || (func_stmts + stmts) > v3Global.opt.outputSplitCFuncs()) {
// Make a new function if ((func_stmts + stmts) > v3Global.opt.outputSplitCFuncs()) {
funcp = new AstCFunc{ofuncp->fileline(), ofuncp->name() + "__" + cvtToStr(funcnum++), finishSubFuncp(funcp);
ofuncp->scopep()}; funcp = createNewSubFuncp();
funcp->dontCombine(true);
funcp->isStatic(false);
funcp->isLoose(true);
funcp->slow(ofuncp->slow());
ofuncp->scopep()->addBlocksp(funcp);
//
AstCCall* const callp = new AstCCall{funcp->fileline(), funcp};
callp->dtypeSetVoid();
ofuncp->addStmtsp(callp->makeStmt());
func_stmts = 0;
} }
funcp->addStmtsp(itemp); funcp->addStmtsp(itemp);
func_stmts += stmts; func_stmts += stmts;
} }
finishSubFuncp(funcp);
VL_DO_DANGLING(tempp->deleteTree(), tempp); VL_DO_DANGLING(tempp->deleteTree(), tempp);
} }
@@ -247,7 +274,6 @@ void orderSequentially(AstCFunc* funcp, const LogicByScope& lbs) {
subFuncp = createNewSubFuncp(scopep); subFuncp = createNewSubFuncp(scopep);
subFuncp->name(subFuncp->name() + "__" + cvtToStr(scopep->user2Inc())); subFuncp->name(subFuncp->name() + "__" + cvtToStr(scopep->user2Inc()));
subFuncp->rtnType("VlCoroutine"); subFuncp->rtnType("VlCoroutine");
if (procp->needProcess()) subFuncp->setNeedProcess();
if (VN_IS(procp, Always)) { if (VN_IS(procp, Always)) {
subFuncp->slow(false); subFuncp->slow(false);
FileLine* const flp = procp->fileline(); FileLine* const flp = procp->fileline();
@@ -256,6 +282,8 @@ void orderSequentially(AstCFunc* funcp, const LogicByScope& lbs) {
} }
} }
subFuncp->addStmtsp(bodyp); subFuncp->addStmtsp(bodyp);
if (procp->needProcess()) subFuncp->setNeedProcess();
splitCheck(subFuncp);
} }
} else { } else {
logicp->unlinkFrBack(); logicp->unlinkFrBack();
+8 -16
View File
@@ -290,26 +290,18 @@ void transformForks(AstNetlist* const netlistp) {
funcp->foreach([&](AstNodeVarRef* refp) { funcp->foreach([&](AstNodeVarRef* refp) {
AstVar* const varp = refp->varp(); AstVar* const varp = refp->varp();
AstBasicDType* const dtypep = varp->dtypep()->basicp(); AstBasicDType* const dtypep = varp->dtypep()->basicp();
// If it a fork sync or an intra-assignment variable, pass it by value bool passByValue = false;
const bool passByValue = (dtypep && dtypep->isForkSync()) if (VString::startsWith(varp->name(), "__Vintra")) {
|| VString::startsWith(varp->name(), "__Vintra"); // Pass it by value to the new function, as otherwise there are issues with
if (passByValue) { // -flocalize (see t_timing_intra_assign)
// We can just pass it to the new function passByValue = true;
} else if (!varp->user1() || !varp->isFuncLocal()) { } else if (!varp->user1() || !varp->isFuncLocal()) {
// Not func local, or not declared before the fork. Their lifetime is longer // Not func local, or not declared before the fork. Their lifetime is longer
// than the forked process. Skip // than the forked process. Skip
return; return;
} else if (m_forkp->joinType().join()) { } else if (dtypep && dtypep->isForkSync()) {
// If it's fork..join, we can refer to variables from the parent process // We can just pass it by value to the new function
} else { passByValue = true;
// TODO: It is possible to relax this by allowing the use of such variables up
// until the first await. Also, variables defined within a forked process
// (inside a begin) are extracted out by V3Begin, so they also trigger this
// error. Preventing this (or detecting such cases and moving the vars back)
// would also allow for using them freely.
refp->v3warn(E_UNSUPPORTED, "Unsupported: variable local to a forking process "
"accessed in a fork..join_any or fork..join_none");
return;
} }
// Remap the reference // Remap the reference
AstVarScope* const vscp = refp->varScopep(); AstVarScope* const vscp = refp->varScopep();
+7 -1
View File
@@ -232,7 +232,13 @@ public:
} }
std::vector<AstNodeStmt*> getAndClearInits() { return std::move(m_inits); } std::vector<AstNodeStmt*> getAndClearInits() { return std::move(m_inits); }
std::vector<AstVar*> getAndClearLocals() { return std::move(m_locals); }
std::vector<AstVar*> getAndClearLocals() {
// With m_locals empty, m_prev and m_curr are no longer valid
m_prev.clear();
m_curr.clear();
return std::move(m_locals);
}
std::vector<AstNodeStmt*> getAndClearPreUpdates() { std::vector<AstNodeStmt*> getAndClearPreUpdates() {
m_hasPreUpdate.clear(); m_hasPreUpdate.clear();
+2 -2
View File
@@ -178,8 +178,8 @@ public:
m_whyNotNodep = nodep; m_whyNotNodep = nodep;
if (debug() >= 5) { if (debug() >= 5) {
UINFO(0, "Clear optimizable: " << why); UINFO(0, "Clear optimizable: " << why);
if (nodep) cout << ": " << nodep; if (nodep) std::cout << ": " << nodep;
cout << endl; std::cout << std::endl;
} }
m_whyNotOptimizable = why; m_whyNotOptimizable = why;
std::ostringstream stack; std::ostringstream stack;
+2 -4
View File
@@ -109,9 +109,7 @@ protected:
// ACCESSORS // ACCESSORS
// Do not make accessor for nodep(), It may change due to // Do not make accessor for nodep(), It may change due to
// reordering a lower block, but we don't repair it // reordering a lower block, but we don't repair it
string name() const override VL_MT_STABLE { string name() const override { return cvtToHex(m_nodep) + ' ' + m_nodep->prettyTypeName(); }
return cvtToHex(m_nodep) + ' ' + m_nodep->prettyTypeName();
}
FileLine* fileline() const override { return nodep()->fileline(); } FileLine* fileline() const override { return nodep()->fileline(); }
public: public:
@@ -148,7 +146,7 @@ public:
SplitVarPostVertex(V3Graph* graphp, AstNode* nodep) SplitVarPostVertex(V3Graph* graphp, AstNode* nodep)
: SplitNodeVertex{graphp, nodep} {} : SplitNodeVertex{graphp, nodep} {}
~SplitVarPostVertex() override = default; ~SplitVarPostVertex() override = default;
string name() const override VL_MT_STABLE { return string{"POST "} + SplitNodeVertex::name(); } string name() const override { return string{"POST "} + SplitNodeVertex::name(); }
string dotColor() const override { return "CadetBlue"; } string dotColor() const override { return "CadetBlue"; }
}; };
+9
View File
@@ -158,6 +158,15 @@ bool VString::isWhitespace(const string& str) {
return true; return true;
} }
string::size_type VString::leadingWhitespaceCount(const string& str) {
string::size_type result = 0;
for (const char c : str) {
++result;
if (!std::isspace(c)) break;
}
return result;
}
double VString::parseDouble(const string& str, bool* successp) { double VString::parseDouble(const string& str, bool* successp) {
char* const strgp = new char[str.size() + 1]; char* const strgp = new char[str.size() + 1];
char* dp = strgp; char* dp = strgp;
+2
View File
@@ -113,6 +113,8 @@ public:
static string removeWhitespace(const string& str); static string removeWhitespace(const string& str);
// Return true if only whitespace or "" // Return true if only whitespace or ""
static bool isWhitespace(const string& str); static bool isWhitespace(const string& str);
// Return number of spaces/tabs leading in string
static string::size_type leadingWhitespaceCount(const string& str);
// Return double by parsing string // Return double by parsing string
static double parseDouble(const string& str, bool* successp); static double parseDouble(const string& str, bool* successp);
// Replace all occurrences of the word 'from' in 'str' with 'to'. A word is considered // Replace all occurrences of the word 'from' in 'str' with 'to'. A word is considered
+2 -1
View File
@@ -123,7 +123,8 @@ public:
<< " " << entp->nodep() << endl); << " " << entp->nodep() << endl);
if (name != "" && m_idNameMap.find(name) != m_idNameMap.end()) { if (name != "" && m_idNameMap.find(name) != m_idNameMap.end()) {
if (!V3Error::errorCount()) { // Else may have just reported warning if (!V3Error::errorCount()) { // Else may have just reported warning
if (debug() >= 9 || V3Error::debugDefault()) dumpSelf(cout, "- err-dump: ", 1); if (debug() >= 9 || V3Error::debugDefault())
dumpSelf(std::cout, "- err-dump: ", 1);
entp->nodep()->v3fatalSrc("Inserting two symbols with same name: " << name); entp->nodep()->v3fatalSrc("Inserting two symbols with same name: " << name);
} }
} else { } else {
+31 -20
View File
@@ -350,7 +350,6 @@ private:
// TYPES // TYPES
enum InsertMode : uint8_t { enum InsertMode : uint8_t {
IM_BEFORE, // Pointing at statement ref is in, insert before this IM_BEFORE, // Pointing at statement ref is in, insert before this
IM_AFTER, // Pointing at last inserted stmt, insert after
IM_WHILE_PRECOND // Pointing to for loop, add to body end IM_WHILE_PRECOND // Pointing to for loop, add to body end
}; };
using DpiCFuncs = std::map<const string, std::tuple<AstNodeFTask*, std::string, AstCFunc*>>; using DpiCFuncs = std::map<const string, std::tuple<AstNodeFTask*, std::string, AstCFunc*>>;
@@ -413,7 +412,6 @@ private:
AstVarScope* const newvscp = VN_AS(refp->varp()->user2p(), VarScope); AstVarScope* const newvscp = VN_AS(refp->varp()->user2p(), VarScope);
refp->varScopep(newvscp); refp->varScopep(newvscp);
refp->varp(refp->varScopep()->varp()); refp->varp(refp->varScopep()->varp());
refp->name(refp->varp()->name());
} }
}); });
} }
@@ -657,8 +655,15 @@ private:
args += ", "; args += ", ";
dpiproto += ", "; dpiproto += ", ";
} }
// Include both the Verilator and C type names, as if either
// differ we may get C compilation problems later
const std::string dpiType = portp->dpiArgType(false, false);
dpiproto += dpiType;
const std::string vType = portp->dtypep()->prettyDTypeName();
if (!portp->isDpiOpenArray() && dpiType != vType) {
dpiproto += " /* " + vType + " */ ";
}
args += portp->name(); // Leftover so ,'s look nice args += portp->name(); // Leftover so ,'s look nice
if (nodep->dpiImport()) dpiproto += portp->dpiArgType(false, false);
} }
} }
} }
@@ -929,15 +934,15 @@ private:
m_dpiNames.emplace(nodep->cname(), std::make_tuple(nodep, signature, funcp)); m_dpiNames.emplace(nodep->cname(), std::make_tuple(nodep, signature, funcp));
return funcp; return funcp;
} else { } else {
// Seen this cname before. Check if it's the same prototype. // Seen this cname import before. Check if it's the same prototype.
const AstNodeFTask* firstNodep; const AstNodeFTask* firstNodep;
string firstSignature; string firstSignature;
AstCFunc* firstFuncp; AstCFunc* firstFuncp;
std::tie(firstNodep, firstSignature, firstFuncp) = it->second; std::tie(firstNodep, firstSignature, firstFuncp) = it->second;
if (signature != firstSignature) { if (signature != firstSignature) {
// Different signature, so error. // Different signature, so error.
nodep->v3error("Duplicate declaration of DPI function with different signature: " nodep->v3error("Duplicate declaration of DPI function with different signature: '"
<< nodep->prettyNameQ() << '\n' << nodep->cname() << "'\n"
<< nodep->warnContextPrimary() << '\n' << nodep->warnContextPrimary() << '\n'
<< nodep->warnMore() // << nodep->warnMore() //
<< "... New signature: " << signature << '\n' // << "... New signature: " << signature << '\n' //
@@ -1093,7 +1098,7 @@ private:
if (nodep->isFunction()) { if (nodep->isFunction()) {
AstVar* const portp = VN_AS(nodep->fvarp(), Var); AstVar* const portp = VN_AS(nodep->fvarp(), Var);
UASSERT_OBJ(portp, nodep, "function without function output variable"); UASSERT_OBJ(portp, nodep, "function without function output variable");
if (!portp->isFuncReturn()) nodep->v3error("Not marked as function return var"); UASSERT_OBJ(portp->isFuncReturn(), nodep, "Not marked as function return var");
if (nodep->dpiImport() || nodep->dpiExport()) { if (nodep->dpiImport() || nodep->dpiExport()) {
AstBasicDType* const bdtypep = portp->dtypep()->basicp(); AstBasicDType* const bdtypep = portp->dtypep()->basicp();
if (!bdtypep->isDpiPrimitive()) { if (!bdtypep->isDpiPrimitive()) {
@@ -1108,7 +1113,7 @@ private:
"other than a single 'logic' (IEEE 1800-2017 35.5.5)"); "other than a single 'logic' (IEEE 1800-2017 35.5.5)");
} }
} }
} else { } else if (nodep->taskPublic()) {
if (portp->isWide()) { if (portp->isWide()) {
nodep->v3warn(E_UNSUPPORTED, nodep->v3warn(E_UNSUPPORTED,
"Unsupported: Public functions with return > 64 bits wide.\n" "Unsupported: Public functions with return > 64 bits wide.\n"
@@ -1317,7 +1322,7 @@ private:
} }
// Mark the fact that this function allocates std::process // Mark the fact that this function allocates std::process
if (nodep->isFromStd() && nodep->name() == "self") cfuncp->setNeedProcess(); if (nodep->needProcess()) cfuncp->setNeedProcess();
// Delete rest of cloned task and return new func // Delete rest of cloned task and return new func
VL_DO_DANGLING(pushDeletep(nodep), nodep); VL_DO_DANGLING(pushDeletep(nodep), nodep);
@@ -1339,16 +1344,13 @@ private:
AstNode* insertBeforeStmt(AstNode* nodep, AstNode* newp) { AstNode* insertBeforeStmt(AstNode* nodep, AstNode* newp) {
// Return node that must be visited, if any // Return node that must be visited, if any
if (debug() >= 9) nodep->dumpTree("- newstmt: "); if (debug() >= 9) nodep->dumpTree("- newstmt: ");
UASSERT_OBJ(m_insStmtp, nodep, "Function not underneath a statement"); UASSERT_OBJ(m_insStmtp, nodep, "Function call not underneath a statement");
AstNode* visitp = nullptr; AstNode* visitp = nullptr;
if (m_insMode == IM_BEFORE) { if (m_insMode == IM_BEFORE) {
// Add the whole thing before insertAt // Add the whole thing before insertAt
UINFO(5, " IM_Before " << m_insStmtp << endl); UINFO(5, " IM_Before " << m_insStmtp << endl);
if (debug() >= 9) newp->dumpTree("- newfunc: "); if (debug() >= 9) newp->dumpTree("- newfunc: ");
m_insStmtp->addHereThisAsNext(newp); m_insStmtp->addHereThisAsNext(newp);
} else if (m_insMode == IM_AFTER) {
UINFO(5, " IM_After " << m_insStmtp << endl);
m_insStmtp->addNextHere(newp);
} else if (m_insMode == IM_WHILE_PRECOND) { } else if (m_insMode == IM_WHILE_PRECOND) {
UINFO(5, " IM_While_Precond " << m_insStmtp << endl); UINFO(5, " IM_While_Precond " << m_insStmtp << endl);
AstWhile* const whilep = VN_AS(m_insStmtp, While); AstWhile* const whilep = VN_AS(m_insStmtp, While);
@@ -1358,8 +1360,6 @@ private:
} else { } else {
nodep->v3fatalSrc("Unknown InsertMode"); nodep->v3fatalSrc("Unknown InsertMode");
} }
m_insMode = IM_AFTER;
m_insStmtp = newp;
return visitp; return visitp;
} }
@@ -1539,6 +1539,13 @@ private:
iterateChildren(nodep); iterateChildren(nodep);
m_insStmtp = nullptr; // Next thing should be new statement m_insStmtp = nullptr; // Next thing should be new statement
} }
void visit(AstVar* nodep) override {
if (nodep->isFuncLocal() && nodep->direction() == VDirection::INPUT && nodep->valuep()) {
// It's the default value of optional argument.
// Such values are added to function calls on this stage and aren't needed here.
pushDeletep(nodep->valuep()->unlinkFrBack());
}
}
void visit(AstStmtExpr* nodep) override { void visit(AstStmtExpr* nodep) override {
m_insMode = IM_BEFORE; m_insMode = IM_BEFORE;
m_insStmtp = nodep; m_insStmtp = nodep;
@@ -1651,7 +1658,14 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp)
<< portp->prettyNameQ() << " in function call to " << portp->prettyNameQ() << " in function call to "
<< nodep->taskp()->prettyTypeName()); << nodep->taskp()->prettyTypeName());
newvaluep = new AstConst{nodep->fileline(), AstConst::Unsized32{}, 0}; newvaluep = new AstConst{nodep->fileline(), AstConst::Unsized32{}, 0};
} else if (!VN_IS(portp->valuep(), Const)) { } else if (AstFuncRef* const funcRefp = VN_CAST(portp->valuep(), FuncRef)) {
const AstNodeFTask* const funcp = funcRefp->taskp();
if (funcp->classMethod() && funcp->lifetime().isStatic()) newvaluep = funcRefp;
} else if (AstConst* const constp = VN_CAST(portp->valuep(), Const)) {
newvaluep = constp;
}
if (!newvaluep) {
// The default value for this port might be a constant // The default value for this port might be a constant
// expression that hasn't been folded yet. Try folding it // expression that hasn't been folded yet. Try folding it
// now; we don't have much to lose if it fails. // now; we don't have much to lose if it fails.
@@ -1665,12 +1679,9 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp)
<< portp->prettyNameQ() << " in function call to " << portp->prettyNameQ() << " in function call to "
<< nodep->taskp()->prettyTypeName()); << nodep->taskp()->prettyTypeName());
newvaluep = new AstConst{nodep->fileline(), AstConst::Unsized32{}, 0}; newvaluep = new AstConst{nodep->fileline(), AstConst::Unsized32{}, 0};
} else {
newvaluep = newvaluep->cloneTree(true);
} }
} else {
newvaluep = VN_AS(portp->valuep(), NodeExpr)->cloneTree(true);
} }
newvaluep = newvaluep->cloneTree(true);
// To avoid problems with callee needing to know to deleteTree // To avoid problems with callee needing to know to deleteTree
// or not, we make this into a pin // or not, we make this into a pin
UINFO(9, "Default pin for " << portp << endl); UINFO(9, "Default pin for " << portp << endl);
+16 -51
View File
@@ -61,7 +61,7 @@ void V3ThreadPool::workerJobLoop(int id) VL_MT_SAFE {
while (true) { while (true) {
// Wait for a notification // Wait for a notification
waitIfStopRequested(); waitIfStopRequested();
job_t job; VAnyPackagedTask job;
{ {
V3LockGuard lock(m_mutex); V3LockGuard lock(m_mutex);
m_cv.wait(m_mutex, [&]() VL_REQUIRES(m_mutex) { m_cv.wait(m_mutex, [&]() VL_REQUIRES(m_mutex) {
@@ -72,7 +72,7 @@ void V3ThreadPool::workerJobLoop(int id) VL_MT_SAFE {
// Get the job // Get the job
UASSERT(!m_queue.empty(), "Job should be available"); UASSERT(!m_queue.empty(), "Job should be available");
job = m_queue.front(); job = std::move(m_queue.front());
m_queue.pop(); m_queue.pop();
} }
@@ -81,40 +81,6 @@ void V3ThreadPool::workerJobLoop(int id) VL_MT_SAFE {
} }
} }
template <>
void V3ThreadPool::pushJob<void>(std::shared_ptr<std::promise<void>>& prom,
std::function<void()>&& f) VL_MT_SAFE {
if (willExecuteSynchronously()) {
f();
prom->set_value();
} else {
const V3LockGuard lock{m_mutex};
m_queue.push([prom, f] {
f();
prom->set_value();
});
}
}
void V3ThreadPool::requestExclusiveAccess(const V3ThreadPool::job_t&& exclusiveAccessJob)
VL_MT_SAFE {
if (willExecuteSynchronously()) {
exclusiveAccessJob();
} else {
V3LockGuard stoppedJobLock{m_stoppedJobsMutex};
// if some other job already requested exclusive access
// wait until it stops
if (stopRequested()) { waitStopRequested(); }
m_stopRequested = true;
waitOtherThreads();
m_exclusiveAccess = true;
exclusiveAccessJob();
m_exclusiveAccess = false;
m_stopRequested = false;
m_stoppedJobsCV.notify_all();
}
}
bool V3ThreadPool::waitIfStopRequested() VL_MT_SAFE VL_EXCLUDES(m_stoppedJobsMutex) { bool V3ThreadPool::waitIfStopRequested() VL_MT_SAFE VL_EXCLUDES(m_stoppedJobsMutex) {
if (!stopRequested()) return false; if (!stopRequested()) return false;
V3LockGuard stoppedJobLock(m_stoppedJobsMutex); V3LockGuard stoppedJobLock(m_stoppedJobsMutex);
@@ -150,11 +116,10 @@ void V3ThreadPool::selfTest() {
auto firstJob = [&](int sleep) -> void { auto firstJob = [&](int sleep) -> void {
std::this_thread::sleep_for(std::chrono::milliseconds{sleep}); std::this_thread::sleep_for(std::chrono::milliseconds{sleep});
s().requestExclusiveAccess([&]() { const V3ThreadPool::ScopedExclusiveAccess exclusiveAccess;
commonValue = 10; commonValue = 10;
std::this_thread::sleep_for(std::chrono::milliseconds{sleep + 10}); std::this_thread::sleep_for(std::chrono::milliseconds{sleep + 10});
UASSERT(commonValue == 10, "unexpected commonValue = " << commonValue); UASSERT(commonValue == 10, "unexpected commonValue = " << commonValue);
});
}; };
auto secondJob = [&](int sleep) -> void { auto secondJob = [&](int sleep) -> void {
commonMutex.lock(); commonMutex.lock();
@@ -175,19 +140,19 @@ void V3ThreadPool::selfTest() {
}; };
std::list<std::future<void>> futures; std::list<std::future<void>> futures;
futures.push_back(s().enqueue<void>(std::bind(firstJob, 100))); futures.push_back(s().enqueue(std::bind(firstJob, 100)));
futures.push_back(s().enqueue<void>(std::bind(secondJob, 100))); futures.push_back(s().enqueue(std::bind(secondJob, 100)));
futures.push_back(s().enqueue<void>(std::bind(firstJob, 100))); futures.push_back(s().enqueue(std::bind(firstJob, 100)));
futures.push_back(s().enqueue<void>(std::bind(secondJob, 100))); futures.push_back(s().enqueue(std::bind(secondJob, 100)));
futures.push_back(s().enqueue<void>(std::bind(secondJob, 200))); futures.push_back(s().enqueue(std::bind(secondJob, 200)));
futures.push_back(s().enqueue<void>(std::bind(firstJob, 200))); futures.push_back(s().enqueue(std::bind(firstJob, 200)));
futures.push_back(s().enqueue<void>(std::bind(firstJob, 300))); futures.push_back(s().enqueue(std::bind(firstJob, 300)));
while (!futures.empty()) { while (!futures.empty()) {
s().waitForFuture(futures.front()); s().waitForFuture(futures.front());
futures.pop_front(); futures.pop_front();
} }
futures.push_back(s().enqueue<void>(std::bind(thirdJob, 100))); futures.push_back(s().enqueue(std::bind(thirdJob, 100)));
futures.push_back(s().enqueue<void>(std::bind(thirdJob, 100))); futures.push_back(s().enqueue(std::bind(thirdJob, 100)));
V3ThreadPool::waitForFutures(futures); V3ThreadPool::waitForFutures(futures);
s().waitIfStopRequested(); s().waitIfStopRequested();
@@ -195,7 +160,7 @@ void V3ThreadPool::selfTest() {
auto forthJob = [&]() -> int { return 1234; }; auto forthJob = [&]() -> int { return 1234; };
std::list<std::future<int>> futuresInt; std::list<std::future<int>> futuresInt;
futuresInt.push_back(s().enqueue<int>(forthJob)); futuresInt.push_back(s().enqueue(forthJob));
auto result = V3ThreadPool::waitForFutures(futuresInt); auto result = V3ThreadPool::waitForFutures(futuresInt);
UASSERT(result.back() == 1234, "unexpected future result = " << result.back()); UASSERT(result.back() == 1234, "unexpected future result = " << result.back());
} }
+97 -28
View File
@@ -29,13 +29,53 @@
//============================================================================ //============================================================================
// Callable, type-erased wrapper for std::packaged_task<Signature> with any Signature.
class VAnyPackagedTask final {
// TYPES
struct PTWrapperBase {
virtual ~PTWrapperBase() {}
virtual void operator()() = 0;
};
template <typename Signature>
struct PTWrapper final : PTWrapperBase {
std::packaged_task<Signature> m_pt;
PTWrapper(std::packaged_task<Signature>&& pt)
: m_pt(std::move(pt)) {}
void operator()() final override { m_pt(); }
};
// MEMBERS
std::unique_ptr<PTWrapperBase> m_ptWrapperp = nullptr; // Wrapper to call
public:
// CONSTRUCTORS
template <typename Signature>
VAnyPackagedTask(std::packaged_task<Signature>&& pt)
: m_ptWrapperp{std::make_unique<PTWrapper<Signature>>(std::move(pt))} {}
VAnyPackagedTask() = default;
~VAnyPackagedTask() = default;
VAnyPackagedTask(const VAnyPackagedTask&) = delete;
VAnyPackagedTask& operator=(const VAnyPackagedTask&) = delete;
VAnyPackagedTask(VAnyPackagedTask&&) = default;
VAnyPackagedTask& operator=(VAnyPackagedTask&&) = default;
// METHODS
// Call the wrapped function
void operator()() { (*m_ptWrapperp)(); }
};
class V3ThreadPool final { class V3ThreadPool final {
// MEMBERS // MEMBERS
static constexpr unsigned int FUTUREWAITFOR_MS = 100; static constexpr unsigned int FUTUREWAITFOR_MS = 100;
using job_t = std::function<void()>;
mutable V3Mutex m_mutex; // Mutex for use by m_queue V3Mutex m_mutex; // Mutex for use by m_queue
std::queue<job_t> m_queue VL_GUARDED_BY(m_mutex); // Queue of jobs std::queue<VAnyPackagedTask> m_queue VL_GUARDED_BY(m_mutex); // Queue of jobs
// We don't need to guard this condition_variable as // We don't need to guard this condition_variable as
// both `notify_one` and `notify_all` functions are atomic, // both `notify_one` and `notify_all` functions are atomic,
// `wait` function is not atomic, but we are guarding `m_queue` that is // `wait` function is not atomic, but we are guarding `m_queue` that is
@@ -62,6 +102,9 @@ class V3ThreadPool final {
} }
public: public:
// Request exclusive access to processing for the object lifetime.
class ScopedExclusiveAccess;
// METHODS // METHODS
// Singleton // Singleton
static V3ThreadPool& s() VL_MT_SAFE { static V3ThreadPool& s() VL_MT_SAFE {
@@ -79,8 +122,8 @@ public:
// will call it. `VL_MT_START` here indicates that // will call it. `VL_MT_START` here indicates that
// every function call inside this `std::function` requires // every function call inside this `std::function` requires
// annotations. // annotations.
template <typename T> template <typename Callable>
std::future<T> enqueue(std::function<T()>&& f) VL_MT_START; auto enqueue(Callable&& f) VL_MT_START;
// Request exclusive access to processing. // Request exclusive access to processing.
// It sends request to stop all other threads and waits for them to stop. // It sends request to stop all other threads and waits for them to stop.
@@ -88,7 +131,9 @@ public:
// they can be stopped. // they can be stopped.
// When all other threads are stopped, this function executes the job // When all other threads are stopped, this function executes the job
// and resumes execution of other jobs. // and resumes execution of other jobs.
void requestExclusiveAccess(const job_t&& exclusiveAccessJob) VL_MT_SAFE; template <typename Callable>
void requestExclusiveAccess(Callable&& exclusiveAccessJob) VL_MT_SAFE
VL_EXCLUDES(m_stoppedJobsMutex);
// Check if other thread requested exclusive access to processing, // Check if other thread requested exclusive access to processing,
// if so, it waits for it to complete. Afterwards it is resumed. // if so, it waits for it to complete. Afterwards it is resumed.
@@ -151,14 +196,39 @@ private:
// Waits until all other jobs are stopped // Waits until all other jobs are stopped
void waitOtherThreads() VL_MT_SAFE_EXCLUDES(m_mutex) VL_REQUIRES(m_stoppedJobsMutex); void waitOtherThreads() VL_MT_SAFE_EXCLUDES(m_mutex) VL_REQUIRES(m_stoppedJobsMutex);
template <typename T>
void pushJob(std::shared_ptr<std::promise<T>>& prom, std::function<T()>&& f) VL_MT_SAFE;
void workerJobLoop(int id) VL_MT_SAFE; void workerJobLoop(int id) VL_MT_SAFE;
static void startWorker(V3ThreadPool* selfThreadp, int id) VL_MT_SAFE; static void startWorker(V3ThreadPool* selfThreadp, int id) VL_MT_SAFE;
}; };
class VL_SCOPED_CAPABILITY V3ThreadPool::ScopedExclusiveAccess final {
public:
ScopedExclusiveAccess() VL_ACQUIRE(V3ThreadPool::s().m_stoppedJobsMutex) VL_MT_SAFE {
if (!V3ThreadPool::s().willExecuteSynchronously()) {
V3ThreadPool::s().m_stoppedJobsMutex.lock();
if (V3ThreadPool::s().stopRequested()) { V3ThreadPool::s().waitStopRequested(); }
V3ThreadPool::s().m_stopRequested = true;
V3ThreadPool::s().waitOtherThreads();
V3ThreadPool::s().m_exclusiveAccess = true;
} else {
V3ThreadPool::s().m_stoppedJobsMutex.assumeLocked();
}
}
~ScopedExclusiveAccess() VL_RELEASE(V3ThreadPool::s().m_stoppedJobsMutex) VL_MT_SAFE {
// Can't use `willExecuteSynchronously`, we're still in exclusive execution state.
if (V3ThreadPool::s().m_exclusiveAccess) {
V3ThreadPool::s().m_exclusiveAccess = false;
V3ThreadPool::s().m_stopRequested = false;
V3ThreadPool::s().m_stoppedJobsCV.notify_all();
V3ThreadPool::s().m_stoppedJobsMutex.unlock();
} else {
V3ThreadPool::s().m_stoppedJobsMutex.pretendUnlock();
}
}
};
template <typename T> template <typename T>
T V3ThreadPool::waitForFuture(std::future<T>& future) VL_MT_SAFE_EXCLUDES(m_mutex) { T V3ThreadPool::waitForFuture(std::future<T>& future) VL_MT_SAFE_EXCLUDES(m_mutex) {
while (true) { while (true) {
@@ -175,29 +245,28 @@ T V3ThreadPool::waitForFuture(std::future<T>& future) VL_MT_SAFE_EXCLUDES(m_mute
} }
} }
template <typename T> template <typename Callable>
std::future<T> V3ThreadPool::enqueue(std::function<T()>&& f) VL_MT_START { auto V3ThreadPool::enqueue(Callable&& f) VL_MT_START {
std::shared_ptr<std::promise<T>> prom = std::make_shared<std::promise<T>>(); using result_t = decltype(f());
std::future<T> result = prom->get_future(); auto&& job = std::packaged_task<result_t()>{std::forward<Callable>(f)};
pushJob(prom, std::move(f)); auto future = job.get_future();
const V3LockGuard guard{m_mutex};
m_cv.notify_one();
return result;
}
template <typename T>
void V3ThreadPool::pushJob(std::shared_ptr<std::promise<T>>& prom,
std::function<T()>&& f) VL_MT_SAFE {
if (willExecuteSynchronously()) { if (willExecuteSynchronously()) {
prom->set_value(f()); job();
} else { } else {
const V3LockGuard guard{m_mutex}; {
m_queue.push([prom, f] { prom->set_value(f()); }); const V3LockGuard guard{m_mutex};
m_queue.push(std::move(job));
}
m_cv.notify_one();
} }
return future;
} }
template <> template <typename Callable>
void V3ThreadPool::pushJob<void>(std::shared_ptr<std::promise<void>>& prom, void V3ThreadPool::requestExclusiveAccess(Callable&& exclusiveAccessJob) VL_MT_SAFE
std::function<void()>&& f) VL_MT_SAFE; VL_EXCLUDES(m_stoppedJobsMutex) {
ScopedExclusiveAccess exclusive_access;
exclusiveAccessJob();
}
#endif // Guard #endif // Guard
+208 -96
View File
@@ -53,6 +53,7 @@
#include "V3Const.h" #include "V3Const.h"
#include "V3EmitV.h" #include "V3EmitV.h"
#include "V3Graph.h" #include "V3Graph.h"
#include "V3MemberMap.h"
#include "V3SenExprBuilder.h" #include "V3SenExprBuilder.h"
#include "V3SenTree.h" #include "V3SenTree.h"
#include "V3UniqueNames.h" #include "V3UniqueNames.h"
@@ -63,41 +64,85 @@ VL_DEFINE_DEBUG_FUNCTIONS;
// ###################################################################### // ######################################################################
enum TimingFlag : uint8_t { enum NodeFlag : uint8_t {
// Properties of flags with higher numbers include properties of flags with T_SUSPENDEE = 1 << 0, // Suspendable (due to dependence on another suspendable)
// lower numbers T_SUSPENDER = 1 << 1, // Suspendable (has timing control)
T_NORM = 0, // Normal non-suspendable process T_HAS_PROC = 1 << 2, // Has an associated std::process
T_SUSP = 1, // Suspendable T_CALLS_PROC_SELF = 1 << 3, // Calls std::process::self
T_PROC = 2 // Suspendable with process metadata };
enum ForkType : uint8_t {
F_NONE = 0, // Not under a fork
F_MIGHT_SUSPEND = 1 << 0, // Fork might suspend the execution of current process
F_MIGHT_NEED_PROC = 1 << 1, // Fork might need a process (any fork really)
};
enum PropagationType : uint8_t {
P_CALL = 1, // Propagation through call to a function/task/method
P_FORK = 2, // Propagation due to fork's behaviour
P_SIGNATURE = 3, // Propagation required to maintain C++ function's signature requirements
}; };
// ###################################################################### // ######################################################################
// Detect nodes affected by timing // Detect nodes affected by timing and/or requiring a process
class TimingSuspendableVisitor final : public VNVisitor { class TimingSuspendableVisitor final : public VNVisitor {
private: private:
// TYPES // TYPES
// Vertex of a dependency graph of suspendable nodes, e.g. if a node (process or task) is // Vertex of a dependency graph of suspendable nodes, e.g. if a node (process or task) is
// suspendable, all its dependents should also be suspendable // suspendable, all its dependents should also be suspendable
class TimingDependencyVertex final : public V3GraphVertex { class DepVtx VL_NOT_FINAL : public V3GraphVertex {
AstClass* const m_classp; // Class associated with a method
AstNode* const m_nodep; // AST node represented by this graph vertex AstNode* const m_nodep; // AST node represented by this graph vertex
// ACCESSORS // ACCESSORS
string name() const override VL_MT_STABLE { string name() const override {
if (m_classp) {
if (VN_IS(nodep(), CFunc)) {
return cvtToHex(nodep()) + ' ' + classp()->name() + "::" + nodep()->name();
}
}
return cvtToHex(nodep()) + ' ' + nodep()->prettyTypeName(); return cvtToHex(nodep()) + ' ' + nodep()->prettyTypeName();
} }
FileLine* fileline() const override { return nodep()->fileline(); } FileLine* fileline() const override { return nodep()->fileline(); }
string dotColor() const override { return nodep()->user2() ? "red" : "black"; }
public: public:
// CONSTRUCTORS // CONSTRUCTORS
TimingDependencyVertex(V3Graph* graphp, AstNode* nodep) DepVtx(V3Graph* graphp, AstNode* nodep, AstClass* classp)
: V3GraphVertex{graphp} : V3GraphVertex{graphp}
, m_classp{classp}
, m_nodep{nodep} {} , m_nodep{nodep} {}
~TimingDependencyVertex() override = default; ~DepVtx() override = default;
// ACCESSORS // ACCESSORS
virtual AstNode* nodep() const VL_MT_STABLE { return m_nodep; } virtual AstNode* nodep() const VL_MT_STABLE { return m_nodep; }
virtual AstNode* classp() const VL_MT_STABLE { return m_classp; }
};
class SuspendDepVtx final : public DepVtx {
string dotColor() const override {
if (nodep()->user2() & T_SUSPENDER) return "red";
if (nodep()->user2() & T_SUSPENDEE) return "blue";
return "black";
}
public:
SuspendDepVtx(V3Graph* graphp, AstNode* nodep, AstClass* classp)
: DepVtx{graphp, nodep, classp} {}
~SuspendDepVtx() override = default;
};
class NeedsProcDepVtx final : public DepVtx {
string dotColor() const override {
if (nodep()->user2() & T_CALLS_PROC_SELF) return "red";
if (nodep()->user2() & T_HAS_PROC) return "blue";
return "black";
}
public:
NeedsProcDepVtx(V3Graph* graphp, AstNode* nodep, AstClass* classp)
: DepVtx{graphp, nodep, classp} {}
~NeedsProcDepVtx() override = default;
}; };
// NODE STATE // NODE STATE
@@ -108,40 +153,70 @@ private:
// process/task suspendable // process/task suspendable
// and to T_PROC if it // and to T_PROC if it
// needs process metadata. // needs process metadata.
// Ast{NodeProcedure,CFunc,Begin}::user3() -> DependencyVertex*. Vertex in m_depGraph // Ast{NodeProcedure,CFunc,Begin}::user3() -> DependencyVertex*. Vertex in m_suspGraph
// Ast{NodeProcedure,CFunc,Begin}::user3() -> DependencyVertex*. Vertex in m_procGraph
const VNUser1InUse m_user1InUse; const VNUser1InUse m_user1InUse;
const VNUser2InUse m_user2InUse; const VNUser2InUse m_user2InUse;
const VNUser3InUse m_user3InUse; const VNUser3InUse m_user3InUse;
const VNUser5InUse m_user5InUse;
// STATE // STATE
VMemberMap memberMap; // Member names cached for fast lookup
AstClass* m_classp = nullptr; // Current class AstClass* m_classp = nullptr; // Current class
AstNode* m_procp = nullptr; // NodeProcedure/CFunc/Begin we're under AstNode* m_procp = nullptr; // NodeProcedure/CFunc/Begin we're under
V3Graph m_depGraph; // Dependency graph where a node is a dependency of another if it being uint8_t m_underFork = F_NONE; // F_NONE or flags of a fork we are under
// suspendable makes the other node suspendable V3Graph m_suspGraph; // Dependency graph where a node is a dependency of another if it being
// suspendable makes the other node suspendable
V3Graph m_procGraph; // Dependency graph where a node is a dependency of another if it being
// suspendable makes the other node suspendable
// METHODS // METHODS
// Get or create the dependency vertex for the given node // Get or create the dependency vertex for the given node
TimingDependencyVertex* getDependencyVertex(AstNode* const nodep) { DepVtx* getSuspendDepVtx(AstNode* const nodep) {
if (!nodep->user3p()) nodep->user3p(new TimingDependencyVertex{&m_depGraph, nodep}); AstClass* classp = nullptr;
return nodep->user3u().to<TimingDependencyVertex*>(); if (AstCFunc* funcp = VN_CAST(nodep, CFunc)) {
if (funcp->scopep() && funcp->scopep()->modp()) {
classp = VN_CAST(funcp->scopep()->modp(), Class);
}
}
if (!nodep->user3p()) nodep->user3p(new SuspendDepVtx{&m_suspGraph, nodep, classp});
return nodep->user3u().to<SuspendDepVtx*>();
}
DepVtx* getNeedsProcDepVtx(AstNode* const nodep) {
AstClass* classp = nullptr;
if (AstCFunc* funcp = VN_CAST(nodep, CFunc)) {
if (funcp->scopep() && funcp->scopep()->modp()) {
classp = VN_CAST(funcp->scopep()->modp(), Class);
}
}
if (!nodep->user5p()) nodep->user5p(new NeedsProcDepVtx{&m_procGraph, nodep, classp});
return nodep->user5u().to<NeedsProcDepVtx*>();
} }
// Set timing flag of a node // Set timing flag of a node
bool setTimingFlag(AstNode* nodep, int flag) { bool passFlag(const AstNode* from, AstNode* to, NodeFlag flag) {
// Properties of flags with higher numbers include properties of flags with lower if ((from->user2() & flag) && !(to->user2() & flag)) {
// numbers, so modify nodep->user2() only if it will increase. to->user2(to->user2() | flag);
if (nodep->user2() < flag) {
nodep->user2(flag);
return true; return true;
} }
return false; return false;
} }
// Propagate suspendable/needProcess flag to all nodes that depend on the given one // Propagate flag to all nodes that depend on the given one
void propagateTimingFlags(TimingDependencyVertex* const vxp) { void propagateFlags(DepVtx* const vxp, NodeFlag flag) {
auto* const parentp = vxp->nodep();
for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
auto* const depVxp = static_cast<DepVtx*>(edgep->top());
AstNode* const depp = depVxp->nodep();
if (passFlag(parentp, depp, flag)) propagateFlags(depVxp, flag);
}
}
template <typename Predicate>
void propagateFlagsReversedIf(DepVtx* const vxp, NodeFlag flag, Predicate p) {
auto* const parentp = vxp->nodep(); auto* const parentp = vxp->nodep();
for (V3GraphEdge* edgep = vxp->inBeginp(); edgep; edgep = edgep->inNextp()) { for (V3GraphEdge* edgep = vxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
auto* const depVxp = static_cast<TimingDependencyVertex*>(edgep->fromp()); auto* const depVxp = static_cast<DepVtx*>(edgep->fromp());
AstNode* const depp = depVxp->nodep(); AstNode* const depp = depVxp->nodep();
if (setTimingFlag(depp, parentp->user2())) propagateTimingFlags(depVxp); if (p(edgep) && passFlag(parentp, depp, flag))
propagateFlagsReversedIf(depVxp, flag, p);
} }
} }
@@ -155,17 +230,20 @@ private:
void visit(AstNodeProcedure* nodep) override { void visit(AstNodeProcedure* nodep) override {
VL_RESTORER(m_procp); VL_RESTORER(m_procp);
m_procp = nodep; m_procp = nodep;
if (nodep->needProcess()) nodep->user2(T_HAS_PROC | T_CALLS_PROC_SELF);
if (VN_IS(nodep, Always)) {
UINFO(1, "Always does " << (nodep->needProcess() ? "" : "NOT ") << "need process\n");
}
iterateChildren(nodep); iterateChildren(nodep);
} }
void visit(AstCFunc* nodep) override { void visit(AstCFunc* nodep) override {
VL_RESTORER(m_procp); VL_RESTORER(m_procp);
m_procp = nodep; m_procp = nodep;
iterateChildren(nodep); iterateChildren(nodep);
TimingDependencyVertex* const vxp = getDependencyVertex(nodep); if (nodep->needProcess()) nodep->user2(T_HAS_PROC | T_CALLS_PROC_SELF);
if (nodep->needProcess()) nodep->user2(T_PROC); DepVtx* const sVxp = getSuspendDepVtx(nodep);
DepVtx* const pVxp = getNeedsProcDepVtx(nodep);
if (!m_classp) return; if (!m_classp) return;
// If class method (possibly overrides another method)
if (!m_classp->user1SetOnce()) m_classp->repairCache();
// Go over overridden functions // Go over overridden functions
@@ -182,19 +260,15 @@ private:
// actually overridden by our method. If this causes a problem, traverse to // actually overridden by our method. If this causes a problem, traverse to
// the root of the inheritance hierarchy and check if the original method is // the root of the inheritance hierarchy and check if the original method is
// virtual or not. // virtual or not.
if (!cextp->classp()->user1SetOnce()) cextp->classp()->repairCache();
if (auto* const overriddenp if (auto* const overriddenp
= VN_CAST(cextp->classp()->findMember(nodep->name()), CFunc)) { = VN_CAST(memberMap.findMember(cextp->classp(), nodep->name()), CFunc)) {
setTimingFlag(nodep, overriddenp->user2()); // Suspendability and process affects typing, so they propagate both ways
if (nodep->user2() DepVtx* const overriddenSVxp = getSuspendDepVtx(overriddenp);
< T_PROC) { // Add a vertex only if the flag can still change DepVtx* const overriddenPVxp = getNeedsProcDepVtx(overriddenp);
// Make a dependency cycle, as being suspendable should propagate both up new V3GraphEdge{&m_suspGraph, sVxp, overriddenSVxp, P_SIGNATURE};
// and down the inheritance tree new V3GraphEdge{&m_suspGraph, overriddenSVxp, sVxp, P_SIGNATURE};
TimingDependencyVertex* const overriddenVxp new V3GraphEdge{&m_procGraph, pVxp, overriddenPVxp, P_SIGNATURE};
= getDependencyVertex(overriddenp); new V3GraphEdge{&m_procGraph, overriddenPVxp, pVxp, P_SIGNATURE};
new V3GraphEdge{&m_depGraph, vxp, overriddenVxp, 1};
new V3GraphEdge{&m_depGraph, overriddenVxp, vxp, 1};
}
} else { } else {
AstClassExtends* more_extends = cextp->classp()->extendsp(); AstClassExtends* more_extends = cextp->classp()->extendsp();
if (more_extends) extends.push(more_extends); if (more_extends) extends.push(more_extends);
@@ -203,30 +277,49 @@ private:
} }
} }
void visit(AstNodeCCall* nodep) override { void visit(AstNodeCCall* nodep) override {
setTimingFlag(m_procp, nodep->funcp()->user2()); if (!m_underFork || (m_underFork & F_MIGHT_SUSPEND))
if (m_procp->user2() < T_PROC) { // Add a vertex only if the flag can still change new V3GraphEdge{&m_suspGraph, getSuspendDepVtx(nodep->funcp()),
TimingDependencyVertex* const procVxp = getDependencyVertex(m_procp); getSuspendDepVtx(m_procp), m_underFork ? P_FORK : P_CALL};
TimingDependencyVertex* const funcVxp = getDependencyVertex(nodep->funcp());
new V3GraphEdge{&m_depGraph, procVxp, funcVxp, 1}; if (!m_underFork)
iterateChildren(nodep); new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(nodep->funcp()),
} getNeedsProcDepVtx(m_procp), P_CALL};
iterateChildren(nodep);
} }
void visit(AstBegin* nodep) override { void visit(AstBegin* nodep) override {
VL_RESTORER(m_procp); VL_RESTORER(m_procp);
VL_RESTORER(m_underFork);
if (!m_underFork || (m_underFork & F_MIGHT_SUSPEND))
new V3GraphEdge{&m_suspGraph, getSuspendDepVtx(nodep), getSuspendDepVtx(m_procp),
m_underFork ? P_FORK : P_CALL};
if (!m_underFork)
new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(nodep), getNeedsProcDepVtx(m_procp),
P_CALL};
m_procp = nodep; m_procp = nodep;
m_underFork = 0;
iterateChildren(nodep); iterateChildren(nodep);
} }
void visit(AstFork* nodep) override { void visit(AstFork* nodep) override {
VL_RESTORER(m_underFork);
v3Global.setUsesTiming(); // Even if there are no event controls, we have to set this flag v3Global.setUsesTiming(); // Even if there are no event controls, we have to set this flag
// so that transformForks() in V3SchedTiming gets called and // so that transformForks() in V3SchedTiming gets called and
// removes all forks and begins // removes all forks and begins
if (nodep->isTimingControl() && m_procp) m_procp->user2(T_SUSP); if (nodep->isTimingControl() && m_procp) {
m_procp->user2(T_SUSPENDEE | T_SUSPENDER);
m_underFork |= F_MIGHT_SUSPEND;
}
m_underFork |= F_MIGHT_NEED_PROC;
iterateChildren(nodep); iterateChildren(nodep);
} }
void visit(AstNode* nodep) override { void visit(AstNode* nodep) override {
if (nodep->isTimingControl()) { if (nodep->isTimingControl()) {
v3Global.setUsesTiming(); v3Global.setUsesTiming();
if (m_procp) m_procp->user2(T_SUSP); if (m_procp) m_procp->user2(T_SUSPENDEE | T_SUSPENDER);
} }
iterateChildren(nodep); iterateChildren(nodep);
} }
@@ -238,12 +331,29 @@ public:
// CONSTRUCTORS // CONSTRUCTORS
explicit TimingSuspendableVisitor(AstNetlist* nodep) { explicit TimingSuspendableVisitor(AstNetlist* nodep) {
iterate(nodep); iterate(nodep);
m_depGraph.removeTransitiveEdges(); m_suspGraph.removeTransitiveEdges();
for (V3GraphVertex* vxp = m_depGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { m_procGraph.removeTransitiveEdges();
TimingDependencyVertex* const depVxp = static_cast<TimingDependencyVertex*>(vxp); // Propagate suspendability
if (depVxp->nodep()->user2()) propagateTimingFlags(depVxp); for (V3GraphVertex* vxp = m_suspGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
DepVtx* const depVxp = static_cast<DepVtx*>(vxp);
if (depVxp->nodep()->user2() & T_SUSPENDEE) propagateFlags(depVxp, T_SUSPENDEE);
} }
if (dumpGraphLevel() >= 6) m_depGraph.dumpDotFilePrefixed("timing_deps"); if (dumpGraphLevel() >= 6) m_suspGraph.dumpDotFilePrefixed("timing_deps");
// Propagate process
for (V3GraphVertex* vxp = m_procGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
DepVtx* const depVxp = static_cast<DepVtx*>(vxp);
if (depVxp->nodep()->user2() & T_HAS_PROC) propagateFlags(depVxp, T_HAS_PROC);
}
// Propagate process downwards (from caller to callee) for suspendable calls
for (V3GraphVertex* vxp = m_suspGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
DepVtx* const depVxp = static_cast<DepVtx*>(vxp);
if (depVxp->nodep()->user2() & T_HAS_PROC)
propagateFlagsReversedIf(depVxp, T_HAS_PROC, [&](const V3GraphEdge* e) -> bool {
return (e->weight() != P_FORK)
&& (static_cast<DepVtx*>(e->top())->nodep()->user2() & T_SUSPENDEE);
});
}
if (dumpGraphLevel() >= 6) m_procGraph.dumpDotFilePrefixed("proc_deps");
} }
~TimingSuspendableVisitor() override = default; ~TimingSuspendableVisitor() override = default;
}; };
@@ -277,13 +387,13 @@ private:
AstActive* m_activep = nullptr; // Current active AstActive* m_activep = nullptr; // Current active
AstNode* m_procp = nullptr; // NodeProcedure/CFunc/Begin we're under AstNode* m_procp = nullptr; // NodeProcedure/CFunc/Begin we're under
double m_timescaleFactor = 1.0; // Factor to scale delays by double m_timescaleFactor = 1.0; // Factor to scale delays by
int m_forkCnt = 0; // Number of forks inside a module
// Unique names // Unique names
V3UniqueNames m_contAssignVarNames{"__VassignWtmp"}; // Names for temp AssignW vars V3UniqueNames m_contAssignVarNames{"__VassignWtmp"}; // Names for temp AssignW vars
V3UniqueNames m_intraValueNames{"__Vintraval"}; // Intra assign delay value var names V3UniqueNames m_intraValueNames{"__Vintraval"}; // Intra assign delay value var names
V3UniqueNames m_intraIndexNames{"__Vintraidx"}; // Intra assign delay index var names V3UniqueNames m_intraIndexNames{"__Vintraidx"}; // Intra assign delay index var names
V3UniqueNames m_intraLsbNames{"__Vintralsb"}; // Intra assign delay LSB var names V3UniqueNames m_intraLsbNames{"__Vintralsb"}; // Intra assign delay LSB var names
V3UniqueNames m_forkNames{"__Vfork"}; // Fork name generator
V3UniqueNames m_trigSchedNames{"__VtrigSched"}; // Trigger scheduler name generator V3UniqueNames m_trigSchedNames{"__VtrigSched"}; // Trigger scheduler name generator
V3UniqueNames m_dynTrigNames{"__VdynTrigger"}; // Dynamic trigger name generator V3UniqueNames m_dynTrigNames{"__VdynTrigger"}; // Dynamic trigger name generator
@@ -299,6 +409,7 @@ private:
// Other // Other
SenTreeFinder m_finder{m_netlistp}; // Sentree finder and uniquifier SenTreeFinder m_finder{m_netlistp}; // Sentree finder and uniquifier
SenExprBuilder* m_senExprBuilderp = nullptr; // Sens expression builder for current m_scope
// METHODS // METHODS
// Find net delay on the LHS of an assignment // Find net delay on the LHS of an assignment
@@ -358,23 +469,6 @@ private:
int scalePowerOfTen = timeunit.powerOfTen() - m_netlistp->timeprecision().powerOfTen(); int scalePowerOfTen = timeunit.powerOfTen() - m_netlistp->timeprecision().powerOfTen();
return std::pow(10.0, scalePowerOfTen); return std::pow(10.0, scalePowerOfTen);
} }
// Construct SenItems from VarRefs in an expression
AstSenItem* varRefpsToSenItemsp(AstNode* const nodep) const {
AstNode* senItemsp = nullptr;
const VNUser4InUse user4InUse;
nodep->foreach([&](AstNodeVarRef* refp) {
if (refp->access().isWriteOnly()) return;
AstVarScope* const vscp = refp->varScopep();
if (vscp->user4SetOnce()) return;
const bool isEvent = vscp->dtypep() && vscp->dtypep()->basicp()
&& vscp->dtypep()->basicp()->isEvent();
const auto edgeType = isEvent ? VEdgeType::ET_EVENT : VEdgeType::ET_CHANGED;
senItemsp = AstNode::addNext(
senItemsp, new AstSenItem{refp->fileline(), edgeType,
new AstVarRef{refp->fileline(), vscp, VAccess::READ}});
});
return VN_AS(senItemsp, SenItem);
}
// Creates the global delay scheduler variable // Creates the global delay scheduler variable
AstVarScope* getCreateDelayScheduler() { AstVarScope* getCreateDelayScheduler() {
if (m_delaySchedp) return m_delaySchedp; if (m_delaySchedp) return m_delaySchedp;
@@ -488,7 +582,7 @@ private:
void addProcessInfo(AstCMethodHard* const methodp) const { void addProcessInfo(AstCMethodHard* const methodp) const {
FileLine* const flp = methodp->fileline(); FileLine* const flp = methodp->fileline();
AstCExpr* const ap = new AstCExpr{ AstCExpr* const ap = new AstCExpr{
flp, m_procp && m_procp->user2() == T_PROC ? "vlProcess" : "nullptr", 0}; flp, m_procp && (m_procp->user2() & T_HAS_PROC) ? "vlProcess" : "nullptr", 0};
ap->dtypeSetVoid(); ap->dtypeSetVoid();
methodp->addPinsp(ap); methodp->addPinsp(ap);
} }
@@ -564,12 +658,19 @@ private:
m_classp = VN_CAST(nodep, Class); m_classp = VN_CAST(nodep, Class);
VL_RESTORER(m_timescaleFactor); VL_RESTORER(m_timescaleFactor);
m_timescaleFactor = calculateTimescaleFactor(nodep->timeunit()); m_timescaleFactor = calculateTimescaleFactor(nodep->timeunit());
VL_RESTORER(m_forkCnt);
m_forkCnt = 0;
iterateChildren(nodep); iterateChildren(nodep);
} }
void visit(AstScope* nodep) override { void visit(AstScope* nodep) override {
VL_RESTORER(m_scopep); VL_RESTORER(m_scopep);
m_scopep = nodep; m_scopep = nodep;
iterateChildren(nodep); SenExprBuilder senExprBuilder{m_scopep};
{ // Restore m_senExprBuilderp before destroying senExprBuilder
VL_RESTORER(m_senExprBuilderp);
m_senExprBuilderp = &senExprBuilder;
iterateChildren(nodep);
}
} }
void visit(AstActive* nodep) override { void visit(AstActive* nodep) override {
m_activep = nodep; m_activep = nodep;
@@ -580,8 +681,8 @@ private:
VL_RESTORER(m_procp); VL_RESTORER(m_procp);
m_procp = nodep; m_procp = nodep;
iterateChildren(nodep); iterateChildren(nodep);
if (nodep->user2() >= T_SUSP) nodep->setSuspendable(); if (nodep->user2() & T_SUSPENDEE) nodep->setSuspendable();
if (nodep->user2() >= T_PROC) nodep->setNeedProcess(); if (nodep->user2() & T_HAS_PROC) nodep->setNeedProcess();
} }
void visit(AstInitial* nodep) override { void visit(AstInitial* nodep) override {
visit(static_cast<AstNodeProcedure*>(nodep)); visit(static_cast<AstNodeProcedure*>(nodep));
@@ -592,9 +693,16 @@ private:
} }
void visit(AstAlways* nodep) override { void visit(AstAlways* nodep) override {
if (nodep->user1SetOnce()) return; if (nodep->user1SetOnce()) return;
VL_RESTORER(m_procp);
m_procp = nodep;
// Workaround for killing `always` processes (doing that is pretty much UB)
// TODO: Disallow killing `always` at runtime (throw an error)
if (nodep->user2() & T_HAS_PROC) nodep->user2(nodep->user2() | T_SUSPENDEE);
iterateChildren(nodep); iterateChildren(nodep);
if (!nodep->user2()) return; if (nodep->user2() & T_HAS_PROC) nodep->setNeedProcess();
if (nodep->user2() == T_PROC) nodep->setNeedProcess(); if (!(nodep->user2() & T_SUSPENDEE)) return;
nodep->setSuspendable(); nodep->setSuspendable();
FileLine* const flp = nodep->fileline(); FileLine* const flp = nodep->fileline();
AstSenTree* const sensesp = m_activep->sensesp(); AstSenTree* const sensesp = m_activep->sensesp();
@@ -615,7 +723,9 @@ private:
VL_RESTORER(m_procp); VL_RESTORER(m_procp);
m_procp = nodep; m_procp = nodep;
iterateChildren(nodep); iterateChildren(nodep);
if (!nodep->user2()) return; if (nodep->user2() & T_HAS_PROC) nodep->setNeedProcess();
if (!(nodep->user2() & T_SUSPENDEE)) return;
nodep->rtnType("VlCoroutine"); nodep->rtnType("VlCoroutine");
// If in a class, create a shared pointer to 'this' // If in a class, create a shared pointer to 'this'
if (m_classp) nodep->addInitsp(new AstCStmt{nodep->fileline(), "VL_KEEP_THIS;\n"}); if (m_classp) nodep->addInitsp(new AstCStmt{nodep->fileline(), "VL_KEEP_THIS;\n"});
@@ -635,10 +745,9 @@ private:
firstCoStmtp->v3warn(E_UNSUPPORTED, firstCoStmtp->v3warn(E_UNSUPPORTED,
"Unsupported: Timing controls inside DPI-exported tasks"); "Unsupported: Timing controls inside DPI-exported tasks");
} }
if (nodep->user2() == T_PROC) nodep->setNeedProcess();
} }
void visit(AstNodeCCall* nodep) override { void visit(AstNodeCCall* nodep) override {
if (nodep->funcp()->user2() && !nodep->user1SetOnce()) { // If suspendable if ((nodep->funcp()->user2() & T_SUSPENDEE) && !nodep->user1SetOnce()) { // If suspendable
VNRelinker relinker; VNRelinker relinker;
nodep->unlinkFrBack(&relinker); nodep->unlinkFrBack(&relinker);
AstCAwait* const awaitp = new AstCAwait{nodep->fileline(), nodep}; AstCAwait* const awaitp = new AstCAwait{nodep->fileline(), nodep};
@@ -723,14 +832,14 @@ private:
= new AstCAwait{flp, evalMethodp, getCreateDynamicTriggerSenTree()}; = new AstCAwait{flp, evalMethodp, getCreateDynamicTriggerSenTree()};
awaitEvalp->dtypeSetVoid(); awaitEvalp->dtypeSetVoid();
// Construct the sen expression for this sentree // Construct the sen expression for this sentree
SenExprBuilder senExprBuilder{m_scopep}; UASSERT_OBJ(m_senExprBuilderp, nodep, "No SenExprBuilder for this scope");
auto* const assignp = new AstAssign{flp, new AstVarRef{flp, trigvscp, VAccess::WRITE}, auto* const assignp = new AstAssign{flp, new AstVarRef{flp, trigvscp, VAccess::WRITE},
senExprBuilder.build(sensesp).first}; m_senExprBuilderp->build(sensesp).first};
// Put all the locals and inits before the trigger eval loop // Put all the locals and inits before the trigger eval loop
for (AstVar* const varp : senExprBuilder.getAndClearLocals()) { for (AstVar* const varp : m_senExprBuilderp->getAndClearLocals()) {
nodep->addHereThisAsNext(varp); nodep->addHereThisAsNext(varp);
} }
for (AstNodeStmt* const stmtp : senExprBuilder.getAndClearInits()) { for (AstNodeStmt* const stmtp : m_senExprBuilderp->getAndClearInits()) {
nodep->addHereThisAsNext(stmtp); nodep->addHereThisAsNext(stmtp);
} }
// Create the trigger eval loop, which will await the evaluation step and check the // Create the trigger eval loop, which will await the evaluation step and check the
@@ -739,7 +848,7 @@ private:
flp, new AstLogNot{flp, new AstVarRef{flp, trigvscp, VAccess::READ}}, flp, new AstLogNot{flp, new AstVarRef{flp, trigvscp, VAccess::READ}},
awaitEvalp->makeStmt()}; awaitEvalp->makeStmt()};
// Put pre updates before the trigger check and assignment // Put pre updates before the trigger check and assignment
for (AstNodeStmt* const stmtp : senExprBuilder.getAndClearPreUpdates()) { for (AstNodeStmt* const stmtp : m_senExprBuilderp->getAndClearPreUpdates()) {
loopp->addStmtsp(stmtp); loopp->addStmtsp(stmtp);
} }
// Then the trigger check and assignment // Then the trigger check and assignment
@@ -752,7 +861,7 @@ private:
loopp->addStmtsp(awaitPostUpdatep->makeStmt()); loopp->addStmtsp(awaitPostUpdatep->makeStmt());
} }
// Put the post updates at the end of the loop // Put the post updates at the end of the loop
for (AstNodeStmt* const stmtp : senExprBuilder.getAndClearPostUpdates()) { for (AstNodeStmt* const stmtp : m_senExprBuilderp->getAndClearPostUpdates()) {
loopp->addStmtsp(stmtp); loopp->addStmtsp(stmtp);
} }
// Finally, await the resumption step in 'act' // Finally, await the resumption step in 'act'
@@ -897,11 +1006,14 @@ private:
if (stmtsp) AstNode::addNext<AstNode, AstNode>(ifp, stmtsp); if (stmtsp) AstNode::addNext<AstNode, AstNode>(ifp, stmtsp);
nodep->replaceWith(ifp); nodep->replaceWith(ifp);
} else { } else {
AstSenItem* const senItemsp = varRefpsToSenItemsp(condp); // We are using a global sentree, so we cannot use ET_TRUE, as that could lead to the
UASSERT_OBJ(senItemsp, nodep, "No varrefs in wait statement condition"); // active region never converging. Because of this, we need to use ET_CHANGED in a
// Put the event control in a while loop with the wait expression as condition // loop.
AstEventControl* const controlp AstEventControl* const controlp = new AstEventControl{
= new AstEventControl{flp, new AstSenTree{flp, senItemsp}, nullptr}; flp,
new AstSenTree{
flp, new AstSenItem{flp, VEdgeType::ET_CHANGED, condp->cloneTree(false)}},
nullptr};
controlp->user2(true); // Commit immediately controlp->user2(true); // Commit immediately
AstWhile* const loopp = new AstWhile{flp, new AstLogNot{flp, condp}, controlp}; AstWhile* const loopp = new AstWhile{flp, new AstLogNot{flp, condp}, controlp};
if (stmtsp) AstNode::addNext<AstNode, AstNode>(loopp, stmtsp); if (stmtsp) AstNode::addNext<AstNode, AstNode>(loopp, stmtsp);
@@ -918,7 +1030,7 @@ private:
if (nodep->user1SetOnce()) return; if (nodep->user1SetOnce()) return;
v3Global.setUsesTiming(); v3Global.setUsesTiming();
// Create a unique name for this fork // Create a unique name for this fork
nodep->name(m_forkNames.get(nodep)); nodep->name("__Vfork_" + cvtToStr(++m_forkCnt));
unsigned idx = 0; // Index for naming begins unsigned idx = 0; // Index for naming begins
AstNode* stmtp = nodep->stmtsp(); AstNode* stmtp = nodep->stmtsp();
// Put each statement in a begin // Put each statement in a begin
+2 -3
View File
@@ -154,7 +154,7 @@ public:
// ACCESSORS // ACCESSORS
AstNode* nodep() const VL_MT_STABLE { return m_nodep; } AstNode* nodep() const VL_MT_STABLE { return m_nodep; }
const AstVar* varp() const { return VN_CAST(nodep(), Var); } const AstVar* varp() const { return VN_CAST(nodep(), Var); }
string name() const override VL_MT_STABLE { string name() const override {
return ((isTristate() ? "tri\\n" return ((isTristate() ? "tri\\n"
: feedsTri() ? "feed\\n" : feedsTri() ? "feed\\n"
: "-\\n") : "-\\n")
@@ -672,8 +672,7 @@ class TristateVisitor final : public TristateBaseVisitor {
VFlagBitPacked{}, w}; // 2-state ok; sep enable VFlagBitPacked{}, w}; // 2-state ok; sep enable
UINFO(9, " newout " << newLhsp << endl); UINFO(9, " newout " << newLhsp << endl);
nodep->addStmtsp(newLhsp); nodep->addStmtsp(newLhsp);
refp->varp(newLhsp); // assign the new var to the varref refp->varp(newLhsp);
refp->name(newLhsp->name());
// create a new var for this drivers enable signal // create a new var for this drivers enable signal
AstVar* const newEnLhsp = new AstVar{varp->fileline(), VVarType::MODULETEMP, AstVar* const newEnLhsp = new AstVar{varp->fileline(), VVarType::MODULETEMP,
+119 -100
View File
@@ -70,6 +70,7 @@
#include "V3Const.h" #include "V3Const.h"
#include "V3Global.h" #include "V3Global.h"
#include "V3MemberMap.h"
#include "V3Number.h" #include "V3Number.h"
#include "V3Randomize.h" #include "V3Randomize.h"
#include "V3String.h" #include "V3String.h"
@@ -223,8 +224,8 @@ private:
using DTypeMap = std::map<const std::string, AstPatMember*>; using DTypeMap = std::map<const std::string, AstPatMember*>;
// STATE // STATE
VMemberMap memberMap; // Member names cached for fast lookup
WidthVP* m_vup = nullptr; // Current node state WidthVP* m_vup = nullptr; // Current node state
AstClass* m_classp = nullptr; // Current class
const AstCell* m_cellp = nullptr; // Current cell for arrayed instantiations const AstCell* m_cellp = nullptr; // Current cell for arrayed instantiations
const AstEnumItem* m_enumItemp = nullptr; // Current enum item const AstEnumItem* m_enumItemp = nullptr; // Current enum item
const AstNodeFTask* m_ftaskp = nullptr; // Current function/task const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
@@ -232,7 +233,6 @@ private:
const AstWith* m_withp = nullptr; // Current 'with' statement const AstWith* m_withp = nullptr; // Current 'with' statement
const AstFunc* m_funcp = nullptr; // Current function const AstFunc* m_funcp = nullptr; // Current function
const AstAttrOf* m_attrp = nullptr; // Current attribute const AstAttrOf* m_attrp = nullptr; // Current attribute
AstPackage* m_pkgp = nullptr; // Current package
const bool m_paramsOnly; // Computing parameter value; limit operation const bool m_paramsOnly; // Computing parameter value; limit operation
const bool m_doGenerate; // Do errors later inside generate statement const bool m_doGenerate; // Do errors later inside generate statement
int m_dtTables = 0; // Number of created data type tables int m_dtTables = 0; // Number of created data type tables
@@ -615,9 +615,11 @@ private:
} }
if (m_vup->final()) { if (m_vup->final()) {
if (nodep->lhsp()->isString() || nodep->rhsp()->isString()) { if (nodep->lhsp()->isString() || nodep->rhsp()->isString()) {
AstNode* const newp AstNodeExpr* lhsp = nodep->lhsp()->unlinkFrBack();
= new AstConcatN{nodep->fileline(), nodep->lhsp()->unlinkFrBack(), AstNodeExpr* rhsp = nodep->rhsp()->unlinkFrBack();
nodep->rhsp()->unlinkFrBack()}; if (!lhsp->isString()) lhsp = new AstCvtPackString{lhsp->fileline(), lhsp};
if (!rhsp->isString()) rhsp = new AstCvtPackString{rhsp->fileline(), rhsp};
AstNode* const newp = new AstConcatN{nodep->fileline(), lhsp, rhsp};
nodep->replaceWith(newp); nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep); VL_DO_DANGLING(pushDeletep(nodep), nodep);
return; return;
@@ -729,19 +731,12 @@ private:
// width: value(LHS) * width(RHS) // width: value(LHS) * width(RHS)
if (m_vup->prelim()) { if (m_vup->prelim()) {
iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH); iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
V3Const::constifyParamsEdit(nodep->rhsp()); // rhsp may change V3Const::constifyParamsNoWarnEdit(nodep->rhsp()); // rhsp may change
uint32_t times = 1;
const AstConst* const constp = VN_CAST(nodep->rhsp(), Const); const AstConst* const constp = VN_CAST(nodep->rhsp(), Const);
if (!constp) { if (constp) times = constp->toUInt();
nodep->v3error("Replication value isn't a constant.");
return;
}
uint32_t times = constp->toUInt();
if (times == 0
&& !VN_IS(nodep->backp(), Concat)) { // Concat Visitor will clean it up.
nodep->v3error("Replication value of 0 is only legal under a concatenation"
" (IEEE 1800-2017 11.4.12.1)");
times = 1;
}
AstNodeDType* const vdtypep = m_vup->dtypeNullSkipRefp(); AstNodeDType* const vdtypep = m_vup->dtypeNullSkipRefp();
if (VN_IS(vdtypep, QueueDType) || VN_IS(vdtypep, DynArrayDType) if (VN_IS(vdtypep, QueueDType) || VN_IS(vdtypep, DynArrayDType)
@@ -775,7 +770,7 @@ private:
<< vdtypep->prettyDTypeNameQ() << " data type"); << vdtypep->prettyDTypeNameQ() << " data type");
} }
iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH); iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
if (nodep->lhsp()->isString()) { if ((vdtypep && vdtypep->isString()) || nodep->lhsp()->isString()) {
AstNode* const newp AstNode* const newp
= new AstReplicateN{nodep->fileline(), nodep->lhsp()->unlinkFrBack(), = new AstReplicateN{nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
nodep->rhsp()->unlinkFrBack()}; nodep->rhsp()->unlinkFrBack()};
@@ -783,6 +778,13 @@ private:
VL_DO_DANGLING(pushDeletep(nodep), nodep); VL_DO_DANGLING(pushDeletep(nodep), nodep);
return; return;
} else { } else {
if (!constp) nodep->v3error("Replication value isn't a constant.");
if (times == 0
&& !VN_IS(nodep->backp(), Concat)) { // Concat Visitor will clean it up.
nodep->v3error("Replication value of 0 is only legal under a concatenation"
" (IEEE 1800-2017 11.4.12.1)");
times = 1; // Set to 1, so we can continue looking for errors
}
nodep->dtypeSetLogicUnsized((nodep->lhsp()->width() * times), nodep->dtypeSetLogicUnsized((nodep->lhsp()->width() * times),
(nodep->lhsp()->widthMin() * times), (nodep->lhsp()->widthMin() * times),
VSigning::UNSIGNED); VSigning::UNSIGNED);
@@ -801,18 +803,7 @@ private:
if (m_vup->prelim()) { if (m_vup->prelim()) {
iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH); iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
V3Const::constifyParamsEdit(nodep->rhsp()); // rhsp may change V3Const::constifyParamsNoWarnEdit(nodep->rhsp()); // rhsp may change
const AstConst* const constp = VN_CAST(nodep->rhsp(), Const);
if (!constp) {
nodep->v3error("Replication value isn't a constant.");
return;
}
const uint32_t times = constp->toUInt();
if (times == 0
&& !VN_IS(nodep->backp(), Concat)) { // Concat Visitor will clean it up.
nodep->v3error("Replication value of 0 is only legal under a concatenation"
" (IEEE 1800-2017 11.4.12.1)");
}
nodep->dtypeSetString(); nodep->dtypeSetString();
} }
if (m_vup->final()) { if (m_vup->final()) {
@@ -848,8 +839,14 @@ private:
} else { } else {
nodep->v3error("Slice size isn't a constant or basic data type."); nodep->v3error("Slice size isn't a constant or basic data type.");
} }
nodep->dtypeSetLogicUnsized(nodep->lhsp()->width(), nodep->lhsp()->widthMin(), if (VN_IS(nodep->lhsp()->dtypep(), DynArrayDType)
VSigning::UNSIGNED); || VN_IS(nodep->lhsp()->dtypep(), QueueDType)
|| VN_IS(nodep->lhsp()->dtypep(), UnpackArrayDType)) {
nodep->dtypeSetStream();
} else {
nodep->dtypeSetLogicUnsized(nodep->lhsp()->width(), nodep->lhsp()->widthMin(),
VSigning::UNSIGNED);
}
} }
if (m_vup->final()) { if (m_vup->final()) {
if (!nodep->dtypep()->widthSized()) { if (!nodep->dtypep()->widthSized()) {
@@ -1550,7 +1547,7 @@ private:
nodep->v3warn(E_UNSUPPORTED, "Unsupported: $bits for queue"); nodep->v3warn(E_UNSUPPORTED, "Unsupported: $bits for queue");
break; break;
} }
default: nodep->v3error("Unhandled attribute type"); default: nodep->v3fatalSrc("Unhandled attribute type");
} }
} else { } else {
const std::pair<uint32_t, uint32_t> dimpair = dtypep->skipRefp()->dimensions(true); const std::pair<uint32_t, uint32_t> dimpair = dtypep->skipRefp()->dimensions(true);
@@ -2115,6 +2112,10 @@ private:
v3Global.rootp()->typeTablep()->addTypesp(newp); v3Global.rootp()->typeTablep()->addTypesp(newp);
} }
} }
if (AstWildcardArrayDType* const wildp
= VN_CAST(nodep->dtypeSkipRefp(), WildcardArrayDType)) {
nodep->dtypep(wildp); // Skip RefDType like for other dynamic array types
}
if (VN_IS(nodep->dtypep()->skipRefToConstp(), ConstDType)) nodep->isConst(true); if (VN_IS(nodep->dtypep()->skipRefToConstp(), ConstDType)) nodep->isConst(true);
// Parameters if implicit untyped inherit from what they are assigned to // Parameters if implicit untyped inherit from what they are assigned to
const AstBasicDType* const bdtypep = VN_CAST(nodep->dtypep(), BasicDType); const AstBasicDType* const bdtypep = VN_CAST(nodep->dtypep(), BasicDType);
@@ -2577,7 +2578,6 @@ private:
// if (debug() >= 9) nodep->dumpTree("- class-in: "); // if (debug() >= 9) nodep->dumpTree("- class-in: ");
if (!nodep->packed() && v3Global.opt.structsPacked()) nodep->packed(true); if (!nodep->packed() && v3Global.opt.structsPacked()) nodep->packed(true);
userIterateChildren(nodep, nullptr); // First size all members userIterateChildren(nodep, nullptr); // First size all members
nodep->repairMemberCache();
nodep->dtypep(nodep); nodep->dtypep(nodep);
nodep->isFourstate(false); nodep->isFourstate(false);
// Error checks // Error checks
@@ -2628,28 +2628,34 @@ private:
} }
void visit(AstClass* nodep) override { void visit(AstClass* nodep) override {
if (nodep->didWidthAndSet()) return; if (nodep->didWidthAndSet()) return;
// If the package is std::process, set m_process type to VlProcessRef
if (m_pkgp && m_pkgp->name() == "std" && nodep->name() == "process") { // If the class is std::process
if (AstVar* const varp = VN_CAST(nodep->findMember("m_process"), Var)) { if (nodep->name() == "process") {
AstBasicDType* const dtypep = new AstBasicDType{ AstPackage* const packagep = getItemPackage(nodep);
nodep->fileline(), VBasicDTypeKwd::PROCESS_REFERENCE, VSigning::UNSIGNED}; if (packagep && packagep->name() == "std") {
v3Global.rootp()->typeTablep()->addTypesp(dtypep); // Change type of m_process to VlProcessRef
varp->getChildDTypep()->unlinkFrBack(); if (AstVar* const varp = VN_CAST(memberMap.findMember(nodep, "m_process"), Var)) {
varp->dtypep(dtypep); AstNodeDType* const dtypep = varp->getChildDTypep();
if (!varp->dtypep()) {
VL_DO_DANGLING(pushDeletep(dtypep->unlinkFrBack()), dtypep);
}
AstBasicDType* const newdtypep = new AstBasicDType{
nodep->fileline(), VBasicDTypeKwd::PROCESS_REFERENCE, VSigning::UNSIGNED};
v3Global.rootp()->typeTablep()->addTypesp(newdtypep);
varp->dtypep(newdtypep);
}
// Mark that self requires process instance
if (AstNodeFTask* const ftaskp
= VN_CAST(memberMap.findMember(nodep, "self"), NodeFTask)) {
ftaskp->setNeedProcess();
}
} }
} }
// Must do extends first, as we may in functions under this class // Must do extends first, as we may in functions under this class
// start following a tree of extends that takes us to other classes // start following a tree of extends that takes us to other classes
VL_RESTORER(m_classp);
m_classp = nodep;
userIterateAndNext(nodep->extendsp(), nullptr); userIterateAndNext(nodep->extendsp(), nullptr);
userIterateChildren(nodep, nullptr); // First size all members userIterateChildren(nodep, nullptr); // First size all members
nodep->repairCache();
}
void visit(AstPackage* nodep) override {
VL_RESTORER(m_pkgp);
m_pkgp = nodep;
userIterateChildren(nodep, nullptr);
} }
void visit(AstThisRef* nodep) override { void visit(AstThisRef* nodep) override {
if (nodep->didWidthAndSet()) return; if (nodep->didWidthAndSet()) return;
@@ -2704,31 +2710,7 @@ private:
if (AstNodeUOrStructDType* const adtypep = VN_CAST(fromDtp, NodeUOrStructDType)) { if (AstNodeUOrStructDType* const adtypep = VN_CAST(fromDtp, NodeUOrStructDType)) {
if (memberSelStruct(nodep, adtypep)) return; if (memberSelStruct(nodep, adtypep)) return;
} else if (AstClassRefDType* const adtypep = VN_CAST(fromDtp, ClassRefDType)) { } else if (AstClassRefDType* const adtypep = VN_CAST(fromDtp, ClassRefDType)) {
if (AstNode* const foundp = memberSelClass(nodep, adtypep)) { if (memberSelClass(nodep, adtypep)) return;
if (AstVar* const varp = VN_CAST(foundp, Var)) {
if (!varp->didWidth()) userIterate(varp, nullptr);
nodep->dtypep(foundp->dtypep());
nodep->varp(varp);
return;
}
if (AstEnumItemRef* const adfoundp = VN_CAST(foundp, EnumItemRef)) {
nodep->replaceWith(adfoundp->cloneTree(false));
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
if (AstNodeFTask* const methodp = VN_CAST(foundp, NodeFTask)) {
nodep->replaceWith(new AstMethodCall{nodep->fileline(),
nodep->fromp()->unlinkFrBack(),
nodep->name(), nullptr});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
UINFO(1, "found object " << foundp << endl);
nodep->v3fatalSrc("MemberSel of non-variable\n"
<< nodep->warnContextPrimary() << '\n'
<< foundp->warnOther() << "... Location of found object\n"
<< foundp->warnContextSecondary());
}
} else if (AstIfaceRefDType* const adtypep = VN_CAST(fromDtp, IfaceRefDType)) { } else if (AstIfaceRefDType* const adtypep = VN_CAST(fromDtp, IfaceRefDType)) {
if (AstNode* const foundp = memberSelIface(nodep, adtypep)) { if (AstNode* const foundp = memberSelIface(nodep, adtypep)) {
if (AstVar* const varp = VN_CAST(foundp, Var)) { if (AstVar* const varp = VN_CAST(foundp, Var)) {
@@ -2767,15 +2749,50 @@ private:
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}}); nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
VL_DO_DANGLING(pushDeletep(nodep), nodep); VL_DO_DANGLING(pushDeletep(nodep), nodep);
} }
AstNode* memberSelClass(AstMemberSel* nodep, AstClassRefDType* adtypep) { bool memberSelClass(AstMemberSel* nodep, AstClassRefDType* adtypep) {
// Returns node if ok // Returns true if ok
// No need to width-resolve the class, as it was done when we did the child // No need to width-resolve the class, as it was done when we did the child
AstClass* const first_classp = adtypep->classp(); AstClass* const first_classp = adtypep->classp();
UASSERT_OBJ(first_classp, nodep, "Unlinked"); UASSERT_OBJ(first_classp, nodep, "Unlinked");
for (AstClass* classp = first_classp; classp;) { for (AstClass* classp = first_classp; classp;) {
if (AstNode* const foundp = classp->findMember(nodep->name())) return foundp; if (AstNode* const foundp = memberMap.findMember(classp, nodep->name())) {
if (AstVar* const varp = VN_CAST(foundp, Var)) {
if (!varp->didWidth()) userIterate(varp, nullptr);
if (varp->lifetime().isStatic()) {
// Static fiels are moved outside the class, so they shouldn't be accessed
// by member select on a class object
AstVarRef* const varRefp
= new AstVarRef{nodep->fileline(), varp, nodep->access()};
varRefp->classOrPackagep(classp);
nodep->replaceWith(varRefp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return true;
}
nodep->dtypep(foundp->dtypep());
nodep->varp(varp);
return true;
}
if (AstEnumItemRef* const adfoundp = VN_CAST(foundp, EnumItemRef)) {
nodep->replaceWith(adfoundp->cloneTree(false));
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return true;
}
if (AstNodeFTask* const methodp = VN_CAST(foundp, NodeFTask)) {
nodep->replaceWith(new AstMethodCall{nodep->fileline(),
nodep->fromp()->unlinkFrBack(),
nodep->name(), nullptr});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return true;
}
UINFO(1, "found object " << foundp << endl);
nodep->v3fatalSrc("MemberSel of non-variable\n"
<< nodep->warnContextPrimary() << '\n'
<< foundp->warnOther() << "... Location of found object\n"
<< foundp->warnContextSecondary());
}
classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr; classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr;
} }
VSpellCheck speller; VSpellCheck speller;
for (AstClass* classp = first_classp; classp;) { for (AstClass* classp = first_classp; classp;) {
for (AstNode* itemp = classp->membersp(); itemp; itemp = itemp->nextp()) { for (AstNode* itemp = classp->membersp(); itemp; itemp = itemp->nextp()) {
@@ -2790,14 +2807,13 @@ private:
"Member " << nodep->prettyNameQ() << " not found in class " "Member " << nodep->prettyNameQ() << " not found in class "
<< first_classp->prettyNameQ() << "\n" << first_classp->prettyNameQ() << "\n"
<< (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest)); << (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest));
return nullptr; // Caller handles error return false; // Caller handles error
} }
AstNode* memberSelIface(AstMemberSel* nodep, AstIfaceRefDType* adtypep) { AstNode* memberSelIface(AstMemberSel* nodep, AstIfaceRefDType* adtypep) {
// Returns node if ok // Returns node if ok
// No need to width-resolve the interface, as it was done when we did the child // No need to width-resolve the interface, as it was done when we did the child
AstNodeModule* const ifacep = adtypep->ifacep(); AstNodeModule* const ifacep = adtypep->ifacep();
UASSERT_OBJ(ifacep, nodep, "Unlinked"); UASSERT_OBJ(ifacep, nodep, "Unlinked");
// if (AstNode* const foundp = ifacep->findMember(nodep->name())) return foundp;
VSpellCheck speller; VSpellCheck speller;
for (AstNode* itemp = ifacep->stmtsp(); itemp; itemp = itemp->nextp()) { for (AstNode* itemp = ifacep->stmtsp(); itemp; itemp = itemp->nextp()) {
if (itemp->name() == nodep->name()) return itemp; if (itemp->name() == nodep->name()) return itemp;
@@ -2814,7 +2830,8 @@ private:
} }
bool memberSelStruct(AstMemberSel* nodep, AstNodeUOrStructDType* adtypep) { bool memberSelStruct(AstMemberSel* nodep, AstNodeUOrStructDType* adtypep) {
// Returns true if ok // Returns true if ok
if (AstMemberDType* const memberp = adtypep->findMember(nodep->name())) { if (AstMemberDType* const memberp
= VN_CAST(memberMap.findMember(adtypep, nodep->name()), MemberDType)) {
if (m_attrp) { // Looking for the base of the attribute if (m_attrp) { // Looking for the base of the attribute
nodep->dtypep(memberp); nodep->dtypep(memberp);
UINFO(9, " MEMBERSEL(attr) -> " << nodep << endl); UINFO(9, " MEMBERSEL(attr) -> " << nodep << endl);
@@ -3202,10 +3219,12 @@ private:
if (!nodep->firstAbovep()) newp->dtypeSetVoid(); if (!nodep->firstAbovep()) newp->dtypeSetVoid();
} else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique" } else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique"
|| nodep->name() == "unique_index") { || nodep->name() == "unique_index") {
AstWith* const withp = methodWithArgument(
nodep, false, true, nullptr, nodep->findUInt32DType(), adtypep->subDTypep());
methodOkArguments(nodep, 0, 0); methodOkArguments(nodep, 0, 0);
methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ); methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
nodep->name()}; nodep->name(), withp};
if (nodep->name() == "unique_index") { if (nodep->name() == "unique_index") {
newp->dtypep(queueDTypeIndexedBy(adtypep->keyDTypep())); newp->dtypep(queueDTypeIndexedBy(adtypep->keyDTypep()));
} else { } else {
@@ -3477,8 +3496,10 @@ private:
AstClass* const first_classp = adtypep->classp(); AstClass* const first_classp = adtypep->classp();
if (nodep->name() == "randomize") { if (nodep->name() == "randomize") {
V3Randomize::newRandomizeFunc(first_classp); V3Randomize::newRandomizeFunc(first_classp);
memberMap.clear();
} else if (nodep->name() == "srandom") { } else if (nodep->name() == "srandom") {
V3Randomize::newSRandomFunc(first_classp); V3Randomize::newSRandomFunc(first_classp);
memberMap.clear();
} }
UASSERT_OBJ(first_classp, nodep, "Unlinked"); UASSERT_OBJ(first_classp, nodep, "Unlinked");
for (AstClass* classp = first_classp; classp;) { for (AstClass* classp = first_classp; classp;) {
@@ -3501,7 +3522,7 @@ private:
} }
} }
if (AstNodeFTask* const ftaskp if (AstNodeFTask* const ftaskp
= VN_CAST(classp->findMember(nodep->name()), NodeFTask)) { = VN_CAST(memberMap.findMember(classp, nodep->name()), NodeFTask)) {
userIterate(ftaskp, nullptr); userIterate(ftaskp, nullptr);
if (ftaskp->lifetime().isStatic()) { if (ftaskp->lifetime().isStatic()) {
AstNodeExpr* argsp = nullptr; AstNodeExpr* argsp = nullptr;
@@ -3771,7 +3792,7 @@ private:
classp = refp->classp(); classp = refp->classp();
UASSERT_OBJ(classp, nodep, "Unlinked"); UASSERT_OBJ(classp, nodep, "Unlinked");
if (AstNodeFTask* const ftaskp = VN_CAST(classp->findMember("new"), Func)) { if (AstNodeFTask* const ftaskp = VN_CAST(memberMap.findMember(classp, "new"), Func)) {
nodep->taskp(ftaskp); nodep->taskp(ftaskp);
nodep->classOrPackagep(classp); nodep->classOrPackagep(classp);
} else { } else {
@@ -3939,7 +3960,8 @@ private:
// '{member:value} or '{data_type: default_value} // '{member:value} or '{data_type: default_value}
if (const AstText* textp = VN_CAST(patp->keyp(), Text)) { if (const AstText* textp = VN_CAST(patp->keyp(), Text)) {
// member: value // member: value
memp = vdtypep->findMember(textp->text()); memp = VN_CAST(memberMap.findMember(vdtypep, textp->text()),
MemberDType);
if (!memp) { if (!memp) {
patp->keyp()->v3error("Assignment pattern key '" patp->keyp()->v3error("Assignment pattern key '"
<< textp->text() << "' not found as member"); << textp->text() << "' not found as member");
@@ -5298,7 +5320,6 @@ private:
nodep->didWidth(true); nodep->didWidth(true);
return; return;
} }
if (m_pkgp && m_pkgp->name() == "std") nodep->isFromStd(true);
if (nodep->classMethod() && nodep->name() == "rand_mode") { if (nodep->classMethod() && nodep->name() == "rand_mode") {
nodep->v3error("The 'rand_mode' method is built-in and cannot be overridden" nodep->v3error("The 'rand_mode' method is built-in and cannot be overridden"
" (IEEE 1800-2017 18.8)"); " (IEEE 1800-2017 18.8)");
@@ -5575,20 +5596,17 @@ private:
|| (!nodep->taskp() || (!nodep->taskp()
&& (nodep->name() == "get_randstate" || nodep->name() == "set_randstate"))) { && (nodep->name() == "get_randstate" || nodep->name() == "set_randstate"))) {
// TODO perhaps this should move to V3LinkDot // TODO perhaps this should move to V3LinkDot
if (!m_classp) { AstClass* const classp = VN_CAST(nodep->classOrPackagep(), Class);
nodep->v3error("Calling implicit class method " << nodep->prettyNameQ() UASSERT_OBJ(classp, nodep, "Should have failed in V3LinkDot");
<< " without being under class");
nodep->replaceWith(new AstConst{nodep->fileline(), 0});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
if (nodep->name() == "randomize") { if (nodep->name() == "randomize") {
nodep->taskp(V3Randomize::newRandomizeFunc(m_classp)); nodep->taskp(V3Randomize::newRandomizeFunc(classp));
memberMap.clear();
} else if (nodep->name() == "srandom") { } else if (nodep->name() == "srandom") {
nodep->taskp(V3Randomize::newSRandomFunc(m_classp)); nodep->taskp(V3Randomize::newSRandomFunc(classp));
memberMap.clear();
} else if (nodep->name() == "get_randstate") { } else if (nodep->name() == "get_randstate") {
methodOkArguments(nodep, 0, 0); methodOkArguments(nodep, 0, 0);
m_classp->baseMostClassp()->needRNG(true); classp->baseMostClassp()->needRNG(true);
v3Global.useRandomizeMethods(true); v3Global.useRandomizeMethods(true);
AstCExpr* const newp AstCExpr* const newp
= new AstCExpr{nodep->fileline(), "__Vm_rng.get_randstate()", 1, true}; = new AstCExpr{nodep->fileline(), "__Vm_rng.get_randstate()", 1, true};
@@ -5601,7 +5619,7 @@ private:
AstNodeExpr* const expr1p = VN_AS(nodep->pinsp(), Arg)->exprp(); // May edit AstNodeExpr* const expr1p = VN_AS(nodep->pinsp(), Arg)->exprp(); // May edit
iterateCheckString(nodep, "LHS", expr1p, BOTH); iterateCheckString(nodep, "LHS", expr1p, BOTH);
AstNodeExpr* const exprp = VN_AS(nodep->pinsp(), Arg)->exprp(); AstNodeExpr* const exprp = VN_AS(nodep->pinsp(), Arg)->exprp();
m_classp->baseMostClassp()->needRNG(true); classp->baseMostClassp()->needRNG(true);
v3Global.useRandomizeMethods(true); v3Global.useRandomizeMethods(true);
AstCExpr* const newp AstCExpr* const newp
= new AstCExpr{nodep->fileline(), "__Vm_rng.set_randstate(", 1, true}; = new AstCExpr{nodep->fileline(), "__Vm_rng.set_randstate(", 1, true};
@@ -5671,7 +5689,8 @@ private:
} }
if (nodep->fileline()->timingOn()) { if (nodep->fileline()->timingOn()) {
if (v3Global.opt.timing().isSetTrue()) { if (v3Global.opt.timing().isSetTrue()) {
userIterate(nodep->condp(), WidthVP{SELF, PRELIM}.p()); iterateCheckBool(nodep, "Wait", nodep->condp(),
BOTH); // it's like an if() condition.
iterateNull(nodep->stmtsp()); iterateNull(nodep->stmtsp());
return; return;
} else if (v3Global.opt.timing().isSetFalse()) { } else if (v3Global.opt.timing().isSetFalse()) {
@@ -6371,7 +6390,7 @@ private:
} }
void checkClassAssign(AstNode* nodep, const char* side, AstNode* rhsp, void checkClassAssign(AstNode* nodep, const char* side, AstNode* rhsp,
AstNodeDType* lhsDTypep) { AstNodeDType* lhsDTypep) {
if (AstClassRefDType* const lhsClassRefp = VN_CAST(lhsDTypep, ClassRefDType)) { if (AstClassRefDType* const lhsClassRefp = VN_CAST(lhsDTypep->skipRefp(), ClassRefDType)) {
UASSERT_OBJ(rhsp->dtypep(), rhsp, "Node has no type"); UASSERT_OBJ(rhsp->dtypep(), rhsp, "Node has no type");
AstNodeDType* const rhsDtypep = rhsp->dtypep()->skipRefp(); AstNodeDType* const rhsDtypep = rhsp->dtypep()->skipRefp();
if (AstClassRefDType* const rhsClassRefp = VN_CAST(rhsDtypep, ClassRefDType)) { if (AstClassRefDType* const rhsClassRefp = VN_CAST(rhsDtypep, ClassRefDType)) {
@@ -6379,7 +6398,7 @@ private:
} else if (auto* const constp = VN_CAST(rhsp, Const)) { } else if (auto* const constp = VN_CAST(rhsp, Const)) {
if (constp->num().isNull()) return; if (constp->num().isNull()) return;
} }
nodep->v3error(side << " expects a " << lhsDTypep->prettyTypeName() << ", got " nodep->v3error(side << " expects a " << lhsClassRefp->prettyTypeName() << ", got "
<< rhsDtypep->prettyTypeName()); << rhsDtypep->prettyTypeName());
} }
} }
+7 -7
View File
@@ -110,10 +110,10 @@ private:
AstBasicDType* const newp = nodep->findInsertSameDType(bdtypep); AstBasicDType* const newp = nodep->findInsertSameDType(bdtypep);
if (newp != bdtypep && debug() >= 9) { if (newp != bdtypep && debug() >= 9) {
UINFO(9, "dtype replacement "); UINFO(9, "dtype replacement ");
nodep->dumpSmall(cout); nodep->dumpSmall(std::cout);
cout << " ----> "; std::cout << " ----> ";
newp->dumpSmall(cout); newp->dumpSmall(std::cout);
cout << endl; std::cout << std::endl;
} }
return newp; return newp;
} }
@@ -190,7 +190,6 @@ private:
void visit(AstNodeUOrStructDType* nodep) override { void visit(AstNodeUOrStructDType* nodep) override {
if (nodep->user1SetOnce()) return; // Process once if (nodep->user1SetOnce()) return; // Process once
visitIterateNodeDType(nodep); visitIterateNodeDType(nodep);
nodep->clearCache();
} }
void visit(AstParamTypeDType* nodep) override { void visit(AstParamTypeDType* nodep) override {
if (nodep->user1SetOnce()) return; // Process once if (nodep->user1SetOnce()) return; // Process once
@@ -253,8 +252,9 @@ public:
// Were changing widthMin's, so the table is now somewhat trashed // Were changing widthMin's, so the table is now somewhat trashed
nodep->typeTablep()->clearCache(); nodep->typeTablep()->clearCache();
iterate(nodep); iterate(nodep);
// Don't want to repairCache, as all needed nodes have been added back in // Don't want to AstTypeTable::repairCache, as all needed nodes
// a repair would prevent dead nodes from being detected // have been added back in; a repair would prevent dead nodes from
// being detected
} }
~WidthCommitVisitor() override = default; ~WidthCommitVisitor() override = default;
}; };
+2 -2
View File
@@ -348,8 +348,8 @@ private:
UINFO(6, "SELEXTRACT " << nodep << endl); UINFO(6, "SELEXTRACT " << nodep << endl);
// if (debug() >= 9) nodep->dumpTree("- SELEX0: "); // if (debug() >= 9) nodep->dumpTree("- SELEX0: ");
// Below 2 lines may change nodep->widthp() // Below 2 lines may change nodep->widthp()
V3Const::constifyParamsEdit(nodep->leftp()); // May relink pointed to node V3Const::constifyParamsNoWarnEdit(nodep->leftp()); // May relink pointed to node
V3Const::constifyParamsEdit(nodep->rightp()); // May relink pointed to node V3Const::constifyParamsNoWarnEdit(nodep->rightp()); // May relink pointed to node
// if (debug() >= 9) nodep->dumpTree("- SELEX3: "); // if (debug() >= 9) nodep->dumpTree("- SELEX3: ");
AstNodeExpr* const fromp = nodep->fromp()->unlinkFrBack(); AstNodeExpr* const fromp = nodep->fromp()->unlinkFrBack();
const FromData fromdata = fromDataForArray(nodep, fromp); const FromData fromdata = fromDataForArray(nodep, fromp);
+5
View File
@@ -49,6 +49,7 @@
#include "V3Expand.h" #include "V3Expand.h"
#include "V3File.h" #include "V3File.h"
#include "V3Force.h" #include "V3Force.h"
#include "V3Fork.h"
#include "V3Gate.h" #include "V3Gate.h"
#include "V3Global.h" #include "V3Global.h"
#include "V3Graph.h" #include "V3Graph.h"
@@ -221,6 +222,10 @@ static void process() {
V3Inst::dearrayAll(v3Global.rootp()); V3Inst::dearrayAll(v3Global.rootp());
V3LinkDot::linkDotArrayed(v3Global.rootp()); V3LinkDot::linkDotArrayed(v3Global.rootp());
// Create dedicated tasks for fork..join_any / fork_join_none processes
if (V3Fork::makeTasks(v3Global.rootp()))
V3LinkDot::linkDotPrimary(v3Global.rootp()); // Link newly created tasks
// Task inlining & pushing BEGINs names to variables/cells // Task inlining & pushing BEGINs names to variables/cells
// Begin processing must be after Param, before module inlining // Begin processing must be after Param, before module inlining
V3Begin::debeginAll(v3Global.rootp()); // Flatten cell names, before inliner V3Begin::debeginAll(v3Global.rootp()); // Flatten cell names, before inliner
+4 -4
View File
@@ -115,11 +115,11 @@ public:
} }
void dump() const { void dump() const {
cout << "# "; std::cout << "# ";
for (uint64_t i = 0; i < m_dataSize; i++) { for (uint64_t i = 0; i < m_dataSize; ++i) {
if (hits(i)) cout << "," << i; if (hits(i)) std::cout << "," << i;
} }
cout << endl; std::cout << std::endl;
} }
}; };
+12 -12
View File
@@ -16,8 +16,8 @@
// clang-format off // clang-format off
#include "config_build.h" #include "config_build.h"
#ifndef HAVE_CONFIG_BUILD #ifndef HAVE_CONFIG_PACKAGE
# error "Something failed during ./configure as config_build.h is incomplete. Perhaps you used autoreconf, don't." # error "Something failed during ./configure as config_package.h is incomplete. Perhaps you used autoreconf, don't."
#endif #endif
// clang-format on // clang-format on
@@ -48,7 +48,7 @@ static int debug() { return V3Error::debugDefault(); }
void VlcOptions::addReadFile(const string& filename) { m_readFiles.insert(filename); } void VlcOptions::addReadFile(const string& filename) { m_readFiles.insert(filename); }
string VlcOptions::version() { string VlcOptions::version() {
string ver = DTVERSION; string ver = PACKAGE_STRING;
ver += " rev " + cvtToStr(DTVERSION_rev); ver += " rev " + cvtToStr(DTVERSION_rev);
return ver; return ver;
} }
@@ -98,18 +98,18 @@ void VlcOptions::parseOptsList(int argc, char** argv) {
} }
void VlcOptions::showVersion(bool verbose) { void VlcOptions::showVersion(bool verbose) {
cout << version(); std::cout << version();
cout << endl; std::cout << endl;
if (!verbose) return; if (!verbose) return;
cout << endl; std::cout << endl;
cout << "Copyright 2003-2023 by Wilson Snyder. Verilator is free software; you can\n"; std::cout << "Copyright 2003-2023 by Wilson Snyder. Verilator is free software; you can\n";
cout << "redistribute it and/or modify the Verilator internals under the terms of\n"; std::cout << "redistribute it and/or modify the Verilator internals under the terms of\n";
cout << "either the GNU Lesser General Public License Version 3 or the Perl Artistic\n"; std::cout << "either the GNU Lesser General Public License Version 3 or the Perl Artistic\n";
cout << "License Version 2.0.\n"; std::cout << "License Version 2.0.\n";
cout << endl; std::cout << endl;
cout << "See https://verilator.org for documentation\n"; std::cout << "See https://verilator.org for documentation\n";
} }
//###################################################################### //######################################################################
+2 -2
View File
@@ -129,10 +129,10 @@ public:
// METHODS // METHODS
void dump() { void dump() {
UINFO(2, "dumpPoints...\n"); UINFO(2, "dumpPoints...\n");
VlcPoint::dumpHeader(cout); VlcPoint::dumpHeader(std::cout);
for (const auto& i : *this) { for (const auto& i : *this) {
const VlcPoint& point = pointNumber(i.second); const VlcPoint& point = pointNumber(i.second);
point.dump(cout); point.dump(std::cout);
} }
} }
VlcPoint& pointNumber(uint64_t num) { return m_points[num]; } VlcPoint& pointNumber(uint64_t num) { return m_points[num]; }
+11 -10
View File
@@ -63,20 +63,21 @@ public:
// METHODS // METHODS
static void dumpHeader() { static void dumpHeader() {
cout << "Tests:\n"; std::cout << "Tests:\n";
// cout<<" Testrun, Computrons,"; // Currently not loaded // std::cout<<" Testrun, Computrons,"; // Currently not loaded
cout << " Covered, Rank, RankPts, Filename\n"; std::cout << " Covered, Rank, RankPts, Filename\n";
} }
void dump(bool bucketsToo) { void dump(bool bucketsToo) {
if (testrun() || computrons() != 0.0) { // currently unused // LCOV_EXCL_LINE if (testrun() || computrons() != 0.0) { // currently unused // LCOV_EXCL_LINE
cout << " " << std::setw(8) << std::setfill('0') << testrun() // LCOV_EXCL_LINE std::cout << " " << std::setw(8) << std::setfill('0') << testrun() // LCOV_EXCL_LINE
<< ", " << std::setw(7) << std::setfill(' ') << computrons() // LCOV_EXCL_LINE << ", " << std::setw(7) << std::setfill(' ')
<< ","; // LCOV_EXCL_LINE << computrons() // LCOV_EXCL_LINE
<< ","; // LCOV_EXCL_LINE
} }
cout << " " << std::setw(7) << std::setfill(' ') << bucketsCovered(); std::cout << " " << std::setw(7) << std::setfill(' ') << bucketsCovered();
cout << ", " << std::setw(7) << std::setfill(' ') << rank(); std::cout << ", " << std::setw(7) << std::setfill(' ') << rank();
cout << ", " << std::setw(7) << std::setfill(' ') << rankPoints(); std::cout << ", " << std::setw(7) << std::setfill(' ') << rankPoints();
cout << ", \"" << name() << "\"\n"; std::cout << ", \"" << name() << "\"\n";
if (bucketsToo) m_buckets.dump(); if (bucketsToo) m_buckets.dump();
} }
}; };
+2 -2
View File
@@ -249,8 +249,8 @@ void VlcTop::annotateCalcNeeded() {
} }
} }
const float pct = totCases ? (100 * totOk / totCases) : 0; const float pct = totCases ? (100 * totOk / totCases) : 0;
cout << "Total coverage (" << totOk << "/" << totCases << ") "; std::cout << "Total coverage (" << totOk << "/" << totCases << ") ";
cout << std::fixed << std::setw(3) << std::setprecision(2) << pct << "%\n"; std::cout << std::fixed << std::setw(3) << std::setprecision(2) << pct << "%\n";
if (totOk != totCases) cout << "See lines with '%00' in " << opt.annotateOut() << '\n'; if (totOk != totCases) cout << "See lines with '%00' in " << opt.annotateOut() << '\n';
} }
+2 -2
View File
@@ -948,8 +948,8 @@ def write_ast_macros(filename):
if not op: if not op:
continue continue
name, monad, kind = op name, monad, kind = op
retrieve = ("VN_AS(op{n}p(), {kind})" if kind != "Node" else retrieve = ("VN_DBG_AS(op{n}p(), {kind})" if kind != "Node"
"op{n}p()").format(n=n, kind=kind) else "op{n}p()").format(n=n, kind=kind)
if monad == "List": if monad == "List":
emitBlock('''\ emitBlock('''\
Ast{kind}* {name}() const VL_MT_STABLE {{ return {retrieve}; }} Ast{kind}* {name}() const VL_MT_STABLE {{ return {retrieve}; }}
+5 -25
View File
@@ -19,12 +19,7 @@
//********************************************************************** //**********************************************************************
//**** Version and host name //**** Version and host name
// Autoconf substitutes this with the strings from AC_INIT. #include "config_package.h"
#define PACKAGE_STRING ""
#define PACKAGE_VERSION_NUMBER_STRING "0.000"
#define DTVERSION PACKAGE_STRING
//********************************************************************** //**********************************************************************
//**** Functions //**** Functions
@@ -60,35 +55,20 @@
//********************************************************************** //**********************************************************************
//**** Compile options //**** Compile options
#include <sys/types.h>
#include <iostream>
#include <cstdlib>
#include <cstdio> #include <cstdio>
#include <cstdlib>
#include <cstring> #include <cstring>
#include <string>
#include <iostream> #include <iostream>
#include <string>
#include <sys/types.h>
// Avoid needing std:: prefixes on some very common items // Avoid needing std:: prefixes on some very common items
using string = std::string; using string = std::string;
using std::cout; using std::cout;
using std::endl; using std::endl;
//**********************************************************************
//**** Configure-discovered library options
// Define if struct stat has st_mtim.tv_nsec (from configure)
#undef HAVE_STAT_NSEC
// Define if SystemC found
// - If defined, the default search path has it, so support is always enabled.
// - If undef, not system-wide, user can set SYSTEMC_INCLUDE.
#undef HAVE_SYSTEMC
#undef HAVE_COROUTINES
//********************************************************************** //**********************************************************************
//**** OS and compiler specifics //**** OS and compiler specifics
#include "verilatedos.h" #include "verilatedos.h"
//**********************************************************************
//**** This file sometimes gets truncated, so check in consumers
#define HAVE_CONFIG_BUILD
+38
View File
@@ -0,0 +1,38 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
// DESCRIPTION: Verilator: Configure source; system configuration
//
// This file is part of Verilator.
//
// Code available from: https://verilator.org
//
// Copyright 2003-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
// Autoconf substitutes this with the strings from AC_INIT.
#define PACKAGE_STRING ""
#define PACKAGE_VERSION_NUMBER_STRING "0.000"
// Otherwise Autoheader generates it (with all the same macros and more)
//**********************************************************************
//**** Configure-discovered library options
// Define if struct stat has st_mtim.tv_nsec (from configure)
#undef HAVE_STAT_NSEC
// Define if SystemC found
// - If defined, the default search path has it, so support is always enabled.
// - If undef, not system-wide, user can set SYSTEMC_INCLUDE.
#undef HAVE_SYSTEMC
// Define if coroutines are supported on this platform
#undef HAVE_COROUTINES
//**********************************************************************
//**** This file sometimes gets truncated, so check in consumers
#define HAVE_CONFIG_PACKAGE
+14 -11
View File
@@ -2251,7 +2251,7 @@ tf_variable_identifier<varp>: // IEEE: part of list_of_tf_variable_ide
id variable_dimensionListE sigAttrListE exprEqE id variable_dimensionListE sigAttrListE exprEqE
{ $$ = VARDONEA($<fl>1, *$1, $2, $3); { $$ = VARDONEA($<fl>1, *$1, $2, $3);
if ($4) AstNode::addNext<AstNode, AstNode>( if ($4) AstNode::addNext<AstNode, AstNode>(
$$, new AstAssign{$4->fileline(), new AstVarRef{$<fl>1, *$1, VAccess::WRITE}, $4}); } $$, new AstAssign{$4->fileline(), new AstParseRef{$<fl>1, VParseRefExp::PX_TEXT, *$1}, $4}); }
; ;
variable_declExpr<nodep>: // IEEE: part of variable_decl_assignment - rhs of expr variable_declExpr<nodep>: // IEEE: part of variable_decl_assignment - rhs of expr
@@ -2753,12 +2753,14 @@ genItemBegin<nodep>: // IEEE: part of generate_block
{ $$ = new AstBegin{$<fl>1, *$1, $4, true, false}; { $$ = new AstBegin{$<fl>1, *$1, $4, true, false};
GRAMMARP->endLabel($<fl>6, *$1, $6); } GRAMMARP->endLabel($<fl>6, *$1, $6); }
| id yP_COLON__BEGIN yBEGIN yEND endLabelE | id yP_COLON__BEGIN yBEGIN yEND endLabelE
{ $$ = nullptr; GRAMMARP->endLabel($<fl>5, *$1, $5); } { $$ = new AstBegin{$<fl>1, *$1, nullptr, true, false};
GRAMMARP->endLabel($<fl>5, *$1, $5); }
| yBEGIN ':' idAny ~c~genItemList yEND endLabelE | yBEGIN ':' idAny ~c~genItemList yEND endLabelE
{ $$ = new AstBegin{$<fl>3, *$3, $4, true, false}; { $$ = new AstBegin{$<fl>3, *$3, $4, true, false};
GRAMMARP->endLabel($<fl>6, *$3, $6); } GRAMMARP->endLabel($<fl>6, *$3, $6); }
| yBEGIN ':' idAny yEND endLabelE | yBEGIN ':' idAny yEND endLabelE
{ $$ = nullptr; GRAMMARP->endLabel($<fl>5, *$3, $5); } { $$ = new AstBegin{$<fl>3, *$3, nullptr, true, false};
GRAMMARP->endLabel($<fl>5, *$3, $5); }
; ;
c_genItemBegin<nodep>: // IEEE: part of generate_block (for checkers) c_genItemBegin<nodep>: // IEEE: part of generate_block (for checkers)
@@ -2993,7 +2995,7 @@ netSig<varp>: // IEEE: net_decl_assignment - one element from
{ $$ = VARDONEA($<fl>1, *$1, nullptr, $2); } { $$ = VARDONEA($<fl>1, *$1, nullptr, $2); }
| netId sigAttrListE '=' expr | netId sigAttrListE '=' expr
{ $$ = VARDONEA($<fl>1, *$1, nullptr, $2); { $$ = VARDONEA($<fl>1, *$1, nullptr, $2);
auto* const assignp = new AstAssignW{$3, new AstVarRef{$<fl>1, *$1, VAccess::WRITE}, $4}; auto* const assignp = new AstAssignW{$3, new AstParseRef{$<fl>1, VParseRefExp::PX_TEXT, *$1}, $4};
if (GRAMMARP->m_netStrengthp) assignp->strengthSpecp(GRAMMARP->m_netStrengthp->cloneTree(false)); if (GRAMMARP->m_netStrengthp) assignp->strengthSpecp(GRAMMARP->m_netStrengthp->cloneTree(false));
AstNode::addNext<AstNode, AstNode>($$, assignp); } AstNode::addNext<AstNode, AstNode>($$, assignp); }
| netId variable_dimensionList sigAttrListE | netId variable_dimensionList sigAttrListE
@@ -3910,14 +3912,14 @@ for_initializationItem<nodep>: // IEEE: variable_assignment + for_varia
AstVar* const varp = VARDONEA($<fl>2, *$2, nullptr, nullptr); AstVar* const varp = VARDONEA($<fl>2, *$2, nullptr, nullptr);
varp->lifetime(VLifetime::AUTOMATIC); varp->lifetime(VLifetime::AUTOMATIC);
$$ = varp; $$ = varp;
$$->addNext(new AstAssign{$3, new AstVarRef{$<fl>2, *$2, VAccess::WRITE}, $4}); } $$->addNext(new AstAssign{$3, new AstParseRef{$<fl>2, VParseRefExp::PX_TEXT, *$2}, $4}); }
// // IEEE-2012: // // IEEE-2012:
| yVAR data_type idAny/*new*/ '=' expr | yVAR data_type idAny/*new*/ '=' expr
{ VARRESET_NONLIST(VAR); VARDTYPE($2); { VARRESET_NONLIST(VAR); VARDTYPE($2);
AstVar* const varp = VARDONEA($<fl>3, *$3, nullptr, nullptr); AstVar* const varp = VARDONEA($<fl>3, *$3, nullptr, nullptr);
varp->lifetime(VLifetime::AUTOMATIC); varp->lifetime(VLifetime::AUTOMATIC);
$$ = varp; $$ = varp;
$$->addNext(new AstAssign{$4, new AstVarRef{$<fl>3, *$3, VAccess::WRITE}, $5}); } $$->addNext(new AstAssign{$4, new AstParseRef{$<fl>3, VParseRefExp::PX_TEXT, *$3}, $5}); }
// // IEEE: variable_assignment // // IEEE: variable_assignment
// // UNSUP variable_lvalue below // // UNSUP variable_lvalue below
| varRefBase '=' expr { $$ = new AstAssign{$2, $1, $3}; } | varRefBase '=' expr { $$ = new AstAssign{$2, $1, $3}; }
@@ -5186,7 +5188,7 @@ let_port_list<nodep>: // IEEE: let_port_list
; ;
let_port_item<nodep>: // IEEE: let_port_Item let_port_item<nodep>: // IEEE: let_port_Item
// // IEEE: Expanded let_formal_type // // IEEE: Expanded let_formal_type
yUNTYPED idAny/*formal_port_identifier*/ variable_dimensionListE exprEqE yUNTYPED idAny/*formal_port_identifier*/ variable_dimensionListE exprEqE
{ $$ = nullptr; BBUNSUP($<fl>1, "Unsupported: let untyped ports"); } { $$ = nullptr; BBUNSUP($<fl>1, "Unsupported: let untyped ports"); }
| data_type id/*formal_port_identifier*/ variable_dimensionListE exprEqE | data_type id/*formal_port_identifier*/ variable_dimensionListE exprEqE
@@ -5612,8 +5614,8 @@ idArrayedForeach<nodeExprp>: // IEEE: id + select (under foreach expression)
; ;
// VarRef without any dots or vectorizaion // VarRef without any dots or vectorizaion
varRefBase<varRefp>: varRefBase<parseRefp>:
id { $$ = new AstVarRef{$<fl>1, *$1, VAccess::READ}; } id { $$ = new AstParseRef{$<fl>1, VParseRefExp::PX_TEXT, *$1}; }
; ;
// ParseRef // ParseRef
@@ -5861,6 +5863,7 @@ property_declarationFront<nodeFTaskp>: // IEEE: part of property_declaration
property_port_listE<nodep>: // IEEE: [ ( [ property_port_list ] ) ] property_port_listE<nodep>: // IEEE: [ ( [ property_port_list ] ) ]
/* empty */ { $$ = nullptr; } /* empty */ { $$ = nullptr; }
| '(' ')' { $$ = nullptr; }
| '(' property_port_list ')' { $$ = $2; } | '(' property_port_list ')' { $$ = $2; }
; ;
@@ -7227,7 +7230,7 @@ vltOffFront<errcodeen>:
{ const char *codemsg = (*$3).c_str(); { const char *codemsg = (*$3).c_str();
if (V3ErrorCode::unusedMsg(codemsg)) {$$ = V3ErrorCode::I_UNUSED; } if (V3ErrorCode::unusedMsg(codemsg)) {$$ = V3ErrorCode::I_UNUSED; }
else {$$ = V3ErrorCode{codemsg}; } else {$$ = V3ErrorCode{codemsg}; }
if ($$ == V3ErrorCode::EC_ERROR) { $1->v3error("Unknown Error Code: " << *$3); } } if ($$ == V3ErrorCode::EC_ERROR) { $1->v3error("Unknown error code: " << *$3); } }
; ;
vltOnFront<errcodeen>: vltOnFront<errcodeen>:
@@ -7239,7 +7242,7 @@ vltOnFront<errcodeen>:
{ const char *codemsg = (*$3).c_str(); { const char *codemsg = (*$3).c_str();
if (V3ErrorCode::unusedMsg(codemsg)) {$$ = V3ErrorCode::I_UNUSED; } if (V3ErrorCode::unusedMsg(codemsg)) {$$ = V3ErrorCode::I_UNUSED; }
else {$$ = V3ErrorCode{codemsg}; } else {$$ = V3ErrorCode{codemsg}; }
if ($$ == V3ErrorCode::EC_ERROR) { $1->v3error("Unknown Error Code: " << *$3); } } if ($$ == V3ErrorCode::EC_ERROR) { $1->v3error("Unknown error code: " << *$3); } }
; ;
vltDModuleE<strp>: vltDModuleE<strp>:
+1 -1
View File
@@ -1 +1 @@
source ~/SandBox/homecvs/v4/verilator/src/.gdbinit source ../src/.gdbinit

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