mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 10:03:44 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57c816f906 | ||
|
|
8f43e74288 | ||
|
|
82e1dbe162 | ||
|
|
cef3960a4e | ||
|
|
783b7ecdab | ||
|
|
8bd1c63b32 | ||
|
|
e6fb7e970d | ||
|
|
891cc0f9b6 | ||
|
|
10fbe74cef | ||
|
|
05d04a3959 | ||
|
|
19f7279542 | ||
|
|
aa608472ae | ||
|
|
26ed2a06a0 | ||
|
|
07013da13d | ||
|
|
d840c612d4 | ||
|
|
188cf85b3f | ||
|
|
c52ba28dd0 | ||
|
|
10c1653e72 | ||
|
|
2fbd41e13b | ||
|
|
96857c5f95 | ||
|
|
131eb319e0 | ||
|
|
6e589377f4 | ||
|
|
2c84b37bf7 | ||
|
|
0d67caff77 | ||
|
|
3dde57d539 | ||
|
|
ec2e3ec0e4 | ||
|
|
9fe459c820 | ||
|
|
823e0723fb | ||
|
|
8bd6d7c5b1 | ||
|
|
839a8fa4d9 | ||
|
|
24917a187a | ||
|
|
6c6f03cf7c | ||
|
|
5676443139 | ||
|
|
e77d847671 | ||
|
|
0baee84e96 | ||
|
|
c446cc2596 | ||
|
|
b66c4153b1 | ||
|
|
d72f1b89fc | ||
|
|
b5b278d072 | ||
|
|
cb5466a90b | ||
|
|
4de25369da | ||
|
|
cdd8953085 | ||
|
|
2f2f0164ff | ||
|
|
6e977e1024 | ||
|
|
70b11f91b4 | ||
|
|
014301587f | ||
|
|
139e93d371 | ||
|
|
fb1fc46b06 | ||
|
|
9882ab6c67 | ||
|
|
1a1f919882 | ||
|
|
91227d26bb | ||
|
|
11b5dae88d | ||
|
|
0c7d485417 | ||
|
|
c40e34b134 | ||
|
|
6cdf8da3f7 | ||
|
|
c65265477f | ||
|
|
de77f1ee5c | ||
|
|
c8c980c49d | ||
|
|
ad329612fd | ||
|
|
c324895cbc | ||
|
|
679a4314dc | ||
|
|
36932d31c4 | ||
|
|
ed25aaaad9 | ||
|
|
147aa41ba5 | ||
|
|
5b7e32581b | ||
|
|
2f90fb2bec | ||
|
|
8c480fd39e | ||
|
|
ffbbd438ae | ||
|
|
96ee81fa3f | ||
|
|
3d2399ea0f | ||
|
|
6d5dde8645 | ||
|
|
048c9f3fe2 | ||
|
|
373265752f | ||
|
|
5e6519c1b0 | ||
|
|
ccdb52c1d9 | ||
|
|
b4b74d72f0 | ||
|
|
ca6ab584d0 | ||
|
|
799a502cac | ||
|
|
8d1570db28 | ||
|
|
2a30bbe493 | ||
|
|
cf6566b9bc | ||
|
|
c3e19f2821 | ||
|
|
e24197fd16 | ||
|
|
63db60f646 | ||
|
|
e66b28823d | ||
|
|
77bd565ef6 | ||
|
|
00d63883f8 | ||
|
|
10dd675371 | ||
|
|
2daa32b98b | ||
|
|
e8e7912871 | ||
|
|
90079c2974 | ||
|
|
967a8530dd | ||
|
|
ef4794e36d | ||
|
|
7c7c92d2dd | ||
|
|
95f3dd0535 | ||
|
|
768b78e7d0 | ||
|
|
4370254e73 | ||
|
|
436f72582b | ||
|
|
af3aba7821 | ||
|
|
7479db6ffc | ||
|
|
e9cc2786b7 | ||
|
|
cbdee5a804 | ||
|
|
0f66262fa1 | ||
|
|
6c80457262 | ||
|
|
16008acdf1 | ||
|
|
5447ed2629 | ||
|
|
9eba61018a | ||
|
|
bc97319402 | ||
|
|
709db5c648 | ||
|
|
b752faa107 | ||
|
|
8d512c3187 | ||
|
|
2d9bc73709 | ||
|
|
b0942ed8c7 | ||
|
|
e6b0bdd4df | ||
|
|
b1c1aa53a9 | ||
|
|
f79749e7b2 | ||
|
|
1a77030c9b | ||
|
|
817ade27fb | ||
|
|
4afa14b612 | ||
|
|
9caa79a7ea | ||
|
|
446f21d2a0 | ||
|
|
06521f6cf1 | ||
|
|
75336670d7 | ||
|
|
106a9fe67f | ||
|
|
a5eccc199b | ||
|
|
837d9f9d77 | ||
|
|
959387b69f | ||
|
|
fd0703b8c8 | ||
|
|
3ff608b9cb | ||
|
|
edc0a0e982 | ||
|
|
1549ff9185 | ||
|
|
3454641244 | ||
|
|
5dedf25e3b | ||
|
|
7214c180ae | ||
|
|
aa0ef554b3 | ||
|
|
a6015863d8 | ||
|
|
5a694ccb8a | ||
|
|
f7d09c671a | ||
|
|
4de1b22672 | ||
|
|
4bdda3f240 | ||
|
|
ced1dfeffd | ||
|
|
0774f825cf | ||
|
|
23fe5c1b93 | ||
|
|
bd4afeea69 | ||
|
|
3ee9b33f8a | ||
|
|
5be0813893 | ||
|
|
f94c1b32e5 | ||
|
|
ff4923cf67 | ||
|
|
5788bc8048 | ||
|
|
5749662194 | ||
|
|
d5d1cc47e0 | ||
|
|
da043ca16d | ||
|
|
9aa90569bf | ||
|
|
294d78e0e2 | ||
|
|
8182c3fe2c | ||
|
|
6f7bc38088 | ||
|
|
dcbc0ccfdc | ||
|
|
399319dc1c | ||
|
|
35d6da391b | ||
|
|
fcbd7c87af | ||
|
|
97feba6898 | ||
|
|
9249ffdb84 | ||
|
|
71df9e3381 | ||
|
|
3c964147be | ||
|
|
54b4b6b378 | ||
|
|
cff37f0775 | ||
|
|
8b1cc3b303 | ||
|
|
65b6121186 | ||
|
|
3d30527860 | ||
|
|
24a9f7c650 | ||
|
|
242f661644 | ||
|
|
32019d2bc4 | ||
|
|
b252bb048f | ||
|
|
ba47c01a01 | ||
|
|
51266898ec | ||
|
|
f9164ab0d2 | ||
|
|
c43e707f43 | ||
|
|
85b7f828b3 | ||
|
|
7005a65d32 | ||
|
|
776d4e6ac2 | ||
|
|
1c88c3083c | ||
|
|
4522834f7a | ||
|
|
9ee5fd0585 | ||
|
|
63b2dbb827 | ||
|
|
5bee8ef15b | ||
|
|
e7de3d368f | ||
|
|
d45deccc0a | ||
|
|
d5b03ab721 | ||
|
|
d1ff81a41c | ||
|
|
43fa6858f6 | ||
|
|
db7eb45433 | ||
|
|
21c01ba97b | ||
|
|
b517fb5ee3 | ||
|
|
9dbc669199 | ||
|
|
4f13c4d1bb | ||
|
|
c37ec19751 | ||
|
|
aaaf8e75af | ||
|
|
8cc31c6cf1 | ||
|
|
8468af1a20 | ||
|
|
db39d70c75 | ||
|
|
04fff7ebd8 | ||
|
|
b154af87a1 | ||
|
|
8b7480806a | ||
|
|
e29f5ad559 | ||
|
|
4618fd5a43 | ||
|
|
ed979c6aa1 | ||
|
|
141c5da3f9 | ||
|
|
0185ee5df3 | ||
|
|
27c08a65e1 | ||
|
|
de408a5eb7 | ||
|
|
fa3c072dd1 | ||
|
|
aac4b7090a | ||
|
|
f0d664530d | ||
|
|
702140bf93 | ||
|
|
2075db3db0 | ||
|
|
b7fec513cf | ||
|
|
78e7758022 | ||
|
|
818318ddd5 | ||
|
|
39eea781bc | ||
|
|
c4f7bd6a04 | ||
|
|
dd03d0f3c9 | ||
|
|
369ce6af2c | ||
|
|
f83817510d | ||
|
|
bfcd779cfb | ||
|
|
e69c380205 | ||
|
|
61e14aa827 | ||
|
|
0c6c83e278 | ||
|
|
6f52208b68 |
@@ -36,9 +36,9 @@ jobs:
|
||||
m32: [0, 1]
|
||||
exclude:
|
||||
# Build pull requests only with ubuntu-22.04 and without m32
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-20.04' || 'do-not-exclude' }}
|
||||
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
|
||||
# - os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
|
||||
# - os: ${{ github.event_name == 'pull_request' && 'ubuntu-20.04' || 'do-not-exclude' }}
|
||||
# - m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
|
||||
# Build -m32 only on ubuntu-22.04 clang++
|
||||
- {os: ubuntu-18.04, m32: 1}
|
||||
- {os: ubuntu-20.04, m32: 1}
|
||||
@@ -107,9 +107,9 @@ jobs:
|
||||
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, vltmt-0, vltmt-1]
|
||||
exclude:
|
||||
# Build pull requests only with ubuntu-22.04 and without m32
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-20.04' || 'do-not-exclude' }}
|
||||
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
|
||||
# - os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
|
||||
# - os: ${{ github.event_name == 'pull_request' && 'ubuntu-20.04' || 'do-not-exclude' }}
|
||||
# - m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
|
||||
# Build -m32 only on ubuntu-22.04
|
||||
- {os: ubuntu-18.04, m32: 1}
|
||||
- {os: ubuntu-20.04, m32: 1}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Build and push verilator docker image when tags are pushed to the repository.
|
||||
# The following secrets must be configured in the github repository:
|
||||
# The following variable(s) must be configured in the github repository:
|
||||
# DOCKER_HUB_NAMESPACE: docker hub namespace.
|
||||
# The following secrets must be configured in the github repository:
|
||||
# DOCKER_HUB_USER: user name for logging into docker hub
|
||||
# DOCKER_HUB_ACCESS_TOKEN: docker hub access token.
|
||||
name: Build Verilator Container
|
||||
@@ -52,7 +53,7 @@ jobs:
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
${{ secrets.DOCKER_HUB_NAMESPACE }}/${{ env.image_name }}
|
||||
${{ vars.DOCKER_HUB_NAMESPACE }}/${{ env.image_name }}
|
||||
tags: |
|
||||
type=match,pattern=(v.*),group=1,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
type=raw,value=${{ inputs.manual_tag }},enable=${{ inputs.manual_tag != '' }}
|
||||
|
||||
@@ -41,3 +41,5 @@ verilator-config-version.cmake
|
||||
**/_build/*
|
||||
**/obj_dir/*
|
||||
/.vscode/
|
||||
/.idea/
|
||||
/cmake-build-*/
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
|
||||
project(Verilator
|
||||
VERSION 5.012
|
||||
VERSION 5.016
|
||||
HOMEPAGE_URL https://verilator.org
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
@@ -8,6 +8,106 @@ The changes in each Verilator version are described below. The
|
||||
contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
|
||||
Verilator 5.016 2023-09-16
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Add prepareClone and atClone APIs for Verilated models (#3503) (#4444). [Yinan Xu]
|
||||
* Add check for conflicting options e.g. binary and lint-only (#4409). [Ethan Sifferman]
|
||||
* Add --no-trace-top to not trace top signals (#4412) (#4422). [Frans Skarman]
|
||||
* Support recursive function calls (#3267).
|
||||
* Support assignments of packed values to stream expressions on queues (#4401). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support no-parentheses calls to static methods (#4432). [Krzysztof Boroński]
|
||||
* Support block_item_declaration in forks (#4455). [Krzysztof Boroński]
|
||||
* Support assignments of stream expressions on queues to packed values (#4458). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support function non-constant default arguments (#4470).
|
||||
* Support 'let'.
|
||||
* Optimize Verilator executable size by refactoring error reporting routines (#4446). [Anthony Donlon]
|
||||
* Optimize Verilation runtime pointers and graphs (#4396) (#4397) (#4398). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Optimize preparations towards multithreaded Verilation (#4291) (#4463) (#4476) (#4477) (#4479). [Kamil Rakoczy, Antmicro Ltd]
|
||||
* Fix Windows filename format, etc (#3873) (#4421). [Anthony Donlon].
|
||||
* Fix t_dist_cppstyle Perl performance issue (#4085). [Srinivasan Venkataramanan]
|
||||
* Fix using type in parameterized classes without #() (#4281) (#4440). [Anthony Donlon]
|
||||
* Fix false INFINITELOOP on forever..mailbox.get() (#4323). [Srinivasan Venkataramanan]
|
||||
* Fix data type of condition operation on class objects (#4345) (#4352). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix variables mutated under fork..join_none/join_any blocks into anonymous objects (#4356). [Krzysztof Boroński]
|
||||
* Fix V3CUse, do not consider implementations (.cpp) at all (#4386). [Krzysztof Boroński]
|
||||
* Fix ++/-- under statements (#4399). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix detection of mixed blocking and nonblocking assignment in nested assignments (#4404). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix jumping over object initialization (#4411). [Krzysztof Boroński]
|
||||
* Fix multiple issues towards short circuit support (#4413) (#4460). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix variable lifetimes in extern methods (#4414). [Krzysztof Boroński]
|
||||
* Fix multiple function definitions in V3Sched (#4416). [Hennadii Chernyshchyk]
|
||||
* Fix false UNUSEDPARAM on generate localparam (#4427). [Bill Pringlemeir]
|
||||
* Fix checking for parameter and port connections in the wrong place (#4428). [Anthony Donlon]
|
||||
* Fix coroutine handle movement during queue manipulation (#4431). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix nested assignments on the LHS (#4435). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix false MULTITOP on bound interfaces (#4438). [Alex Solomatnikov]
|
||||
* Fix internal error on real conversion (#4447). [vdhotre-ventana]
|
||||
* Fix lifetime unknown error on enum.name (#4448). [jwoutersymatra]
|
||||
* Fix unstable output of VHashSha256 (#4453). [Anthony Donlon]
|
||||
* Fix static cast from a stream type (#4469) (#4485). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix error on enum with VARHIDDEN of cell (#4482). [Michail Rontionov]
|
||||
* Fix lint of case statements with enum and wildcard bits (#4464) (#4487). [Anthony Donlon]
|
||||
* Fix reference to extended class in parameterized class (#4466).
|
||||
* Fix recursive display causing segfault (#4480). [Kuoping Hsu]
|
||||
* Fix the error message when the type of ref argument is wrong (#4490). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix display %x formatting of real.
|
||||
* Fix mis-warning on #() in classes' own functions.
|
||||
* Fix IGNOREDRETURN to not warn on void-cast static function calls.
|
||||
* Fix ZERODLY to not warn on 'wait(0)'.
|
||||
|
||||
|
||||
Verilator 5.014 2023-08-06
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Deprecation planned for 32-bit pointer -m32 mode (#4268).
|
||||
* Deprecate CMake config below version 3.13 (#4389) (#4390). [Vito Gamberini]
|
||||
* Support some stream operations on queues (#4292). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support property declaration with empty parentheses (#4313) (#4317). [Anthony Donlon]
|
||||
* Support locator methods with "with" on assoc arrays (#4335). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support string replication with variable (#4341). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Support more types in wait (#4374). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Support static method calls as default values of function arguments (#4378). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Add GENUNNAMED lint warning. [Srinivasan Venkataramanan, Deepa Palaniappan]
|
||||
* Add MISINDENT lint warning for misleading indentation.
|
||||
* Fix 'VlForkSync' redeclaration (#4277). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix processes that can outlive their parents (#4253). [Krzysztof Boronski, Antmicro Ltd]
|
||||
* Fix duplicate fork names (#4295). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix splitting coroutines (#4297) (#4307). [Jiamin Zhu]
|
||||
* Fix error when multiple duplicate DPI exports (#4301).
|
||||
* Fix class reference assignment checking (#4296). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix handling of ref types in initial values of type parameters (#4304). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix comparison of string parameters (#4308). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix state update for always processes (#4311). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix multiple edge timing controls in class methods (#4318) (#4320) (#4344). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix implicit calls of base class constructors with optional arguments (#4319). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix propagation of process requirement (#4321). [Krzysztof Boroński]
|
||||
* Fix unhandled overloads in V3InstrCount (#4324). [Krzysztof Boroński]
|
||||
* Fix selects of static members (#4326). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix references to members of results of static methods (#4327). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix unique..with method on queues of class objects (#4328). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix queue slicing (#4329). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix wildcard referring types (#4336) (#4342). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix comparison of class objects (#4346). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix unexpected RefDType on assoc arrays (#4337). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix cmake astgen for Rocky Linux 8.7 (#4343). [Julian Daube]
|
||||
* Fix class timescale in class packages (#4348). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix string concatenations (#4354). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix unlinked task error from broken context (#4355) (#4402). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix selects on unpacked structs (#4359). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix select operation on assoc array with wide keys (#4360). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix non-public methods with wide output (#4364). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix handling of super.new calls (#4366). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix assign to input var in methods (#4367). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix VlProcess not found (#4368). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix order of evaluation of function calls in statements (#4375). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix config_build.h issues (#4380) (#4381). [Andrew Miloradovsky]
|
||||
|
||||
|
||||
Verilator 5.012 2023-06-13
|
||||
==========================
|
||||
|
||||
|
||||
+2
-2
@@ -449,8 +449,8 @@ install-msg:
|
||||
|
||||
IN_WILD := ${srcdir}/*.in ${srcdir}/*/*.in
|
||||
|
||||
# autoheader might not change config_build.h.in, so touch it
|
||||
${srcdir}/config_build.h: ${srcdir}/config_build.h.in configure
|
||||
# autoheader might not change config_package.h.in, so touch it
|
||||
${srcdir}/config_package.h: ${srcdir}/config_package.h.in configure
|
||||
cd ${srcdir} && autoheader
|
||||
touch $@
|
||||
Makefile: Makefile.in config.status $(IN_WILD)
|
||||
|
||||
+5
-3
@@ -1,20 +1,22 @@
|
||||
.. Github doesn't render images unless absolute URL
|
||||
.. Do not know of a conditional tag, "only: github" nor "github display" works
|
||||
|
||||
.. image:: https://img.shields.io/badge/Website-Verilator.org-181717.svg
|
||||
:target: https://verilator.org
|
||||
.. image:: https://img.shields.io/badge/License-LGPL%20v3-blue.svg
|
||||
:target: https://www.gnu.org/licenses/lgpl-3.0]
|
||||
:target: https://www.gnu.org/licenses/lgpl-3.0
|
||||
.. image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
|
||||
:target: https://opensource.org/licenses/Artistic-2.0
|
||||
.. image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
|
||||
:target: https://repology.org/project/verilator/versions
|
||||
.. image:: https://img.shields.io/docker/pulls/verilator/verilator
|
||||
:target: https://hub.docker.com/r/verilator/verilator
|
||||
.. image:: https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f
|
||||
:target: https://www.codacy.com/gh/verilator/verilator
|
||||
.. image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg
|
||||
:target: https://codecov.io/gh/verilator/verilator
|
||||
.. image:: https://github.com/verilator/verilator/workflows/build/badge.svg
|
||||
: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
|
||||
|
||||
@@ -440,6 +440,7 @@ detailed descriptions of these arguments.
|
||||
--trace-params Enable tracing of parameters
|
||||
--trace-structs Enable tracing structure names
|
||||
--trace-threads <threads> Enable FST waveform creation on separate threads
|
||||
--no-trace-top Do not emit traces for signals in the top module generated by verilator
|
||||
--trace-underscore Enable tracing of _signals
|
||||
-U<var> Undefine preprocessor define
|
||||
--no-unlimited-stack Don't disable stack size limit
|
||||
|
||||
+2
-2
@@ -71,8 +71,8 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
|
||||
sudo apt-get install libsystemc libsystemc-dev
|
||||
fi
|
||||
if [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
|
||||
sudo apt-get install mold ||
|
||||
sudo apt-get install mold
|
||||
sudo apt-get install bear mold ||
|
||||
sudo apt-get install bear mold
|
||||
fi
|
||||
if [ "$COVERAGE" = 1 ]; then
|
||||
yes yes | sudo cpan -fi Parallel::Forker
|
||||
|
||||
+2
-2
@@ -10,11 +10,11 @@
|
||||
# Then 'make maintainer-dist'
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[5.012 2023-06-13],
|
||||
AC_INIT([Verilator],[5.016 2023-09-16],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
|
||||
AC_CONFIG_HEADERS(src/config_build.h)
|
||||
AC_CONFIG_HEADERS(src/config_package.h)
|
||||
AC_CONFIG_FILES(Makefile src/Makefile src/Makefile_obj include/verilated.mk include/verilated_config.h verilator.pc verilator-config.cmake verilator-config-version.cmake)
|
||||
|
||||
# Version
|
||||
|
||||
+12
-5
@@ -13,7 +13,9 @@ Aliaksei Chapyzhenka
|
||||
Ameya Vikram Singh
|
||||
Andreas Kuster
|
||||
Andrei Kostovski
|
||||
Andrew Miloradovsky
|
||||
Andrew Nolte
|
||||
Anthony Donlon
|
||||
Arkadiusz Kozdra
|
||||
Aylon Chaim Porat
|
||||
Cameron Kirk
|
||||
@@ -38,6 +40,8 @@ Fan Shupei
|
||||
february cozzocrea
|
||||
Felix Neumärker
|
||||
Felix Yan
|
||||
Frans Skarman
|
||||
G-A. Kamendje
|
||||
Garrett Smith
|
||||
Geza Lore
|
||||
Gianfranco Costamagna
|
||||
@@ -46,7 +50,6 @@ Gökçe Aydos
|
||||
Graham Rushton
|
||||
Guokai Chen
|
||||
Gustav Svensk
|
||||
G-A. Kamendje
|
||||
Harald Heckmann
|
||||
Hennadii Chernyshchyk
|
||||
Howard Su
|
||||
@@ -54,9 +57,9 @@ Huang Rui
|
||||
Huanghuang Zhou
|
||||
HungMingWu
|
||||
HyungKi Jeong
|
||||
Ilya Barkov
|
||||
Iru Cai
|
||||
Ivan Vnučec
|
||||
Ilya Barkov
|
||||
Iztok Jeras
|
||||
Jake Merdich
|
||||
James Hanlon
|
||||
@@ -71,17 +74,18 @@ Jeremy Bennett
|
||||
Jesse Taube
|
||||
Jevin Sweval
|
||||
Jiacheng Qian
|
||||
Jiamin Zhu
|
||||
Jiuyang Liu
|
||||
Joey Liu
|
||||
John Coiner
|
||||
John Demme
|
||||
John Wehle
|
||||
Jiamin Zhu
|
||||
Jonathan Drolet
|
||||
Jose Loyola
|
||||
Joseph Nwabueze
|
||||
Josep Sans
|
||||
Joseph Nwabueze
|
||||
Josh Redford
|
||||
Julian Daube
|
||||
Julie Schwartz
|
||||
Julien Margetts
|
||||
Kaleb Barrett
|
||||
@@ -91,6 +95,7 @@ Keith Colbert
|
||||
Kevin Kiningham
|
||||
Kritik Bhimani
|
||||
Krzysztof Bieganski
|
||||
Krzysztof Boronski
|
||||
Krzysztof Boroński
|
||||
Kuba Ober
|
||||
Larry Doolittle
|
||||
@@ -162,15 +167,17 @@ Varun Koyyalagunta
|
||||
Vassilis Papaefstathiou
|
||||
Veripool API Bot
|
||||
Victor Besyakov
|
||||
Vito Gamberini
|
||||
William D. Jones
|
||||
Wilson Snyder
|
||||
Xi Zhang
|
||||
Yinan Xu
|
||||
Yoda Lee
|
||||
Yossi Nivin
|
||||
Yu-Sheng Lin
|
||||
Yuri Victorovich
|
||||
Yutetsu TAKATSUKASA
|
||||
Yu-Sheng Lin
|
||||
Yves Mathieu
|
||||
Zhanglei Wang
|
||||
Zixi Li
|
||||
أحمد المحمودي
|
||||
|
||||
+5
-1
@@ -18,12 +18,16 @@
|
||||
RST2HTML = rst2html
|
||||
PYTHON3 = python3
|
||||
DOXYGEN = doxygen
|
||||
SPHINXOPTS ?= -c guide
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
|
||||
SOURCEDIR = guide
|
||||
BUILDDIR = _build
|
||||
|
||||
SPHINXOPTS ?= -c guide
|
||||
ifneq ($(VERILATOR_ANALYTICS_ID),)
|
||||
SPHINXOPTS += -D html_theme_options.analytics_id=$(VERILATOR_ANALYTICS_ID)
|
||||
endif
|
||||
|
||||
######################################################################
|
||||
|
||||
.SUFFIXES:
|
||||
|
||||
@@ -115,7 +115,6 @@ html_theme = 'sphinx_rtd_theme'
|
||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
||||
|
||||
html_theme_options = {
|
||||
'analytics_id': 'G-D27B0C9QEB',
|
||||
'logo_only': True,
|
||||
'style_nav_header_background': "#45acf8", # Default is #2980B9
|
||||
# 'canonical_url':
|
||||
|
||||
@@ -128,6 +128,43 @@ in the distribution. These headers use Doxygen comments, `///` and `//<`,
|
||||
to indicate and document those functions that are part of the Verilated
|
||||
public API.
|
||||
|
||||
Process-Level Clone APIs
|
||||
--------------------------
|
||||
|
||||
Modern operating systems support process-level clone (a.k.a copying, forking)
|
||||
with system call interfaces in C/C++, e.g., :code:`fork()` in Linux.
|
||||
|
||||
However, after cloning a parent process, some resources cannot be inherited
|
||||
in the child process. For example, in POSIX systems, when you fork a process,
|
||||
the child process inherits all the memory of the parent process. However,
|
||||
only the thread that called fork is replicated in the child process. Other
|
||||
threads are not.
|
||||
|
||||
Therefore, to support the process-level clone mechanisms, Verilator supports
|
||||
:code:`prepareClone()` and :code:`atClone()` APIs to allow the user to manually
|
||||
re-construct the model in the child process. The two APIs handle all necessary
|
||||
resources required for releasing and re-initializing before and after cloning.
|
||||
|
||||
The two APIs are supported in the verilated models. Here is an example of usage
|
||||
with Linux :code:`fork()` and :code:`pthread_atfork` APIs:
|
||||
|
||||
.. code-block:: C++
|
||||
|
||||
// static function pointers to fit pthread_atfork
|
||||
static auto prepareClone = [](){ topp->prepareClone(); };
|
||||
static auto atClone = [](){ topp->atClone(); };
|
||||
|
||||
// in main function, register the handlers:
|
||||
pthread_atfork(prepareClone, atClone, atClone);
|
||||
|
||||
For better flexibility, you can also manually call the handlers before and
|
||||
after :code:`fork()`.
|
||||
|
||||
With the process-level clone APIs, users can create process-level snapshots
|
||||
for the verilated models. While the Verilator save/restore option provides
|
||||
persistent and circuit-dependent snapshots, the process-level clone APIs
|
||||
enable in-memory, circuit-transparent, and highly efficient snapshots.
|
||||
|
||||
|
||||
Direct Programming Interface (DPI)
|
||||
==================================
|
||||
|
||||
@@ -17,10 +17,10 @@ C++11 compiler support
|
||||
Verilator will require C++20 or newer compilers for both compiling
|
||||
Verilator and compiling all Verilated models no sooner than January 2025.
|
||||
|
||||
Verilated_heavy.h
|
||||
The legacy "verilated_heavy.h" include was replaced with just including
|
||||
"verilated.h". Verilated_heavy.h is planned for removal no sooner than
|
||||
July 2022.
|
||||
32-bit compiler support
|
||||
Verilator currently regresses both 64-bit and 32-bit pointer modes (GCC's
|
||||
`-m64` and `-m32`). Support for 32-bit `-m32` mode will be deprecated no
|
||||
sooner than January 2024.
|
||||
|
||||
Option `-O<letter>`
|
||||
The debug `-O<letter>` options have been replaced with
|
||||
|
||||
@@ -125,9 +125,12 @@ Summary:
|
||||
|
||||
After generating the SystemC/C++ code, Verilator will invoke the
|
||||
toolchain to build the model library (and executable when :vlopt:`--exe`
|
||||
is also used). Verilator manages the build itself, and for this --build
|
||||
is also used). Verilator manages the build itself, and for this --build
|
||||
requires GNU Make to be available on the platform.
|
||||
|
||||
:vlopt:`--build` cannot be specified when using :vlopt:`-E`,
|
||||
:vlopt:`--dpi-hdr-only`, :vlopt:`--lint-only`, or :vlopt:`--xml-only`.
|
||||
|
||||
.. option:: --build-dep-bin <filename>
|
||||
|
||||
Rarely needed. When a dependency (.d) file is created, this filename
|
||||
@@ -1314,6 +1317,7 @@ Summary:
|
||||
This is not needed with standard designs with only one top. See also
|
||||
:option:`MULTITOP` warning.
|
||||
|
||||
|
||||
.. option:: --trace
|
||||
|
||||
Adds waveform tracing code to the model using VCD format. This overrides
|
||||
@@ -1392,6 +1396,17 @@ Summary:
|
||||
This option is accepted, but has absolutely no effect with
|
||||
:vlopt:`--trace`, which respects :vlopt:`--threads` instead.
|
||||
|
||||
.. option:: --no-trace-top
|
||||
|
||||
Disables tracing for the input and output signals in the top wrapper which
|
||||
Verilator adds to the design. The signals are still traced in the original
|
||||
verilog top modules.
|
||||
|
||||
When combined with :option:`--main-top-name` set to "-" or when the name of
|
||||
the top module is set to "" in its constructor, the generated trace file
|
||||
will have the verilog top module as its root, rather than another module
|
||||
added by Verilator.
|
||||
|
||||
.. option:: --trace-underscore
|
||||
|
||||
Enable tracing of signals or modules that start with an
|
||||
@@ -1523,13 +1538,15 @@ Summary:
|
||||
|
||||
.. option:: -Wno-lint
|
||||
|
||||
Disable all lint-related warning messages, and all style warnings. This is
|
||||
equivalent to ``-Wno-ALWCOMBORDER -Wno-ASCRANGE -Wno-BSSPACE -Wno-CASEINCOMPLETE
|
||||
-Wno-CASEOVERLAP -Wno-CASEX -Wno-CASTCONST -Wno-CASEWITHX -Wno-CMPCONST -Wno-COLONPLUS
|
||||
-Wno-IMPLICIT -Wno-IMPLICITSTATIC -Wno-PINCONNECTEMPTY
|
||||
-Wno-PINMISSING -Wno-STATICVAR -Wno-SYNCASYNCNET -Wno-UNDRIVEN -Wno-UNSIGNED
|
||||
-Wno-UNUSEDGENVAR -Wno-UNUSEDPARAM -Wno-UNUSEDSIGNAL
|
||||
-Wno-WIDTH`` plus the list shown for Wno-style.
|
||||
Disable all lint-related warning messages, and all style warnings. This
|
||||
is equivalent to ``-Wno-ALWCOMBORDER`` ``-Wno-ASCRANGE``
|
||||
``-Wno-BSSPACE`` ``-Wno-CASEINCOMPLETE`` ``-Wno-CASEOVERLAP``
|
||||
``-Wno-CASEX`` ``-Wno-CASTCONST`` ``-Wno-CASEWITHX`` ``-Wno-CMPCONST``
|
||||
``-Wno-COLONPLUS`` ``-Wno-IMPLICIT`` ``-Wno-IMPLICITSTATIC``
|
||||
``-Wno-PINCONNECTEMPTY`` ``-Wno-PINMISSING`` ``-Wno-STATICVAR``
|
||||
``-Wno-SYNCASYNCNET`` ``-Wno-UNDRIVEN`` ``-Wno-UNSIGNED``
|
||||
``-Wno-UNUSEDGENVAR`` ``-Wno-UNUSEDPARAM`` ``-Wno-UNUSEDSIGNAL``
|
||||
``-Wno-WIDTH``, plus the list shown for :vlopt:`-Wno-style`.
|
||||
|
||||
It is strongly recommended that you clean up your code rather than using this
|
||||
option; it is only intended to be used when running test-cases of code
|
||||
@@ -1537,12 +1554,13 @@ Summary:
|
||||
|
||||
.. option:: -Wno-style
|
||||
|
||||
Disable all code style related warning messages (note that by default, they are
|
||||
already disabled). This is equivalent to ``-Wno-DECLFILENAME -Wno-DEFPARAM
|
||||
-Wno-EOFNEWLINE -Wno-IMPORTSTAR -Wno-INCABSPATH -Wno-PINCONNECTEMPTY
|
||||
-Wno-PINNOCONNECT -Wno-SYNCASYNCNET -Wno-UNDRIVEN
|
||||
-Wno-UNUSEDGENVAR -Wno-UNUSEDPARAM -Wno-UNUSEDSIGNAL
|
||||
-Wno-VARHIDDEN``.
|
||||
Disable all code style related warning messages (note that by default,
|
||||
they are already disabled). This is equivalent to ``-Wno-DECLFILENAME``
|
||||
``-Wno-DEFPARAM`` ``-Wno-EOFNEWLINE`` ``-Wno-GENUNNAMED``
|
||||
``-Wno-IMPORTSTAR`` ``-Wno-INCABSPATH`` ``-Wno-PINCONNECTEMPTY``
|
||||
``-Wno-PINNOCONNECT`` ``-Wno-SYNCASYNCNET`` ``-Wno-UNDRIVEN``
|
||||
``-Wno-UNUSEDGENVAR`` ``-Wno-UNUSEDPARAM`` ``-Wno-UNUSEDSIGNAL``
|
||||
``-Wno-VARHIDDEN``.
|
||||
|
||||
.. option:: -Wpedantic
|
||||
|
||||
@@ -1560,19 +1578,25 @@ Summary:
|
||||
|
||||
.. option:: -Wwarn-lint
|
||||
|
||||
Enable all lint-related warning messages (note that by default, they are already
|
||||
enabled), but do not affect style messages. This is equivalent to
|
||||
``-Wwarn-ALWCOMBORDER -Wwarn-BSSPACE -Wwarn-CASEINCOMPLETE
|
||||
-Wwarn-CASEOVERLAP -Wwarn-CASEX -Wwarn-CASTCONST -Wwarn-CASEWITHX -Wwarn-CMPCONST
|
||||
-Wwarn-COLONPLUS -Wwarn-IMPLICIT -Wwarn-ASCRANGE
|
||||
-Wwarn-PINMISSING -Wwarn-REALCVT -Wwarn-UNSIGNED -Wwarn-WIDTH``.
|
||||
Enable all lint-related warning messages (note that by default, they are
|
||||
already enabled), but do not affect style messages. This is equivalent
|
||||
to ``-Wwarn-ALWCOMBORDER`` ``-Wwarn-ASCRANGE`` ``-Wwarn-BSSPACE``
|
||||
``-Wwarn-CASEINCOMPLETE`` ``-Wwarn-CASEOVERLAP`` ``-Wwarn-CASEWITHX``
|
||||
``-Wwarn-CASEX`` ``-Wwarn-CASTCONST`` ``-Wwarn-CMPCONST``
|
||||
``-Wwarn-COLONPLUS`` ``-Wwarn-IMPLICIT`` ``-Wwarn-IMPLICITSTATIC``
|
||||
``-Wwarn-LATCH`` ``-Wwarn-MISINDENT`` ``-Wwarn-NEWERSTD``
|
||||
``-Wwarn-PINMISSING`` ``-Wwarn-REALCVT`` ``-Wwarn-STATICVAR``
|
||||
``-Wwarn-UNSIGNED`` ``-Wwarn-WIDTHTRUNC`` ``-Wwarn-WIDTHEXPAND``
|
||||
``-Wwarn-WIDTHXZEXPAND``.
|
||||
|
||||
.. option:: -Wwarn-style
|
||||
|
||||
Enable all code style-related warning messages. This is equivalent to
|
||||
``-Wwarn ASSIGNDLY -Wwarn-DECLFILENAME -Wwarn-DEFPARAM -Wwarn-EOFNEWLINE
|
||||
-Wwarn-INCABSPATH -Wwarn-PINNOCONNECT -Wwarn-SYNCASYNCNET -Wwarn-UNDRIVEN
|
||||
-Wwarn-UNUSEDGENVAR -Wwarn-UNUSEDPARAM -Wwarn-UNUSEDSIGNAL -Wwarn-VARHIDDEN``.
|
||||
``-Wwarn-ASSIGNDLY`` ``-Wwarn-DECLFILENAME`` ``-Wwarn-DEFPARAM``
|
||||
``-Wwarn-EOFNEWLINE`` ``-Wwarn-GENUNNAMED`` ``-Wwarn-INCABSPATH``
|
||||
``-Wwarn-PINNOCONNECT`` ``-Wwarn-SYNCASYNCNET`` ``-Wwarn-UNDRIVEN``
|
||||
``-Wwarn-UNUSEDGENVAR`` ``-Wwarn-UNUSEDPARAM`` ``-Wwarn-UNUSEDSIGNAL``
|
||||
``-Wwarn-VARHIDDEN``.
|
||||
|
||||
.. option:: --x-assign 0
|
||||
|
||||
|
||||
+1
-1
@@ -486,7 +486,7 @@ From the sc_main.cpp file, you'd then:
|
||||
|
||||
#include "Vour.h"
|
||||
#include "Vour_our.h"
|
||||
cout << "clock is " << top->our->clk << endl;
|
||||
std::cout << "clock is " << top->our->clk << std::endl;
|
||||
|
||||
|
||||
In this example, clk is a bool you can read or set as any other variable.
|
||||
|
||||
@@ -126,7 +126,7 @@ Those developing Verilator itself may also want these (see internals.rst):
|
||||
::
|
||||
|
||||
sudo apt-get install clang clang-format-14 cmake gdb gprof graphviz lcov
|
||||
sudo apt-get install libclang-dev yapf3
|
||||
sudo apt-get install libclang-dev yapf3 bear
|
||||
sudo pip3 install clang sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe ruff
|
||||
cpan install Pod::Perldoc
|
||||
cpan install Parallel::Forker
|
||||
|
||||
@@ -44,7 +44,7 @@ The best place to get started is to try the :ref:`Examples`.
|
||||
.. [#] Verilog is defined by the `Institute of Electrical and Electronics
|
||||
Engineers (IEEE) Standard for Verilog Hardware Description
|
||||
Language`, Std. 1364, released in 1995, 2001, and 2005. The
|
||||
Verilator documentation uses the shorthand, e.g., "IEEE 1394-2005",
|
||||
Verilator documentation uses the shorthand, e.g., "IEEE 1364-2005",
|
||||
to refer to the, e.g., 2005 version of this standard.
|
||||
|
||||
.. [#] SystemVerilog is defined by the `Institute of Electrical and
|
||||
|
||||
@@ -500,7 +500,7 @@ feedback-directed optimization. See the appropriate compiler
|
||||
documentation.
|
||||
|
||||
|
||||
.. _Runtime Debugging
|
||||
.. _Runtime Debugging:
|
||||
|
||||
Runtime Debugging
|
||||
=================
|
||||
|
||||
@@ -165,7 +165,7 @@ compiled on the different target system.
|
||||
|
||||
To support this, none of the files that Verilator produces will reference
|
||||
any configure-generated build-system-specific files, such as
|
||||
:file:`config.h` (which is renamed in Verilator to :file:`config_build.h`
|
||||
:file:`config.h` (which is renamed in Verilator to :file:`config_package.h`
|
||||
to reduce confusion.) The disadvantage of this approach is that
|
||||
:file:`include/verilatedos.h` must self-detect the requirements of the
|
||||
target system, rather than using configure.
|
||||
|
||||
+192
-1
@@ -670,6 +670,52 @@ List Of Warnings
|
||||
used as a clock.
|
||||
|
||||
|
||||
.. option:: GENUNNAMED
|
||||
|
||||
Warns that a generate block was unnamed and "genblk" will be used per
|
||||
IEEE.
|
||||
|
||||
The potential issue is that adding additional generate blocks will
|
||||
renumber the assigned names, which may cause eventual problems with
|
||||
synthesis constraints or other tools that depend on hierarchical paths
|
||||
remaining consistent.
|
||||
|
||||
Blocks that are empty may not be reported with this warning, as no
|
||||
scopes are created for empty blocks, so there is no harm in having them
|
||||
unnamed.
|
||||
|
||||
Disabled by default as this is a code-style warning; it will simulate
|
||||
correctly.
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 2
|
||||
|
||||
generate
|
||||
if (PARAM == 1) begin //<--- Warning
|
||||
end
|
||||
|
||||
Results in:
|
||||
|
||||
.. code-block::
|
||||
|
||||
%Warning-GENUNNAMED: example.v:2:9: Unnamed generate block (IEEE 1800-2017 27.6)
|
||||
|
||||
To fix this assign a label (often with the naming convention prefix of
|
||||
:code:`gen_` or :code:`g_`), for example:
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 2
|
||||
|
||||
generate
|
||||
if (PARAM == 1) begin : gen_param_1 //<--- Repaired
|
||||
end
|
||||
|
||||
Other tools with similar warnings: Verible's generate-label, "All
|
||||
generate block statements must have a label."
|
||||
|
||||
|
||||
.. option:: HIERBLOCK
|
||||
|
||||
Warns that the top module is marked as a hierarchy block by the
|
||||
@@ -876,6 +922,102 @@ List Of Warnings
|
||||
Ignoring this warning will only suppress the lint check; it will
|
||||
simulate correctly.
|
||||
|
||||
.. option:: LIFETIME
|
||||
|
||||
Error when a variable is referenced in a process that can outlive the process
|
||||
in which it was declared. This can happen when using 'fork..join_none' or
|
||||
'fork..join_any' blocks, which spawn process that can outlive their parents.
|
||||
This error occurs only when Verilator can't replace the reference with a
|
||||
reference to copy of this variable, local to the forked process. For example:
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 3
|
||||
|
||||
task foo(int local_var);
|
||||
fork
|
||||
#10 local_var++;
|
||||
#20 $display("local_var = %d", local_var);
|
||||
join_none
|
||||
endtask
|
||||
|
||||
In the example above 'local_var' exists only within scope of 'foo', once foo
|
||||
finishes, the stack frame containing 'i' gets removed. However, the process
|
||||
forked from foo continues, as it contains a delay. After 10 units of time
|
||||
pass, this process attempts to modify 'local_var'. However, this variable no
|
||||
longer exits. It can't be made local to the forked process upon spawning, because
|
||||
it's modified and can be referenced somewhere else, for example in the other
|
||||
forked process, that was delayed by 20 units of time in this example. Thus,
|
||||
there's no viable stack allocation for it.
|
||||
|
||||
In order to fix it, if the intent is not to share the variable's state outside
|
||||
of the process, then create a local copy of the variable.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 4
|
||||
|
||||
task foo(int local_var);
|
||||
fork
|
||||
#10 begin
|
||||
int forked_var = local_var;
|
||||
forked_var++;
|
||||
end
|
||||
#20 begin
|
||||
// Note that we are going to print the original value here,
|
||||
// as `forked_var`is a local copy that was initialized while
|
||||
// `foo` was still alive.
|
||||
int forked_var = local_var;
|
||||
$display("forked_var = %d", forked_var)
|
||||
end
|
||||
join_none
|
||||
endtask
|
||||
|
||||
If you need to share its state, another strategy is to ensure it's allocated
|
||||
statically:
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 1
|
||||
|
||||
int static_var;
|
||||
|
||||
task foo();
|
||||
fork
|
||||
#10 static_var++;
|
||||
#20 $display("static_var = %d", static_var);
|
||||
join_none
|
||||
endtask
|
||||
|
||||
However, if you need to be able to instantiate at runtime, the solution would be to
|
||||
wrap it in an object, since the forked process can hold a reference to that object
|
||||
and ensure that the variable stays alive this way:
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 2
|
||||
|
||||
class Wrapper;
|
||||
int m_var;
|
||||
|
||||
// Here we implicitly hold a reference to `this`
|
||||
task foo();
|
||||
fork
|
||||
#10 m_var++;
|
||||
#20 $display("this.m_var = %d", m_var);
|
||||
join_none
|
||||
endtask
|
||||
endclass
|
||||
|
||||
// Here we explicitly hold a handle to an object
|
||||
task bar(Wrapper wrapper);
|
||||
fork
|
||||
#10 wrapper.m_var++;
|
||||
#20 $display("wrapper.m_var = %d", wrapper.m_var);
|
||||
join_none
|
||||
endtask
|
||||
|
||||
.. option:: LITENDIAN
|
||||
|
||||
@@ -897,6 +1039,51 @@ List Of Warnings
|
||||
unsupported. Verilator uses only the typical delay value.
|
||||
|
||||
|
||||
.. option:: MISINDENT
|
||||
|
||||
Warns that the indentation of a statement is misleading, suggesting the
|
||||
statement is part of a previous :code:`if` or :code:`while` block while
|
||||
it is not.
|
||||
|
||||
Verilator suppresses this check when there is an inconsistent mix of
|
||||
spaces and tabs, as it cannot ensure the width of tabs. Verilator also
|
||||
ignores blocks with :code:`begin`/:code:`end`, as the :code:`end`
|
||||
visually indicates the earlier statement's end.
|
||||
|
||||
Ignoring this warning will only suppress the lint check; it will
|
||||
simulate correctly.
|
||||
|
||||
For example
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 3
|
||||
|
||||
if (something)
|
||||
statement_in_if;
|
||||
statement_not_in_if; //<--- Warning
|
||||
|
||||
Results in:
|
||||
|
||||
.. code-block::
|
||||
|
||||
%Warning-MISINDENT: example.v:3:9: Misleading indentation
|
||||
|
||||
To fix this repair the indentation to match the correct earlier
|
||||
statement, for example:
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 3
|
||||
|
||||
if (something)
|
||||
statement_in_if;
|
||||
statement_not_in_if; //<--- Repaired
|
||||
|
||||
Other tools with similar warnings: GCC -Wmisleading-indentation,
|
||||
clang-tidy readability-misleading-indentation.
|
||||
|
||||
|
||||
.. option:: MODDUP
|
||||
|
||||
.. TODO better example
|
||||
@@ -1803,11 +1990,15 @@ List Of Warnings
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
wait(0); // Blocks forever
|
||||
wait(1); // Blocks forever
|
||||
|
||||
Warns that a `wait` statement awaits a constant condition, which means it
|
||||
either blocks forever or never blocks.
|
||||
|
||||
As a special case `wait(0)` with the literal constant `0` (as opposed to
|
||||
something that elaborates to zero), does not warn, as it is presumed the
|
||||
code is making the intent clear.
|
||||
|
||||
|
||||
.. option:: WIDTH
|
||||
|
||||
|
||||
+1
-1
@@ -2123,7 +2123,7 @@ Or in a hand-written C++ wrapper:
|
||||
::
|
||||
|
||||
#ifdef TEST_VERBOSE
|
||||
cout << "Read a=" << a << endl;
|
||||
std::cout << "Read a=" << a << std::endl;
|
||||
#endif
|
||||
|
||||
A filename that should be used to check the output results is given with
|
||||
|
||||
+13
-1
@@ -56,6 +56,7 @@ Das
|
||||
Dcache
|
||||
Deadman
|
||||
Debacker
|
||||
Deepa
|
||||
Defparams
|
||||
Denio
|
||||
Deprecations
|
||||
@@ -213,6 +214,7 @@ Maupin
|
||||
Mdir
|
||||
Mednick
|
||||
Menküc
|
||||
Michail
|
||||
Michiels
|
||||
Microsystems
|
||||
Milanovic
|
||||
@@ -246,6 +248,7 @@ Olof
|
||||
Olofsson
|
||||
Oyvind
|
||||
PLI
|
||||
Palaniappan
|
||||
Patricio
|
||||
Petr
|
||||
Piechotka
|
||||
@@ -261,6 +264,7 @@ Pre
|
||||
Preprocess
|
||||
Pretet
|
||||
Pretl
|
||||
Pringlemeir
|
||||
Priyadharshini
|
||||
Pullup
|
||||
Pulver
|
||||
@@ -275,6 +279,7 @@ Renga
|
||||
Requin
|
||||
Rodionov
|
||||
Rolfe
|
||||
Rontionov
|
||||
Roodselaar
|
||||
Runtime
|
||||
Ruud
|
||||
@@ -376,6 +381,7 @@ Wfuture
|
||||
Whatson
|
||||
Wildman
|
||||
Wim
|
||||
Wmisleading
|
||||
Wno
|
||||
Wojciech
|
||||
Wolfel
|
||||
@@ -410,7 +416,9 @@ architected
|
||||
args
|
||||
arrarys
|
||||
assertOn
|
||||
astgen
|
||||
async
|
||||
atClone
|
||||
ato
|
||||
atoi
|
||||
autoconf
|
||||
@@ -649,6 +657,7 @@ ish
|
||||
isunbounded
|
||||
isunknown
|
||||
jobserver
|
||||
jwoutersymatra
|
||||
killua
|
||||
lang
|
||||
lcov
|
||||
@@ -768,6 +777,7 @@ pragmas
|
||||
pre
|
||||
precisions
|
||||
predefines
|
||||
prepareClone
|
||||
prepend
|
||||
prepended
|
||||
preprocess
|
||||
@@ -905,6 +915,7 @@ undef
|
||||
undefineall
|
||||
undriven
|
||||
ungetc
|
||||
unhandled
|
||||
uniquified
|
||||
unistd
|
||||
unlink
|
||||
@@ -925,7 +936,9 @@ vc
|
||||
vcd
|
||||
vcddiff
|
||||
vcoverage
|
||||
vdhotre
|
||||
vec
|
||||
ventana
|
||||
ver
|
||||
verFiles
|
||||
verible
|
||||
@@ -971,4 +984,3 @@ ypq
|
||||
yurivict
|
||||
zdave
|
||||
Øyvind
|
||||
|
||||
|
||||
@@ -4181,7 +4181,7 @@ if(!(isfeof=feof(xc->fh)))
|
||||
if((xc->hier.u.attr.subtype == FST_MT_SOURCESTEM)||(xc->hier.u.attr.subtype == FST_MT_SOURCEISTEM))
|
||||
{
|
||||
int sidx_skiplen_dummy = 0;
|
||||
xc->hier.u.attr.arg_from_name = fstGetVarint64((unsigned char *)xc->str_scope_nam, &sidx_skiplen_dummy);
|
||||
xc->hier.u.attr.arg_from_name = fstGetVarint64((unsigned char *)xc->str_scope_attr, &sidx_skiplen_dummy);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
+16
-2
@@ -320,7 +320,7 @@ std::string VlRNG::get_randstate() const VL_MT_UNSAFE {
|
||||
const char* const stateCharsp = reinterpret_cast<const char*>(&m_state);
|
||||
static_assert(sizeof(m_state) == 16, "");
|
||||
std::string result{"R00112233445566770011223344556677"};
|
||||
for (int i = 0; i < sizeof(m_state); ++i) {
|
||||
for (size_t i = 0; i < sizeof(m_state); ++i) {
|
||||
result[1 + i * 2] = 'a' + ((stateCharsp[i] >> 4) & 15);
|
||||
result[1 + i * 2 + 1] = 'a' + (stateCharsp[i] & 15);
|
||||
}
|
||||
@@ -332,7 +332,7 @@ void VlRNG::set_randstate(const std::string& state) VL_MT_UNSAFE {
|
||||
return;
|
||||
}
|
||||
char* const stateCharsp = reinterpret_cast<char*>(&m_state);
|
||||
for (int i = 0; i < sizeof(m_state); ++i) {
|
||||
for (size_t i = 0; i < sizeof(m_state); ++i) {
|
||||
stateCharsp[i]
|
||||
= (((state[1 + i * 2] - 'a') & 15) << 4) | ((state[1 + i * 2 + 1] - 'a') & 15);
|
||||
}
|
||||
@@ -1896,6 +1896,12 @@ std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_PURE {
|
||||
return std::string{destout, len};
|
||||
}
|
||||
|
||||
std::string VL_CVT_PACK_STR_ND(const VlQueue<std::string>& q) VL_PURE {
|
||||
std::string output;
|
||||
for (const std::string& s : q) output += s;
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE {
|
||||
std::string lstring = lhs;
|
||||
const int32_t rhs_s = rhs; // To signed value
|
||||
@@ -2602,6 +2608,14 @@ VerilatedVirtualBase* VerilatedContext::threadPoolp() {
|
||||
return m_threadPool.get();
|
||||
}
|
||||
|
||||
void VerilatedContext::prepareClone() { delete m_threadPool.release(); }
|
||||
|
||||
VerilatedVirtualBase* VerilatedContext::threadPoolpOnClone() {
|
||||
if (VL_UNLIKELY(m_threadPool)) m_threadPool.release();
|
||||
m_threadPool = std::unique_ptr<VlThreadPool>(new VlThreadPool{this, m_threads - 1});
|
||||
return m_threadPool.get();
|
||||
}
|
||||
|
||||
VerilatedVirtualBase*
|
||||
VerilatedContext::enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&)) {
|
||||
if (!m_executionProfiler) m_executionProfiler.reset(construct(*this));
|
||||
|
||||
@@ -568,6 +568,8 @@ public:
|
||||
void addModel(VerilatedModel*);
|
||||
|
||||
VerilatedVirtualBase* threadPoolp();
|
||||
void prepareClone();
|
||||
VerilatedVirtualBase* threadPoolpOnClone();
|
||||
VerilatedVirtualBase*
|
||||
enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&));
|
||||
|
||||
|
||||
@@ -210,8 +210,8 @@ private:
|
||||
|
||||
std::string result = prefix + "*" + suffix;
|
||||
|
||||
// cout << "\nch pre="<<prefix<<" s="<<suffix<<"\nch a="<<old<<"\nch b="<<add
|
||||
// <<"\ncho="<<result<<endl;
|
||||
// std::cout << "\nch pre=" << prefix << " s=" << suffix << "\nch a="
|
||||
// << old << "\nch b=" << add << "\ncho=" << result << std::endl;
|
||||
return result;
|
||||
}
|
||||
bool itemMatchesString(VerilatedCovImpItem* itemp, const std::string& match)
|
||||
@@ -316,7 +316,7 @@ public:
|
||||
valps[2] = page_default.c_str();
|
||||
|
||||
// Keys -> strings
|
||||
std::string keys[VerilatedCovConst::MAX_KEYS];
|
||||
std::array<std::string, VerilatedCovConst::MAX_KEYS> keys;
|
||||
for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
|
||||
if (ckeyps[i] && ckeyps[i][0]) keys[i] = ckeyps[i];
|
||||
}
|
||||
@@ -337,7 +337,7 @@ public:
|
||||
const std::string key = keys[i];
|
||||
if (!keys[i].empty()) {
|
||||
const std::string val = valps[i];
|
||||
// cout<<" "<<__FUNCTION__<<" "<<key<<" = "<<val<<endl;
|
||||
// std::cout << " " << __FUNCTION__ << " " << key << " = " << val << std::endl;
|
||||
m_insertp->m_keys[addKeynum] = valueIndex(key);
|
||||
m_insertp->m_vals[addKeynum] = valueIndex(val);
|
||||
++addKeynum;
|
||||
|
||||
@@ -1526,6 +1526,28 @@ static inline WDataOutP VL_STREAML_WWI(int lbits, WDataOutP owp, WDataInP const
|
||||
return owp;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void VL_ASSIGN_DYN_Q(VlQueue<T>& q, int elem_size, int lbits, QData from) {
|
||||
const int size = (lbits + elem_size - 1) / elem_size;
|
||||
q.renew(size);
|
||||
const QData mask = VL_MASK_Q(elem_size);
|
||||
for (int i = 0; i < size; ++i) q.at(i) = (T)((from >> (i * elem_size)) & mask);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline IData VL_DYN_TO_I(const VlQueue<T>& q, int elem_size) {
|
||||
IData ret = 0;
|
||||
for (int i = 0; i < q.size(); ++i) ret |= q.at(i) << (i * elem_size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline QData VL_DYN_TO_Q(const VlQueue<T>& q, int elem_size) {
|
||||
QData ret = 0;
|
||||
for (int i = 0; i < q.size(); ++i) ret |= q.at(i) << (i * elem_size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Because concats are common and wide, it's valuable to always have a clean output.
|
||||
// Thus we specify inputs must be clean, so we don't need to clean the output.
|
||||
// Note the bit shifts are always constants, so the adds in these constify out.
|
||||
@@ -2196,6 +2218,7 @@ extern IData VL_DIST_UNIFORM(IData& seedr, IData ustart, IData uend) VL_MT_SAFE;
|
||||
// Conversion functions
|
||||
|
||||
extern std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_PURE;
|
||||
extern std::string VL_CVT_PACK_STR_ND(const VlQueue<std::string>& q) VL_PURE;
|
||||
inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE {
|
||||
VlWide<VL_WQ_WORDS_E> lw;
|
||||
VL_SET_WQ(lw, lhs);
|
||||
|
||||
@@ -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
|
||||
@@ -86,36 +86,6 @@ public:
|
||||
#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
|
||||
// cleared, as we assume that either the coroutine has finished and deleted itself, or, if it got
|
||||
@@ -132,6 +102,8 @@ class VlCoroutineHandle final {
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
// Construct
|
||||
// non-explicit:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
VlCoroutineHandle(VlProcessRef process)
|
||||
: m_coro{nullptr}
|
||||
, m_process{process} {
|
||||
@@ -144,9 +116,11 @@ public:
|
||||
if (m_process) m_process->state(VlProcess::WAITING);
|
||||
}
|
||||
// Move the handle, leaving a nullptr
|
||||
// non-explicit:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
VlCoroutineHandle(VlCoroutineHandle&& moved)
|
||||
: m_coro{std::exchange(moved.m_coro, nullptr)}
|
||||
, m_process{moved.m_process}
|
||||
, m_process{std::exchange(moved.m_process, nullptr)}
|
||||
, m_fileline{moved.m_fileline} {}
|
||||
// Destroy if the handle isn't null
|
||||
~VlCoroutineHandle() {
|
||||
@@ -163,6 +137,8 @@ public:
|
||||
// Move the handle, leaving a null handle
|
||||
auto& operator=(VlCoroutineHandle&& moved) {
|
||||
m_coro = std::exchange(moved.m_coro, nullptr);
|
||||
m_process = std::exchange(moved.m_process, nullptr);
|
||||
m_fileline = moved.m_fileline;
|
||||
return *this;
|
||||
}
|
||||
// Resume the coroutine if the handle isn't null and the process isn't killed
|
||||
|
||||
@@ -73,6 +73,36 @@ extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
|
||||
#define VL_OUT(name, msb, lsb) IData name ///< Declare output signal, 17-32 bits
|
||||
#define VL_OUTW(name, msb, lsb, words) VlWide<words> name ///< Declare output signal, 65+ bits
|
||||
|
||||
//===================================================================
|
||||
// VlProcess stores metadata of running processes
|
||||
|
||||
class VlProcess final {
|
||||
// MEMBERS
|
||||
int m_state; // Current state of the process
|
||||
|
||||
public:
|
||||
// TYPES
|
||||
enum : int { // Type int for compatibility with $c
|
||||
FINISHED = 0,
|
||||
RUNNING = 1,
|
||||
WAITING = 2,
|
||||
SUSPENDED = 3,
|
||||
KILLED = 4,
|
||||
};
|
||||
|
||||
// CONSTRUCTORS
|
||||
VlProcess()
|
||||
: m_state{RUNNING} {}
|
||||
|
||||
// METHODS
|
||||
int state() { return m_state; }
|
||||
void state(int s) { m_state = s; }
|
||||
};
|
||||
|
||||
using VlProcessRef = std::shared_ptr<VlProcess>;
|
||||
|
||||
inline std::string VL_TO_STRING(const VlProcessRef& p) { return std::string("process"); }
|
||||
|
||||
//===================================================================
|
||||
// Activity trigger vector
|
||||
|
||||
@@ -487,7 +517,7 @@ public:
|
||||
template <typename Func>
|
||||
VlQueue unique(Func with_func) const {
|
||||
VlQueue out;
|
||||
std::set<T_Value> saw;
|
||||
std::set<decltype(with_func(0, m_deque[0]))> saw;
|
||||
for (const auto& i : m_deque) {
|
||||
const auto i_mapped = with_func(0, i);
|
||||
const auto it = saw.find(i_mapped);
|
||||
@@ -516,7 +546,7 @@ public:
|
||||
VlQueue<IData> unique_index(Func with_func) const {
|
||||
VlQueue<IData> out;
|
||||
IData index = 0;
|
||||
std::unordered_set<T_Value> saw;
|
||||
std::set<decltype(with_func(0, m_deque[0]))> saw;
|
||||
for (const auto& i : m_deque) {
|
||||
const auto i_mapped = with_func(index, i);
|
||||
auto it = saw.find(i_mapped);
|
||||
@@ -830,6 +860,22 @@ public:
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> unique(Func with_func) const {
|
||||
VlQueue<T_Value> out;
|
||||
T_Key default_key;
|
||||
using WithType = decltype(with_func(m_map.begin()->first, m_map.begin()->second));
|
||||
std::set<WithType> saw;
|
||||
for (const auto& i : m_map) {
|
||||
const auto i_mapped = with_func(default_key, i.second);
|
||||
const auto it = saw.find(i_mapped);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i_mapped);
|
||||
out.push_back(i.second);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
VlQueue<T_Key> unique_index() const {
|
||||
VlQueue<T_Key> out;
|
||||
std::set<T_Key> saw;
|
||||
@@ -843,6 +889,21 @@ public:
|
||||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Key> unique_index(Func with_func) const {
|
||||
VlQueue<T_Key> out;
|
||||
using WithType = decltype(with_func(m_map.begin()->first, m_map.begin()->second));
|
||||
std::set<WithType> saw;
|
||||
for (const auto& i : m_map) {
|
||||
const auto i_mapped = with_func(i.first, i.second);
|
||||
auto it = saw.find(i_mapped);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i_mapped);
|
||||
out.push_back(i.first);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> find(Func with_func) const {
|
||||
VlQueue<T_Value> out;
|
||||
for (const auto& i : m_map)
|
||||
@@ -903,6 +964,16 @@ public:
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> min(Func with_func) const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::min_element(
|
||||
m_map.begin(), m_map.end(),
|
||||
[&with_func](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return with_func(a.first, a.second) < with_func(b.first, b.second);
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
VlQueue<T_Value> max() const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::max_element(
|
||||
@@ -912,6 +983,16 @@ public:
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> max(Func with_func) const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::max_element(
|
||||
m_map.begin(), m_map.end(),
|
||||
[&with_func](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return with_func(a.first, a.second) < with_func(b.first, b.second);
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
|
||||
T_Value r_sum() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
@@ -1457,8 +1538,14 @@ public:
|
||||
// For 'if (ptr)...'
|
||||
operator bool() const { return m_objp; }
|
||||
// In SV A == B iff both are handles to the same object (IEEE 1800-2017 8.4)
|
||||
bool operator==(const VlClassRef& rhs) const { return m_objp == rhs.m_objp; };
|
||||
bool operator!=(const VlClassRef& rhs) const { return m_objp != rhs.m_objp; };
|
||||
template <typename T_OtherClass>
|
||||
bool operator==(const VlClassRef<T_OtherClass>& rhs) const {
|
||||
return m_objp == rhs.m_objp;
|
||||
};
|
||||
template <typename T_OtherClass>
|
||||
bool operator!=(const VlClassRef<T_OtherClass>& rhs) const {
|
||||
return m_objp != rhs.m_objp;
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
|
||||
@@ -175,7 +175,7 @@ public:
|
||||
VerilatedVpioVarBase(const VerilatedVar* varp, const VerilatedScope* scopep)
|
||||
: m_varp{varp}
|
||||
, m_scopep{scopep}
|
||||
, m_fullname{std::string{m_scopep->name()} + '.' + name()} {}
|
||||
, m_fullname{std::string{m_scopep->name()} + '.' + m_varp->name()} {}
|
||||
explicit VerilatedVpioVarBase(const VerilatedVpioVarBase* varp) {
|
||||
if (varp) {
|
||||
m_varp = varp->m_varp;
|
||||
@@ -2307,7 +2307,7 @@ PLI_INT32 vpi_flush(void) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
VL_VPI_ERROR_RESET_();
|
||||
Verilated::runFlushCallbacks();
|
||||
return 0;
|
||||
return 0; // Gcc coverage bug // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_mcd_flush(PLI_UINT32 mcd) {
|
||||
|
||||
+37
-16
@@ -94,20 +94,6 @@
|
||||
// Function requires a capability inbound (-fthread-safety)
|
||||
#define VL_CAPABILITY(x) \
|
||||
VL_CLANG_ATTR(capability(x))
|
||||
// Function requires not having a capability inbound (-fthread-safety)
|
||||
#define VL_REQUIRES(x) \
|
||||
VL_CLANG_ATTR(annotate("REQUIRES")) \
|
||||
VL_CLANG_ATTR(requires_capability(x))
|
||||
// Name of capability/lock (-fthread-safety)
|
||||
#define VL_GUARDED_BY(x) \
|
||||
VL_CLANG_ATTR(annotate("GUARDED_BY")) \
|
||||
VL_CLANG_ATTR(guarded_by(x))
|
||||
// The data that the annotated pointer points to is protected by the given capability.
|
||||
// The pointer itself is not protected.
|
||||
// Allowed on: pointer data member. (-fthread-safety)
|
||||
#define VL_PT_GUARDED_BY(x) \
|
||||
VL_CLANG_ATTR(annotate("PT_GUARDED_BY")) \
|
||||
VL_CLANG_ATTR(pt_guarded_by(x))
|
||||
// Name of mutex protecting this variable (-fthread-safety)
|
||||
#define VL_EXCLUDES(x) \
|
||||
VL_CLANG_ATTR(annotate("EXCLUDES")) \
|
||||
@@ -119,6 +105,36 @@
|
||||
// Allowed on: function, method. (-fthread-safety)
|
||||
#define VL_RETURN_CAPABILITY(x) \
|
||||
VL_CLANG_ATTR(lock_returned(x))
|
||||
// Assert that capability is already held.
|
||||
// Allowed on: function, method. (-fthread-safety)
|
||||
#define VL_ASSERT_CAPABILITY(x) \
|
||||
VL_CLANG_ATTR(assert_capability(x))
|
||||
|
||||
// Require mutex locks only in code units which work with enabled multi-threading.
|
||||
#if !defined(VL_MT_DISABLED_CODE_UNIT)
|
||||
// Function requires not having a capability inbound (-fthread-safety)
|
||||
# define VL_REQUIRES(x) \
|
||||
VL_CLANG_ATTR(annotate("REQUIRES")) \
|
||||
VL_CLANG_ATTR(requires_capability(x))
|
||||
// Name of capability/lock (-fthread-safety)
|
||||
# define VL_GUARDED_BY(x) \
|
||||
VL_CLANG_ATTR(annotate("GUARDED_BY")) \
|
||||
VL_CLANG_ATTR(guarded_by(x))
|
||||
// The data that the annotated pointer points to is protected by the given capability.
|
||||
// The pointer itself is not protected.
|
||||
// Allowed on: pointer data member. (-fthread-safety)
|
||||
# define VL_PT_GUARDED_BY(x) \
|
||||
VL_CLANG_ATTR(annotate("PT_GUARDED_BY")) \
|
||||
VL_CLANG_ATTR(pt_guarded_by(x))
|
||||
#else
|
||||
// Keep annotations for clang_check_attributes
|
||||
# define VL_REQUIRES(x) \
|
||||
VL_CLANG_ATTR(annotate("REQUIRES"))
|
||||
# define VL_GUARDED_BY(x) \
|
||||
VL_CLANG_ATTR(annotate("GUARDED_BY"))
|
||||
# define VL_PT_GUARDED_BY(x) \
|
||||
VL_CLANG_ATTR(annotate("PT_GUARDED_BY"))
|
||||
#endif
|
||||
|
||||
// Defaults for unsupported compiler features
|
||||
#ifndef VL_ATTR_ALWINLINE
|
||||
@@ -426,6 +442,11 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
||||
Type(const Type& other) = delete; \
|
||||
Type& operator=(const Type&) = delete
|
||||
|
||||
// Declare a class as unmovable; put after a private:
|
||||
#define VL_UNMOVABLE(Type) \
|
||||
Type(Type&& other) = delete; \
|
||||
Type& operator=(Type&&) = delete
|
||||
|
||||
//=========================================================================
|
||||
// Verilated function size macros
|
||||
|
||||
@@ -573,8 +594,8 @@ static inline double VL_ROUND(double n) {
|
||||
//=========================================================================
|
||||
// Stringify macros
|
||||
|
||||
#define VL_STRINGIFY(x) VL_STRINGIFY2(x)
|
||||
#define VL_STRINGIFY2(x) #x
|
||||
#define VL_STRINGIFY(...) VL_STRINGIFY2(__VA_ARGS__)
|
||||
#define VL_STRINGIFY2(...) #__VA_ARGS__
|
||||
|
||||
//=========================================================================
|
||||
// Offset of field in type
|
||||
|
||||
+267
-50
@@ -10,16 +10,36 @@ import argparse
|
||||
import os
|
||||
import sys
|
||||
import shlex
|
||||
from typing import Callable, Iterable, Optional, Union
|
||||
from typing import Callable, Iterable, Optional, Union, TYPE_CHECKING
|
||||
import dataclasses
|
||||
from dataclasses import dataclass
|
||||
import enum
|
||||
from enum import Enum
|
||||
import multiprocessing
|
||||
import re
|
||||
import tempfile
|
||||
|
||||
import clang.cindex
|
||||
from clang.cindex import CursorKind, Index, TranslationUnitSaveError, TranslationUnitLoadError
|
||||
from clang.cindex import (
|
||||
Index,
|
||||
TranslationUnitSaveError,
|
||||
TranslationUnitLoadError,
|
||||
CompilationDatabase,
|
||||
)
|
||||
|
||||
if not TYPE_CHECKING:
|
||||
from clang.cindex import CursorKind
|
||||
else:
|
||||
# Workaround for missing support for members defined out-of-class in Pylance:
|
||||
# https://github.com/microsoft/pylance-release/issues/2365#issuecomment-1035803067
|
||||
|
||||
class CursorKindMeta(type):
|
||||
|
||||
def __getattr__(cls, name: str) -> clang.cindex.CursorKind:
|
||||
return getattr(clang.cindex.CursorKind, name)
|
||||
|
||||
class CursorKind(clang.cindex.CursorKind, metaclass=CursorKindMeta):
|
||||
pass
|
||||
|
||||
|
||||
def fully_qualified_name(node):
|
||||
@@ -60,6 +80,7 @@ class VlAnnotations:
|
||||
stable_tree: bool = False
|
||||
mt_safe_postinit: bool = False
|
||||
mt_unsafe: bool = False
|
||||
mt_disabled: bool = False
|
||||
mt_unsafe_one: bool = False
|
||||
pure: bool = False
|
||||
guarded: bool = False
|
||||
@@ -81,7 +102,7 @@ class VlAnnotations:
|
||||
return self.stable_tree or self.mt_start
|
||||
|
||||
def is_mt_unsafe_call(self):
|
||||
return self.mt_unsafe or self.mt_unsafe_one
|
||||
return self.mt_unsafe or self.mt_unsafe_one or self.mt_disabled
|
||||
|
||||
def is_mt_safe_call(self):
|
||||
return (not self.is_mt_unsafe_call()
|
||||
@@ -131,6 +152,8 @@ class VlAnnotations:
|
||||
result.mt_unsafe = True
|
||||
elif node.displayname == "MT_UNSAFE_ONE":
|
||||
result.mt_unsafe_one = True
|
||||
elif node.displayname == "MT_DISABLED":
|
||||
result.mt_disabled = True
|
||||
elif node.displayname == "PURE":
|
||||
result.pure = True
|
||||
elif node.displayname in ["ACQUIRE", "ACQUIRE_SHARED"]:
|
||||
@@ -203,6 +226,19 @@ class FunctionInfo:
|
||||
def copy(self, /, **changes):
|
||||
return dataclasses.replace(self, **changes)
|
||||
|
||||
@staticmethod
|
||||
def from_decl_file_line_and_refd_node(file: str, line: int,
|
||||
refd: clang.cindex.Cursor,
|
||||
annotations: VlAnnotations):
|
||||
file = os.path.abspath(file)
|
||||
refd = refd.canonical
|
||||
assert refd is not None
|
||||
name_parts = fully_qualified_name(refd)
|
||||
usr = refd.get_usr()
|
||||
ftype = FunctionType.from_node(refd)
|
||||
|
||||
return FunctionInfo(name_parts, usr, file, line, annotations, ftype)
|
||||
|
||||
@staticmethod
|
||||
def from_node(node: clang.cindex.Cursor,
|
||||
refd: Optional[clang.cindex.Cursor] = None,
|
||||
@@ -228,6 +264,7 @@ class DiagnosticKind(Enum):
|
||||
NON_PURE_CALL_IN_PURE_CTX = enum.auto()
|
||||
NON_MT_SAFE_CALL_IN_MT_SAFE_CTX = enum.auto()
|
||||
NON_STABLE_TREE_CALL_IN_STABLE_TREE_CTX = enum.auto()
|
||||
MISSING_MT_DISABLED_ANNOTATION = enum.auto()
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.value < other.value
|
||||
@@ -265,35 +302,142 @@ class CallAnnotationsValidator:
|
||||
|
||||
self._index = Index.create()
|
||||
|
||||
self._processed_headers: set[str] = set()
|
||||
# Map key represents translation unit initial defines
|
||||
# (from command line and source's lines before any include)
|
||||
self._processed_headers: dict[str, set[str]] = {}
|
||||
self._external_decls: dict[str, set[tuple[str, int]]] = {}
|
||||
|
||||
# Current context
|
||||
self._main_source_file: str = ""
|
||||
self._defines: dict[str, str] = {}
|
||||
self._call_location: Optional[FunctionInfo] = None
|
||||
self._caller: Optional[FunctionInfo] = None
|
||||
self._level: int = 0
|
||||
self._constructor_context: list[clang.cindex.Cursor] = []
|
||||
self._level: int = 0
|
||||
|
||||
def is_mt_disabled_code_unit(self):
|
||||
return "VL_MT_DISABLED_CODE_UNIT" in self._defines
|
||||
|
||||
def is_constructor_context(self):
|
||||
return len(self._constructor_context) > 0
|
||||
|
||||
# Parses all lines in a form: `#define KEY VALUE` located before any `#include` line.
|
||||
# The parsing is very simple, there is no support for line breaks, etc.
|
||||
@staticmethod
|
||||
def parse_initial_defines(source_file: str) -> dict[str, str]:
|
||||
defs: dict[str, str] = {}
|
||||
with open(source_file, "r", encoding="utf-8") as file:
|
||||
for line in file:
|
||||
line = line.strip()
|
||||
match = re.fullmatch(
|
||||
r"^#\s*(define\s+(\w+)(?:\s+(.*))?|include\s+.*)$", line)
|
||||
if match:
|
||||
if match.group(1).startswith("define"):
|
||||
key = match.group(2)
|
||||
value = match.groups("1")[2]
|
||||
defs[key] = value
|
||||
elif match.group(1).startswith("include"):
|
||||
break
|
||||
return defs
|
||||
|
||||
@staticmethod
|
||||
def filter_out_unsupported_compiler_args(
|
||||
args: list[str]) -> tuple[list[str], dict[str, str]]:
|
||||
filtered_args = []
|
||||
defines = {}
|
||||
args_iter = iter(args)
|
||||
try:
|
||||
while arg := next(args_iter):
|
||||
# Skip positional arguments (input file name).
|
||||
if not arg.startswith("-") and (arg.endswith(".cpp")
|
||||
or arg.endswith(".c")
|
||||
or arg.endswith(".h")):
|
||||
continue
|
||||
|
||||
# Skipped options with separate value argument.
|
||||
if arg in ["-o", "-T", "-MT", "-MQ", "-MF"
|
||||
"-L"]:
|
||||
next(args_iter)
|
||||
continue
|
||||
|
||||
# Skipped options without separate value argument.
|
||||
if arg == "-c" or arg.startswith("-W") or arg.startswith("-L"):
|
||||
continue
|
||||
|
||||
# Preserved options with separate value argument.
|
||||
if arg in [
|
||||
"-x"
|
||||
"-Xclang", "-I", "-isystem", "-iquote", "-include",
|
||||
"-include-pch"
|
||||
]:
|
||||
filtered_args += [arg, next(args_iter)]
|
||||
continue
|
||||
|
||||
kv_str = None
|
||||
d_or_u = None
|
||||
# Preserve define/undefine with separate value argument.
|
||||
if arg in ["-D", "-U"]:
|
||||
filtered_args.append(arg)
|
||||
d_or_u = arg[1]
|
||||
kv_str = next(args_iter)
|
||||
filtered_args.append(kv_str)
|
||||
# Preserve define/undefine without separate value argument.
|
||||
elif arg[0:2] in ["-D", "-U"]:
|
||||
filtered_args.append(arg)
|
||||
kv_str = arg[2:]
|
||||
d_or_u = arg[1]
|
||||
# Preserve everything else.
|
||||
else:
|
||||
filtered_args.append(arg)
|
||||
continue
|
||||
|
||||
# Keep track of defines for class' internal purposes.
|
||||
key_value = kv_str.split("=", 1)
|
||||
key = key_value[0]
|
||||
val = "1" if len(key_value) == 1 else key_value[1]
|
||||
|
||||
if d_or_u == "D":
|
||||
defines[key] = val
|
||||
elif d_or_u == "U" and key in defines:
|
||||
del defines[key]
|
||||
|
||||
except StopIteration:
|
||||
pass
|
||||
|
||||
return (filtered_args, defines)
|
||||
|
||||
def compile_and_analyze_file(self, source_file: str,
|
||||
compiler_args: list[str],
|
||||
build_dir: Optional[str]):
|
||||
filename = os.path.abspath(source_file)
|
||||
initial_cwd = "."
|
||||
|
||||
filtered_args, defines = self.filter_out_unsupported_compiler_args(
|
||||
compiler_args)
|
||||
defines.update(self.parse_initial_defines(source_file))
|
||||
|
||||
if build_dir:
|
||||
initial_cwd = os.getcwd()
|
||||
os.chdir(build_dir)
|
||||
translation_unit = self._index.parse(filename, compiler_args)
|
||||
has_errors = False
|
||||
for diag in translation_unit.diagnostics:
|
||||
if diag.severity > clang.cindex.Diagnostic.Error:
|
||||
has_errors = True
|
||||
if translation_unit and not has_errors:
|
||||
try:
|
||||
translation_unit = self._index.parse(filename, filtered_args)
|
||||
except TranslationUnitLoadError:
|
||||
translation_unit = None
|
||||
errors = []
|
||||
if translation_unit:
|
||||
for diag in translation_unit.diagnostics:
|
||||
if diag.severity >= clang.cindex.Diagnostic.Error:
|
||||
errors.append(str(diag))
|
||||
if translation_unit and len(errors) == 0:
|
||||
self._defines = defines
|
||||
self._main_source_file = filename
|
||||
self.process_translation_unit(translation_unit)
|
||||
self._main_source_file = ""
|
||||
self._defines = {}
|
||||
else:
|
||||
print(f"%Error: parsing failed: {filename}", file=sys.stderr)
|
||||
for error in errors:
|
||||
print(f" {error}", file=sys.stderr)
|
||||
if build_dir:
|
||||
os.chdir(initial_cwd)
|
||||
|
||||
@@ -563,6 +707,18 @@ class CallAnnotationsValidator:
|
||||
f" from: {node.location.file.name}:{node.location.line}")
|
||||
return True
|
||||
|
||||
def process_function_declaration(self, node: clang.cindex.Cursor):
|
||||
# Ignore declarations in main .cpp file
|
||||
if node.location.file.name != self._main_source_file:
|
||||
children = list(node.get_children())
|
||||
annotations = VlAnnotations.from_nodes_list(children)
|
||||
if not annotations.mt_disabled:
|
||||
self._external_decls.setdefault(node.get_usr(), set()).add(
|
||||
(str(node.location.file.name), int(node.location.line)))
|
||||
return self.iterate_children(children, self.dispatch_node)
|
||||
|
||||
return self.iterate_children(node.get_children(), self.dispatch_node)
|
||||
|
||||
# Definition handling
|
||||
|
||||
def dispatch_node_inside_definition(self, node: clang.cindex.Cursor):
|
||||
@@ -593,6 +749,18 @@ class CallAnnotationsValidator:
|
||||
assert refd is not None
|
||||
|
||||
def_annotations = VlAnnotations.from_nodes_list(node_children)
|
||||
# Implicitly mark definitions in VL_MT_DISABLED_CODE_UNIT .cpp files as
|
||||
# VL_MT_DISABLED. Existence of the annotation on declarations in .h
|
||||
# files is verified below.
|
||||
# Also sets VL_REQUIRES, as this annotation is added together with
|
||||
# explicit VL_MT_DISABLED.
|
||||
if self.is_mt_disabled_code_unit():
|
||||
if node.location.file.name == self._main_source_file:
|
||||
annotations.mt_disabled = True
|
||||
annotations.requires = True
|
||||
if refd.location.file.name == self._main_source_file:
|
||||
def_annotations.mt_disabled = True
|
||||
def_annotations.requires = True
|
||||
|
||||
if not (def_annotations.is_empty() or def_annotations == annotations):
|
||||
# Use definition's annotations for the diagnostic
|
||||
@@ -605,12 +773,26 @@ class CallAnnotationsValidator:
|
||||
DiagnosticKind.ANNOTATIONS_DEF_DECL_MISMATCH)
|
||||
|
||||
# Use concatenation of definition and declaration annotations
|
||||
# for callees validation.
|
||||
# for calls validation.
|
||||
self._caller = FunctionInfo.from_node(node, refd,
|
||||
def_annotations | annotations)
|
||||
prev_call_location = self._call_location
|
||||
self._call_location = self._caller
|
||||
|
||||
if self.is_mt_disabled_code_unit():
|
||||
# Report declarations of this functions that don't have MT_DISABLED annotation
|
||||
# and are located in headers.
|
||||
if node.location.file.name == self._main_source_file:
|
||||
usr = node.get_usr()
|
||||
declarations = self._external_decls.get(usr, set())
|
||||
for file, line in declarations:
|
||||
self.emit_diagnostic(
|
||||
FunctionInfo.from_decl_file_line_and_refd_node(
|
||||
file, line, refd, def_annotations),
|
||||
DiagnosticKind.MISSING_MT_DISABLED_ANNOTATION)
|
||||
if declarations:
|
||||
del self._external_decls[usr]
|
||||
|
||||
self.iterate_children(node_children,
|
||||
self.dispatch_node_inside_definition)
|
||||
|
||||
@@ -622,34 +804,36 @@ class CallAnnotationsValidator:
|
||||
# Nodes not located inside definition
|
||||
|
||||
def dispatch_node(self, node: clang.cindex.Cursor):
|
||||
if node.is_definition() and node.kind in [
|
||||
if node.kind in [
|
||||
CursorKind.CXX_METHOD, CursorKind.FUNCTION_DECL,
|
||||
CursorKind.CONSTRUCTOR, CursorKind.CONVERSION_FUNCTION
|
||||
]:
|
||||
return self.process_function_definition(node)
|
||||
if node.is_definition() and node.kind in [
|
||||
CursorKind.NAMESPACE, CursorKind.STRUCT_DECL,
|
||||
CursorKind.UNION_DECL, CursorKind.CLASS_DECL
|
||||
]:
|
||||
return self.iterate_children(node.get_children(),
|
||||
self.dispatch_node)
|
||||
if node.is_definition():
|
||||
return self.process_function_definition(node)
|
||||
# else:
|
||||
return self.process_function_declaration(node)
|
||||
|
||||
return self.iterate_children(node.get_children(), self.dispatch_node)
|
||||
|
||||
def process_translation_unit(
|
||||
self, translation_unit: clang.cindex.TranslationUnit):
|
||||
self._level += 1
|
||||
kv_defines = sorted([f"{k}={v}" for k, v in self._defines.items()])
|
||||
concat_defines = '\n'.join(kv_defines)
|
||||
# List of headers already processed in a TU with specified set of defines.
|
||||
tu_processed_headers = self._processed_headers.setdefault(
|
||||
concat_defines, set())
|
||||
for child in translation_unit.cursor.get_children():
|
||||
if self._is_ignored_top_level(child):
|
||||
continue
|
||||
if self._processed_headers:
|
||||
if tu_processed_headers:
|
||||
filename = os.path.abspath(child.location.file.name)
|
||||
if filename in self._processed_headers:
|
||||
if filename in tu_processed_headers:
|
||||
continue
|
||||
self.dispatch_node(child)
|
||||
self._level -= 1
|
||||
|
||||
self._processed_headers.update([
|
||||
tu_processed_headers.update([
|
||||
os.path.abspath(str(hdr.source))
|
||||
for hdr in translation_unit.get_includes()
|
||||
])
|
||||
@@ -711,34 +895,40 @@ def get_filter_funcs(verilator_root: str):
|
||||
|
||||
|
||||
def precompile_header(compile_command: CompileCommand, tmp_dir: str) -> str:
|
||||
initial_cwd = os.getcwd()
|
||||
errors = []
|
||||
try:
|
||||
initial_cwd = os.getcwd()
|
||||
os.chdir(compile_command.directory)
|
||||
|
||||
index = Index.create()
|
||||
translation_unit = index.parse(compile_command.filename,
|
||||
compile_command.args)
|
||||
for diag in translation_unit.diagnostics:
|
||||
if diag.severity > clang.cindex.Diagnostic.Error:
|
||||
pch_file = None
|
||||
break
|
||||
else:
|
||||
if diag.severity >= clang.cindex.Diagnostic.Error:
|
||||
errors.append(str(diag))
|
||||
|
||||
if len(errors) == 0:
|
||||
pch_file = os.path.join(
|
||||
tmp_dir,
|
||||
f"{compile_command.refid:02}_{os.path.basename(compile_command.filename)}.pch"
|
||||
)
|
||||
translation_unit.save(pch_file)
|
||||
|
||||
if pch_file:
|
||||
return pch_file
|
||||
|
||||
except (TranslationUnitSaveError, TranslationUnitLoadError,
|
||||
OSError) as exception:
|
||||
print(f"%Warning: {exception}", file=sys.stderr)
|
||||
|
||||
finally:
|
||||
os.chdir(initial_cwd)
|
||||
|
||||
if pch_file:
|
||||
return pch_file
|
||||
|
||||
except (TranslationUnitSaveError, TranslationUnitLoadError, OSError):
|
||||
pass
|
||||
|
||||
print(
|
||||
f"%Warning: Precompiling failed, skipping: {compile_command.filename}")
|
||||
f"%Warning: Precompilation failed, skipping: {compile_command.filename}",
|
||||
file=sys.stderr)
|
||||
for error in errors:
|
||||
print(f" {error}", file=sys.stderr)
|
||||
return ""
|
||||
|
||||
|
||||
@@ -761,7 +951,7 @@ def run_analysis(ccl: Iterable[CompileCommand], pccl: Iterable[CompileCommand],
|
||||
is_ignored_def, is_ignored_call)
|
||||
for compile_command in ccl:
|
||||
cav.compile_and_analyze_file(compile_command.filename,
|
||||
compile_command.args + extra_args,
|
||||
extra_args + compile_command.args,
|
||||
compile_command.directory)
|
||||
|
||||
|
||||
@@ -881,6 +1071,10 @@ class TopDownSummaryPrinter():
|
||||
name += "is pure but calls non-pure function(s)"
|
||||
elif func.reason == DiagnosticKind.NON_STABLE_TREE_CALL_IN_STABLE_TREE_CTX:
|
||||
name += "is stable_tree but calls non-stable_tree or non-mtsafe"
|
||||
elif func.reason == DiagnosticKind.MISSING_MT_DISABLED_ANNOTATION:
|
||||
name += ("defined in a file marked as " +
|
||||
"VL_MT_DISABLED_CODE_UNIT has declaration(s) " +
|
||||
"without VL_MT_DISABLED annotation")
|
||||
else:
|
||||
name += "for unknown reason (please add description)"
|
||||
|
||||
@@ -947,10 +1141,15 @@ def main():
|
||||
type=int,
|
||||
default=0,
|
||||
help="Number of parallel jobs to use.")
|
||||
parser.add_argument(
|
||||
"--compile-commands-dir",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Path to directory containing compile_commands.json.")
|
||||
parser.add_argument("--cxxflags",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Flags passed to clang++.")
|
||||
help="Extra flags passed to clang++.")
|
||||
parser.add_argument(
|
||||
"--compilation-root",
|
||||
type=str,
|
||||
@@ -975,34 +1174,52 @@ def main():
|
||||
cmdline.compilation_root = cmdline.verilator_root
|
||||
|
||||
verilator_root = os.path.abspath(cmdline.verilator_root)
|
||||
compilation_root = os.path.abspath(cmdline.compilation_root)
|
||||
default_compilation_root = os.path.abspath(cmdline.compilation_root)
|
||||
|
||||
compdb: Optional[CompilationDatabase] = None
|
||||
if cmdline.compile_commands_dir:
|
||||
compdb = CompilationDatabase.fromDirectory(
|
||||
cmdline.compile_commands_dir)
|
||||
|
||||
default_cxx_flags = [
|
||||
f"-I{verilator_root}/src",
|
||||
f"-I{verilator_root}/include",
|
||||
f"-I{verilator_root}/src/obj_opt",
|
||||
"-fcoroutines-ts",
|
||||
]
|
||||
if cmdline.cxxflags is not None:
|
||||
cxxflags = shlex.split(cmdline.cxxflags)
|
||||
common_cxxflags = shlex.split(cmdline.cxxflags)
|
||||
else:
|
||||
cxxflags = default_cxx_flags
|
||||
common_cxxflags = []
|
||||
|
||||
precompile_commands_list = []
|
||||
|
||||
if cmdline.precompile:
|
||||
hdr_cxxflags = ['-xc++-header'] + cxxflags
|
||||
hdr_cxxflags = ['-xc++-header'] + common_cxxflags
|
||||
for refid, file in enumerate(cmdline.precompile):
|
||||
filename = os.path.abspath(file)
|
||||
compile_command = CompileCommand(refid, filename, hdr_cxxflags,
|
||||
compilation_root)
|
||||
default_compilation_root)
|
||||
precompile_commands_list.append(compile_command)
|
||||
|
||||
compile_commands_list = []
|
||||
for refid, file in enumerate(cmdline.file):
|
||||
filename = os.path.abspath(file)
|
||||
compile_command = CompileCommand(refid, filename, cxxflags,
|
||||
compilation_root)
|
||||
root = default_compilation_root
|
||||
cxxflags = []
|
||||
if compdb:
|
||||
entry = compdb.getCompileCommands(filename)
|
||||
entry_list = list(entry)
|
||||
# Compilation database can contain multiple entries for single file,
|
||||
# e.g. when it has been updated by appending new entries.
|
||||
# Use last entry for the file, if it exists, as it is the newest one.
|
||||
if len(entry_list) > 0:
|
||||
last_entry = entry_list[-1]
|
||||
root = last_entry.directory
|
||||
entry_args = list(last_entry.arguments)
|
||||
# First argument in compile_commands.json arguments list is
|
||||
# compiler executable name/path. CIndex (libclang) always
|
||||
# implicitly prepends executable name, so it shouldn't be passed
|
||||
# here.
|
||||
cxxflags = common_cxxflags + entry_args[1:]
|
||||
else:
|
||||
cxxflags = common_cxxflags[:]
|
||||
|
||||
compile_command = CompileCommand(refid, filename, cxxflags, root)
|
||||
compile_commands_list.append(compile_command)
|
||||
|
||||
summary_printer = TopDownSummaryPrinter()
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
*~
|
||||
*.old
|
||||
config_build.h
|
||||
config_package.h
|
||||
Makefile
|
||||
Makefile_obj
|
||||
.objcache*
|
||||
|
||||
+7
-2
@@ -82,6 +82,7 @@ set(HEADERS
|
||||
V3File.h
|
||||
V3FileLine.h
|
||||
V3Force.h
|
||||
V3Fork.h
|
||||
V3FunctionTraits.h
|
||||
V3Gate.h
|
||||
V3Global.h
|
||||
@@ -150,6 +151,7 @@ set(HEADERS
|
||||
V3Table.h
|
||||
V3Task.h
|
||||
V3ThreadPool.h
|
||||
V3ThreadSafety.h
|
||||
V3Timing.h
|
||||
V3Trace.h
|
||||
V3TraceDecl.h
|
||||
@@ -227,6 +229,7 @@ set(COMMON_SOURCES
|
||||
V3File.cpp
|
||||
V3FileLine.cpp
|
||||
V3Force.cpp
|
||||
V3Fork.cpp
|
||||
V3Gate.cpp
|
||||
V3Global.cpp
|
||||
V3Graph.cpp
|
||||
@@ -322,7 +325,7 @@ file(TO_NATIVE_PATH ${srcdir}/../bin/verilator_includer VERILATOR_INCLUDER)
|
||||
file(TO_NATIVE_PATH ${srcdir}/vlcovgen VLCOVGEN)
|
||||
file(TO_NATIVE_PATH ${srcdir}/config_rev CONFIG_REV)
|
||||
|
||||
configure_file(config_build.h.in config_build.h @ONLY)
|
||||
configure_file(config_package.h.in config_package.h @ONLY)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT V3Ast__gen_forward_class_decls.h V3Dfg__gen_forward_class_decls.h
|
||||
@@ -414,7 +417,7 @@ foreach(astgen_name ${ASTGENERATED_NAMES})
|
||||
MAIN_DEPENDENCY ${astgen_name}.cpp
|
||||
DEPENDS ${ASTGEN} V3Ast.h
|
||||
COMMAND ${PYTHON3} ARGS
|
||||
${ASTGEN} -I"${srcdir}" --astdef V3AstNodeDType.h --astdef V3AstNodeExpr.h --astdef V3AstNodeOther.h --dfgdef V3DfgVertices.h ${astgen_name}.cpp
|
||||
${ASTGEN} -I "${srcdir}" --astdef V3AstNodeDType.h --astdef V3AstNodeExpr.h --astdef V3AstNodeOther.h --dfgdef V3DfgVertices.h ${astgen_name}.cpp
|
||||
)
|
||||
list(APPEND GENERATED_FILES ${astgen_name}__gen.cpp)
|
||||
endforeach()
|
||||
@@ -488,11 +491,13 @@ target_include_directories(${verilator}
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
target_compile_options(${verilator} PRIVATE /bigobj)
|
||||
target_compile_definitions(${verilator} PRIVATE
|
||||
YY_NO_UNISTD_H
|
||||
)
|
||||
target_include_directories(${verilator} PRIVATE ../platform/win32)
|
||||
target_link_libraries(${verilator} PRIVATE bcrypt psapi)
|
||||
target_link_options(${verilator} PRIVATE /STACK:10000000)
|
||||
endif()
|
||||
|
||||
install(TARGETS ${verilator})
|
||||
|
||||
+24
-5
@@ -18,14 +18,13 @@
|
||||
#### Start of system configuration section. ####
|
||||
|
||||
srcdir = @srcdir@
|
||||
PYTHON3 = @PYTHON3@
|
||||
EXEEXT = @EXEEXT@
|
||||
PYTHON3 = @PYTHON3@
|
||||
# VPATH only does sources; fix install_test's not creating ../bin
|
||||
vpath %.h @srcdir@
|
||||
|
||||
#### End of system configuration section. ####
|
||||
|
||||
|
||||
default: dbg opt
|
||||
debug: dbg
|
||||
optimize: opt
|
||||
@@ -36,6 +35,26 @@ endif
|
||||
|
||||
UNDER_GIT = $(wildcard ${srcdir}/../.git/logs/HEAD)
|
||||
|
||||
ifeq (,$(wildcard obj_dbg/bear.o))
|
||||
ifneq (, $(shell which bear 2>/dev/null))
|
||||
BEAR := $(shell which bear)
|
||||
ifeq (, $(shell $(BEAR) --output obj_dbg/comptest.json -- true))
|
||||
$(shell which bear 2>/dev/null >obj_dbg/bear.o)
|
||||
else
|
||||
# unsupported version
|
||||
BEAR :=
|
||||
endif
|
||||
endif
|
||||
else
|
||||
BEAR := $(shell cat obj_dbg/bear.o)
|
||||
endif
|
||||
|
||||
ifneq ($(BEAR),)
|
||||
BEAR_OBJ_OPT := $(BEAR) --append --output obj_dbg/compile_commands.json --
|
||||
else
|
||||
BEAR_OBJ_OPT :=
|
||||
endif
|
||||
|
||||
#*********************************************************************
|
||||
|
||||
obj_opt:
|
||||
@@ -62,8 +81,8 @@ endif
|
||||
|
||||
dbg: ../bin/verilator_bin_dbg$(EXEEXT) ../bin/verilator_coverage_bin_dbg$(EXEEXT)
|
||||
../bin/verilator_bin_dbg$(EXEEXT): obj_dbg ../bin prefiles
|
||||
$(MAKE) -C obj_dbg -j 1 TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj serial
|
||||
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj
|
||||
$(BEAR_OBJ_OPT) $(MAKE) -C obj_dbg -j 1 TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj serial
|
||||
$(BEAR_OBJ_OPT) $(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj
|
||||
|
||||
../bin/verilator_coverage_bin_dbg$(EXEEXT): obj_dbg ../bin prefiles
|
||||
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj serial_vlcov
|
||||
@@ -95,7 +114,7 @@ clean mostlyclean distclean maintainer-clean::
|
||||
-rm -f .objcache*
|
||||
|
||||
distclean maintainer-clean::
|
||||
-rm -f Makefile Makefile_obj config_build.h
|
||||
-rm -f Makefile Makefile_obj config_package.h
|
||||
|
||||
maintainer-clean::
|
||||
-rm -f config_rev.h
|
||||
|
||||
@@ -205,6 +205,7 @@ RAW_OBJS = \
|
||||
V3File.o \
|
||||
V3FileLine.o \
|
||||
V3Force.o \
|
||||
V3Fork.o \
|
||||
V3Gate.o \
|
||||
V3Global.o \
|
||||
V3Graph.o \
|
||||
@@ -288,6 +289,7 @@ NON_STANDALONE_HEADERS = \
|
||||
V3AstNodeExpr.h \
|
||||
V3AstNodeOther.h \
|
||||
V3DfgVertices.h \
|
||||
V3ThreadPool.h \
|
||||
V3WidthCommit.h \
|
||||
|
||||
AST_DEFS := \
|
||||
|
||||
@@ -47,6 +47,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
// Extend V3GraphVertex class for use in latch detection graph
|
||||
|
||||
class LatchDetectGraphVertex final : public V3GraphVertex {
|
||||
VL_RTTI_IMPL(LatchDetectGraphVertex, V3GraphVertex)
|
||||
public:
|
||||
enum VertexType : uint8_t { VT_BLOCK, VT_BRANCH, VT_OUTPUT };
|
||||
|
||||
|
||||
+2
-2
@@ -157,7 +157,7 @@ private:
|
||||
sentreep->unlinkFrBack();
|
||||
if (m_procedurep) {
|
||||
// To support this need queue of asserts to activate
|
||||
nodep->v3error("Unsupported: Procedural concurent assertion with"
|
||||
nodep->v3error("Unsupported: Procedural concurrent assertion with"
|
||||
" clocking event inside always (IEEE 1800-2917 16.14.6)");
|
||||
}
|
||||
}
|
||||
@@ -256,7 +256,7 @@ private:
|
||||
ifp = nextifp;
|
||||
} while (ifp);
|
||||
|
||||
AstNode* const newifp = nodep->cloneTree(false);
|
||||
AstIf* const newifp = nodep->cloneTree(false);
|
||||
const bool allow_none = nodep->unique0Pragma();
|
||||
|
||||
// Empty case means no property
|
||||
|
||||
+87
-21
@@ -55,12 +55,28 @@ bool VNUser5InUse::s_userBusy = false;
|
||||
int AstNodeDType::s_uniqueNum = 0;
|
||||
|
||||
//######################################################################
|
||||
// V3AstType
|
||||
// VNType
|
||||
|
||||
const VNTypeInfo VNType::typeInfoTable[] = {
|
||||
#include "V3Ast__gen_type_info.h" // From ./astgen
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, VNType rhs);
|
||||
|
||||
//######################################################################
|
||||
// Creators
|
||||
// VSelfPointerText
|
||||
|
||||
const std::shared_ptr<const string> VSelfPointerText::s_emptyp = std::make_shared<string>("");
|
||||
const std::shared_ptr<const string> VSelfPointerText::s_thisp = std::make_shared<string>("this");
|
||||
|
||||
string VSelfPointerText::protect(bool useSelfForThis, bool protect) const {
|
||||
const string& sp
|
||||
= useSelfForThis ? VString::replaceWord(asString(), "this", "vlSelf") : asString();
|
||||
return VIdProtect::protectWordsIf(sp, protect);
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// AstNode
|
||||
|
||||
AstNode::AstNode(VNType t, FileLine* fl)
|
||||
: m_type{t}
|
||||
@@ -125,9 +141,7 @@ string AstNode::encodeNumber(int64_t num) {
|
||||
}
|
||||
}
|
||||
|
||||
string AstNode::nameProtect() const VL_MT_STABLE {
|
||||
return VIdProtect::protectIf(name(), protect());
|
||||
}
|
||||
string AstNode::nameProtect() const { return VIdProtect::protectIf(name(), protect()); }
|
||||
string AstNode::origNameProtect() const { return VIdProtect::protectIf(origName(), protect()); }
|
||||
|
||||
string AstNode::shortName() const {
|
||||
@@ -285,7 +299,7 @@ string AstNode::vpiName(const string& namein) {
|
||||
return pretty;
|
||||
}
|
||||
|
||||
string AstNode::prettyTypeName() const VL_MT_STABLE {
|
||||
string AstNode::prettyTypeName() const {
|
||||
if (name() == "") return typeName();
|
||||
return std::string{typeName()} + " '" + prettyName() + "'";
|
||||
}
|
||||
@@ -1081,10 +1095,12 @@ bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ig
|
||||
// private: Return true if the two trees are identical
|
||||
if (!node1p && !node2p) return true;
|
||||
if (!node1p || !node2p) return false;
|
||||
if (node1p->type() != node2p->type() || node1p->dtypep() != node2p->dtypep()
|
||||
|| !node1p->same(node2p) || (gateOnly && !node1p->isGateOptimizable())) {
|
||||
return false;
|
||||
}
|
||||
if (node1p->type() != node2p->type()) return false;
|
||||
UASSERT_OBJ(
|
||||
(!node1p->dtypep() && !node2p->dtypep()) || (node1p->dtypep() && node2p->dtypep()), node1p,
|
||||
"Comparison of a node with dtypep() with a node without dtypep()\n-node2=" << node2p);
|
||||
if (node1p->dtypep() && !node1p->dtypep()->similarDType(node2p->dtypep())) return false;
|
||||
if (!node1p->same(node2p) || (gateOnly && !node1p->isGateOptimizable())) return false;
|
||||
return (sameTreeIter(node1p->m_op1p, node2p->m_op1p, false, gateOnly)
|
||||
&& sameTreeIter(node1p->m_op2p, node2p->m_op2p, false, gateOnly)
|
||||
&& sameTreeIter(node1p->m_op3p, node2p->m_op3p, false, gateOnly)
|
||||
@@ -1098,9 +1114,57 @@ bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ig
|
||||
void AstNode::checkTreeIter(const AstNode* prevBackp) const VL_MT_STABLE {
|
||||
// private: Check a tree and children
|
||||
UASSERT_OBJ(prevBackp == this->backp(), this, "Back node inconsistent");
|
||||
switch (this->type()) {
|
||||
#include "V3Ast__gen_op_checks.h"
|
||||
default: VL_UNREACHABLE; // LCOV_EXCL_LINE
|
||||
const VNTypeInfo& typeInfo = *type().typeInfo();
|
||||
for (int i = 1; i <= 4; i++) {
|
||||
AstNode* nodep = nullptr;
|
||||
switch (i) {
|
||||
case 1: nodep = op1p(); break;
|
||||
case 2: nodep = op2p(); break;
|
||||
case 3: nodep = op3p(); break;
|
||||
case 4: nodep = op4p(); break;
|
||||
default: this->v3fatalSrc("Bad case"); break;
|
||||
}
|
||||
const char* opName = typeInfo.m_opNamep[i - 1];
|
||||
switch (typeInfo.m_opType[i - 1]) {
|
||||
case VNTypeInfo::OP_UNUSED:
|
||||
UASSERT_OBJ(!nodep, this, typeInfo.m_namep << " must not use " << opName << "()");
|
||||
break;
|
||||
case VNTypeInfo::OP_USED:
|
||||
UASSERT_OBJ(nodep, this,
|
||||
typeInfo.m_namep << " must have non nullptr " << opName << "()");
|
||||
UASSERT_OBJ(!nodep->nextp(), this,
|
||||
typeInfo.m_namep << "::" << opName
|
||||
<< "() cannot have a non nullptr nextp()");
|
||||
nodep->checkTreeIter(this);
|
||||
break;
|
||||
case VNTypeInfo::OP_LIST:
|
||||
if (const AstNode* const headp = nodep) {
|
||||
const AstNode* backp = this;
|
||||
const AstNode* tailp = headp;
|
||||
const AstNode* opp = headp;
|
||||
do {
|
||||
opp->checkTreeIter(backp);
|
||||
UASSERT_OBJ(opp == headp || !opp->nextp() || !opp->m_headtailp, opp,
|
||||
"Headtailp should be null in middle of lists");
|
||||
backp = tailp = opp;
|
||||
opp = opp->nextp();
|
||||
} while (opp);
|
||||
UASSERT_OBJ(headp->m_headtailp == tailp, headp,
|
||||
"Tail in headtailp is inconsistent");
|
||||
UASSERT_OBJ(tailp->m_headtailp == headp, tailp,
|
||||
"Head in headtailp is inconsistent");
|
||||
}
|
||||
break;
|
||||
case VNTypeInfo::OP_OPTIONAL:
|
||||
if (nodep) {
|
||||
UASSERT_OBJ(!nodep->nextp(), this,
|
||||
typeInfo.m_namep << "::" << opName
|
||||
<< "() cannot have a non-nullptr nextp()");
|
||||
nodep->checkTreeIter(this);
|
||||
}
|
||||
break;
|
||||
default: this->v3fatalSrc("Bad case"); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1289,12 +1353,6 @@ bool AstNode::isTreePureRecurse() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void AstNode::v3errorEndFatal(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
v3errorEnd(str);
|
||||
assert(0); // LCOV_EXCL_LINE
|
||||
VL_UNREACHABLE;
|
||||
}
|
||||
|
||||
string AstNode::instanceStr() const {
|
||||
// Max iterations before giving up on location search,
|
||||
// in case we have some circular reference bug.
|
||||
@@ -1319,9 +1377,9 @@ string AstNode::instanceStr() const {
|
||||
|
||||
return "";
|
||||
}
|
||||
void AstNode::v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
void AstNode::v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
|
||||
if (!m_fileline) {
|
||||
V3Error::s().v3errorEnd(str, instanceStr());
|
||||
V3Error::v3errorEnd(str, instanceStr());
|
||||
} else {
|
||||
std::ostringstream nsstr;
|
||||
nsstr << str.str();
|
||||
@@ -1338,6 +1396,11 @@ void AstNode::v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s()
|
||||
nsstr, m_fileline->warnIsOff(V3Error::s().errorCode()) ? "" : instanceStr());
|
||||
}
|
||||
}
|
||||
void AstNode::v3errorEndFatal(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
|
||||
v3errorEnd(str);
|
||||
assert(0); // LCOV_EXCL_LINE
|
||||
VL_UNREACHABLE;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Data type conversion
|
||||
@@ -1406,6 +1469,9 @@ AstNodeDType* AstNode::findQueueIndexDType() const {
|
||||
AstNodeDType* AstNode::findVoidDType() const {
|
||||
return v3Global.rootp()->typeTablep()->findVoidDType(fileline());
|
||||
}
|
||||
AstNodeDType* AstNode::findStreamDType() const {
|
||||
return v3Global.rootp()->typeTablep()->findStreamDType(fileline());
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// VNDeleter
|
||||
|
||||
+173
-97
@@ -82,12 +82,36 @@ using MTaskIdSet = std::set<int>; // Set of mtaskIds for Var sorting
|
||||
// Ast<nodetypename>*' is returned.
|
||||
#define VN_AS(nodep, nodetypename) (AstNode::privateAs<Ast##nodetypename, decltype(nodep)>(nodep))
|
||||
|
||||
// Same as VN_AS, but only checks the type in debug builds. Type checking is less critical in node
|
||||
// child getters (the strongly-typed functions that wrap op*p pointers). This is because the op*p
|
||||
// pointers are usually populated by code that already asserts the correct type. Having fewer type
|
||||
// assertions yields better performance in release builds.
|
||||
#ifdef VL_DEBUG
|
||||
#define VN_DBG_AS(nodep, nodetypename) VN_AS(nodep, nodetypename)
|
||||
#else
|
||||
#define VN_DBG_AS(nodep, nodetypename) \
|
||||
(AstNode::unsafePrivateAs<Ast##nodetypename, decltype(nodep)>(nodep))
|
||||
#endif
|
||||
|
||||
// (V)erilator (N)ode deleted: Pointer to deleted AstNode (for assertions only)
|
||||
#define VN_DELETED(nodep) VL_UNLIKELY((uint64_t)(nodep) == 0x1)
|
||||
|
||||
//######################################################################
|
||||
|
||||
struct VNTypeInfo {
|
||||
const char* m_namep;
|
||||
enum uint8_t {
|
||||
OP_UNUSED,
|
||||
OP_USED,
|
||||
OP_LIST,
|
||||
OP_OPTIONAL,
|
||||
} m_opType[4];
|
||||
const char* m_opNamep[4];
|
||||
};
|
||||
|
||||
class VNType final {
|
||||
static const VNTypeInfo typeInfoTable[];
|
||||
|
||||
public:
|
||||
#include "V3Ast__gen_type_enum.h" // From ./astgen
|
||||
// Above include has:
|
||||
@@ -100,6 +124,7 @@ public:
|
||||
constexpr VNType(en _e) VL_MT_SAFE : m_e{_e} {}
|
||||
explicit VNType(int _e)
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
const VNTypeInfo* typeInfo() const VL_MT_SAFE { return &typeInfoTable[m_e]; }
|
||||
constexpr operator en() const VL_MT_SAFE { return m_e; }
|
||||
};
|
||||
constexpr bool operator==(const VNType& lhs, const VNType& rhs) VL_PURE {
|
||||
@@ -791,6 +816,7 @@ public:
|
||||
"BLOCKTEMP", "MODULETEMP", "STMTTEMP", "XTEMP", "IFACEREF", "MEMBER"};
|
||||
return names[m_e];
|
||||
}
|
||||
bool isParam() const { return m_e == GPARAM || m_e == LPARAM; }
|
||||
bool isSignal() const {
|
||||
return (m_e == WIRE || m_e == WREAL || m_e == IMPLICITWIRE || m_e == TRIWIRE || m_e == TRI0
|
||||
|| m_e == TRI1 || m_e == PORT || m_e == SUPPLY0 || m_e == SUPPLY1 || m_e == VAR);
|
||||
@@ -1157,30 +1183,34 @@ inline std::ostream& operator<<(std::ostream& os, const VNumRange& rhs) {
|
||||
class VUseType final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
IMP_INCLUDE, // Implementation (.cpp) needs an include
|
||||
INT_INCLUDE, // Interface (.h) needs an include
|
||||
IMP_FWD_CLASS, // Implementation (.cpp) needs a forward class declaration
|
||||
INT_FWD_CLASS, // Interface (.h) needs a forward class declaration
|
||||
// Enum values are compared with <, so order matters
|
||||
INT_FWD_CLASS = 1 << 0, // Interface (.h) needs a forward class declaration
|
||||
INT_INCLUDE = 1 << 1, // Interface (.h) needs an include
|
||||
};
|
||||
enum en m_e;
|
||||
VUseType()
|
||||
: m_e{IMP_FWD_CLASS} {}
|
||||
: m_e{INT_FWD_CLASS} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VUseType(en _e)
|
||||
: m_e{_e} {}
|
||||
explicit VUseType(int _e)
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
bool isInclude() const { return m_e == IMP_INCLUDE || m_e == INT_INCLUDE; }
|
||||
bool isFwdClass() const { return m_e == IMP_FWD_CLASS || m_e == INT_FWD_CLASS; }
|
||||
constexpr operator en() const { return m_e; }
|
||||
bool containsAny(VUseType other) { return m_e & other.m_e; }
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"IMP_INC", "INT_INC", "IMP_FWD", "INT_FWD"};
|
||||
return names[m_e];
|
||||
static const char* const names[] = {"INT_FWD", "INT_INC", "INT_FWD_INC"};
|
||||
return names[m_e - 1];
|
||||
}
|
||||
};
|
||||
constexpr bool operator==(const VUseType& lhs, const VUseType& rhs) { return lhs.m_e == rhs.m_e; }
|
||||
constexpr bool operator==(const VUseType& lhs, VUseType::en rhs) { return lhs.m_e == rhs; }
|
||||
constexpr bool operator==(VUseType::en lhs, const VUseType& rhs) { return lhs == rhs.m_e; }
|
||||
constexpr VUseType::en operator|(VUseType::en lhs, VUseType::en rhs) {
|
||||
return VUseType::en((uint8_t)lhs | (uint8_t)rhs);
|
||||
}
|
||||
constexpr VUseType::en operator&(VUseType::en lhs, VUseType::en rhs) {
|
||||
return VUseType::en((uint8_t)lhs & (uint8_t)rhs);
|
||||
}
|
||||
inline std::ostream& operator<<(std::ostream& os, const VUseType& rhs) {
|
||||
return os << rhs.ascii();
|
||||
}
|
||||
@@ -1221,6 +1251,44 @@ public:
|
||||
~VBasicTypeKey() = default;
|
||||
};
|
||||
|
||||
// ######################################################################
|
||||
// VSelfPointerText - Represents text to be emitted before a given var reference, call, etc. to
|
||||
// serve as a pointer to a 'self' object. For example, it could be empty (no self pointer), or the
|
||||
// string 'this', or 'vlSymsp->...'
|
||||
|
||||
class VSelfPointerText final {
|
||||
private:
|
||||
// STATIC MEMBERS
|
||||
// Keep these in shared pointers to avoid branching for special cases
|
||||
static const std::shared_ptr<const string> s_emptyp; // Holds ""
|
||||
static const std::shared_ptr<const string> s_thisp; // Holds "this"
|
||||
|
||||
// MEMBERS
|
||||
std::shared_ptr<const string> m_strp;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
class Empty {}; // for creator type-overload selection
|
||||
VSelfPointerText(Empty)
|
||||
: m_strp{s_emptyp} {}
|
||||
class This {}; // for creator type-overload selection
|
||||
VSelfPointerText(This)
|
||||
: m_strp{s_thisp} {}
|
||||
VSelfPointerText(This, const string& field)
|
||||
: m_strp{std::make_shared<const string>("this->" + field)} {}
|
||||
class VlSyms {}; // for creator type-overload selection
|
||||
VSelfPointerText(VlSyms, const string& field)
|
||||
: m_strp{std::make_shared<const string>("(&vlSymsp->" + field + ')')} {}
|
||||
|
||||
// METHODS
|
||||
bool isEmpty() const { return m_strp == s_emptyp; }
|
||||
bool isVlSym() const { return m_strp->find("vlSymsp") != string::npos; }
|
||||
bool hasThis() const { return m_strp == s_thisp || VString::startsWith(*m_strp, "this"); }
|
||||
string protect(bool useSelfForThis, bool protect) const;
|
||||
const std::string& asString() const { return *m_strp; }
|
||||
bool operator==(const VSelfPointerText& other) const { return *m_strp == *other.m_strp; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// AstNUser - Generic base class for AST User nodes.
|
||||
// - Also used to allow parameter passing up/down iterate calls
|
||||
@@ -1303,55 +1371,55 @@ protected:
|
||||
class VNUser1InUse final : VNUserInUseBase {
|
||||
protected:
|
||||
friend class AstNode;
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
public:
|
||||
VNUser1InUse() { allocate(1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser1InUse() { free (1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
VNUser1InUse() { allocate(1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser1InUse() { free (1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void clear() { clearcnt(1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void check() { checkcnt(1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
};
|
||||
class VNUser2InUse final : VNUserInUseBase {
|
||||
protected:
|
||||
friend class AstNode;
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
public:
|
||||
VNUser2InUse() { allocate(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser2InUse() { free (2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
VNUser2InUse() { allocate(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser2InUse() { free (2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void clear() { clearcnt(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void check() { checkcnt(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
};
|
||||
class VNUser3InUse final : VNUserInUseBase {
|
||||
protected:
|
||||
friend class AstNode;
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
public:
|
||||
VNUser3InUse() { allocate(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser3InUse() { free (3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
VNUser3InUse() { allocate(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser3InUse() { free (3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void clear() { clearcnt(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void check() { checkcnt(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
};
|
||||
class VNUser4InUse final : VNUserInUseBase {
|
||||
protected:
|
||||
friend class AstNode;
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
public:
|
||||
VNUser4InUse() { allocate(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser4InUse() { free (4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
VNUser4InUse() { allocate(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser4InUse() { free (4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void clear() { clearcnt(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void check() { checkcnt(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
};
|
||||
class VNUser5InUse final : VNUserInUseBase {
|
||||
protected:
|
||||
friend class AstNode;
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
public:
|
||||
VNUser5InUse() { allocate(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser5InUse() { free (5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
VNUser5InUse() { allocate(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser5InUse() { free (5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void clear() { clearcnt(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void check() { checkcnt(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
};
|
||||
@@ -1634,7 +1702,7 @@ public:
|
||||
const char* typeName() const VL_MT_SAFE { return type().ascii(); } // See also prettyTypeName
|
||||
AstNode* nextp() const VL_MT_STABLE { return m_nextp; }
|
||||
AstNode* backp() const VL_MT_STABLE { return m_backp; }
|
||||
AstNode* abovep() const; // Parent node above, only when no nextp() as otherwise slow
|
||||
AstNode* abovep() const; // Get parent node above, only for list head and tail
|
||||
AstNode* op1p() const VL_MT_STABLE { return m_op1p; }
|
||||
AstNode* op2p() const VL_MT_STABLE { return m_op2p; }
|
||||
AstNode* op3p() const VL_MT_STABLE { return m_op3p; }
|
||||
@@ -1686,7 +1754,7 @@ public:
|
||||
static constexpr int INSTR_COUNT_PLI = 20; // PLI routines
|
||||
|
||||
// ACCESSORS
|
||||
virtual string name() const VL_MT_STABLE { return ""; }
|
||||
virtual string name() const { return ""; }
|
||||
virtual string origName() const { return ""; }
|
||||
virtual void name(const string& name) {
|
||||
this->v3fatalSrc("name() called on object without name() method");
|
||||
@@ -1694,7 +1762,7 @@ public:
|
||||
virtual void tag(const string& text) {}
|
||||
virtual string tag() const { return ""; }
|
||||
virtual string verilogKwd() const { return ""; }
|
||||
string nameProtect() const VL_MT_STABLE; // Name with --protect-id applied
|
||||
string nameProtect() const; // Name with --protect-id applied
|
||||
string origNameProtect() const; // origName with --protect-id applied
|
||||
string shortName() const; // Name with __PVT__ removed for concatenating scopes
|
||||
static string dedotName(const string& namein); // Name with dots removed
|
||||
@@ -1707,10 +1775,10 @@ public:
|
||||
static string encodeName(const string& namein);
|
||||
static string encodeNumber(int64_t num); // Encode number into internal C representation
|
||||
static string vcdName(const string& namein); // Name for printing out to vcd files
|
||||
string prettyName() const VL_MT_STABLE { return prettyName(name()); }
|
||||
string prettyName() const { return prettyName(name()); }
|
||||
string prettyNameQ() const { return prettyNameQ(name()); }
|
||||
// "VARREF" for error messages (NOT dtype's pretty name)
|
||||
string prettyTypeName() const VL_MT_STABLE;
|
||||
string prettyTypeName() const;
|
||||
virtual string prettyOperatorName() const { return "operator " + prettyTypeName(); }
|
||||
FileLine* fileline() const VL_MT_SAFE { return m_fileline; }
|
||||
void fileline(FileLine* fl) { m_fileline = fl; }
|
||||
@@ -1742,74 +1810,74 @@ public:
|
||||
inline bool isString() const VL_MT_STABLE;
|
||||
|
||||
// clang-format off
|
||||
VNUser user1u() const VL_MT_STABLE {
|
||||
VNUser user1u() const VL_MT_STABLE {
|
||||
// Slows things down measurably, so disabled by default
|
||||
//UASSERT_STATIC(VNUser1InUse::s_userBusy, "userp set w/o busy");
|
||||
return ((m_user1Cnt==VNUser1InUse::s_userCntGbl) ? m_user1u : VNUser{0});
|
||||
//UASSERT_STATIC(VNUser1InUse::s_userBusy, "user1p used without AstUserInUse");
|
||||
return ((m_user1Cnt == VNUser1InUse::s_userCntGbl) ? m_user1u : VNUser{0});
|
||||
}
|
||||
AstNode* user1p() const VL_MT_STABLE { return user1u().toNodep(); }
|
||||
void user1u(const VNUser& user) { m_user1u=user; m_user1Cnt=VNUser1InUse::s_userCntGbl; }
|
||||
void user1p(void* userp) { user1u(VNUser{userp}); }
|
||||
int user1() const { return user1u().toInt(); }
|
||||
void user1(int val) { user1u(VNUser{val}); }
|
||||
int user1Inc(int val=1) { int v=user1(); user1(v+val); return v; }
|
||||
int user1SetOnce() { int v=user1(); if (!v) user1(1); return v; } // Better for cache than user1Inc()
|
||||
AstNode* user1p() const VL_MT_STABLE { return user1u().toNodep(); }
|
||||
void user1u(const VNUser& user) { m_user1u = user; m_user1Cnt = VNUser1InUse::s_userCntGbl; }
|
||||
void user1p(void* userp) { user1u(VNUser{userp}); }
|
||||
void user1(int val) { user1u(VNUser{val}); }
|
||||
int user1() const { return user1u().toInt(); }
|
||||
int user1Inc(int val = 1) { int v = user1(); user1(v + val); return v; }
|
||||
int user1SetOnce() { int v = user1(); if (!v) user1(1); return v; } // Better for cache than user1Inc()
|
||||
static void user1ClearTree() { VNUser1InUse::clear(); } // Clear userp()'s across the entire tree
|
||||
|
||||
VNUser user2u() const VL_MT_STABLE {
|
||||
VNUser user2u() const VL_MT_STABLE {
|
||||
// Slows things down measurably, so disabled by default
|
||||
//UASSERT_STATIC(VNUser2InUse::s_userBusy, "userp set w/o busy");
|
||||
return ((m_user2Cnt==VNUser2InUse::s_userCntGbl) ? m_user2u : VNUser{0});
|
||||
//UASSERT_STATIC(VNUser2InUse::s_userBusy, "user2p used without AstUserInUse");
|
||||
return ((m_user2Cnt == VNUser2InUse::s_userCntGbl) ? m_user2u : VNUser{0});
|
||||
}
|
||||
AstNode* user2p() const VL_MT_STABLE { return user2u().toNodep(); }
|
||||
void user2u(const VNUser& user) { m_user2u=user; m_user2Cnt=VNUser2InUse::s_userCntGbl; }
|
||||
void user2p(void* userp) { user2u(VNUser{userp}); }
|
||||
int user2() const { return user2u().toInt(); }
|
||||
void user2(int val) { user2u(VNUser{val}); }
|
||||
int user2Inc(int val=1) { int v=user2(); user2(v+val); return v; }
|
||||
int user2SetOnce() { int v=user2(); if (!v) user2(1); return v; } // Better for cache than user2Inc()
|
||||
AstNode* user2p() const VL_MT_STABLE { return user2u().toNodep(); }
|
||||
void user2u(const VNUser& user) { m_user2u = user; m_user2Cnt = VNUser2InUse::s_userCntGbl; }
|
||||
void user2p(void* userp) { user2u(VNUser{userp}); }
|
||||
void user2(int val) { user2u(VNUser{val}); }
|
||||
int user2() const { return user2u().toInt(); }
|
||||
int user2Inc(int val = 1) { int v = user2(); user2(v + val); return v; }
|
||||
int user2SetOnce() { int v = user2(); if (!v) user2(1); return v; } // Better for cache than user2Inc()
|
||||
static void user2ClearTree() { VNUser2InUse::clear(); } // Clear userp()'s across the entire tree
|
||||
|
||||
VNUser user3u() const VL_MT_STABLE {
|
||||
VNUser user3u() const VL_MT_STABLE {
|
||||
// Slows things down measurably, so disabled by default
|
||||
//UASSERT_STATIC(VNUser3InUse::s_userBusy, "userp set w/o busy");
|
||||
return ((m_user3Cnt==VNUser3InUse::s_userCntGbl) ? m_user3u : VNUser{0});
|
||||
//UASSERT_STATIC(VNUser3InUse::s_userBusy, "user3p used without AstUserInUse");
|
||||
return ((m_user3Cnt == VNUser3InUse::s_userCntGbl) ? m_user3u : VNUser{0});
|
||||
}
|
||||
AstNode* user3p() const VL_MT_STABLE { return user3u().toNodep(); }
|
||||
void user3u(const VNUser& user) { m_user3u=user; m_user3Cnt=VNUser3InUse::s_userCntGbl; }
|
||||
void user3p(void* userp) { user3u(VNUser{userp}); }
|
||||
int user3() const { return user3u().toInt(); }
|
||||
void user3(int val) { user3u(VNUser{val}); }
|
||||
int user3Inc(int val=1) { int v=user3(); user3(v+val); return v; }
|
||||
int user3SetOnce() { int v=user3(); if (!v) user3(1); return v; } // Better for cache than user3Inc()
|
||||
AstNode* user3p() const VL_MT_STABLE { return user3u().toNodep(); }
|
||||
void user3u(const VNUser& user) { m_user3u = user; m_user3Cnt = VNUser3InUse::s_userCntGbl; }
|
||||
void user3p(void* userp) { user3u(VNUser{userp}); }
|
||||
void user3(int val) { user3u(VNUser{val}); }
|
||||
int user3() const { return user3u().toInt(); }
|
||||
int user3Inc(int val = 1) { int v = user3(); user3(v + val); return v; }
|
||||
int user3SetOnce() { int v = user3(); if (!v) user3(1); return v; } // Better for cache than user3Inc()
|
||||
static void user3ClearTree() { VNUser3InUse::clear(); } // Clear userp()'s across the entire tree
|
||||
|
||||
VNUser user4u() const VL_MT_STABLE {
|
||||
VNUser user4u() const VL_MT_STABLE {
|
||||
// Slows things down measurably, so disabled by default
|
||||
//UASSERT_STATIC(VNUser4InUse::s_userBusy, "userp set w/o busy");
|
||||
return ((m_user4Cnt==VNUser4InUse::s_userCntGbl) ? m_user4u : VNUser{0});
|
||||
//UASSERT_STATIC(VNUser4InUse::s_userBusy, "user4p used without AstUserInUse");
|
||||
return ((m_user4Cnt == VNUser4InUse::s_userCntGbl) ? m_user4u : VNUser{0});
|
||||
}
|
||||
AstNode* user4p() const VL_MT_STABLE { return user4u().toNodep(); }
|
||||
void user4u(const VNUser& user) { m_user4u=user; m_user4Cnt=VNUser4InUse::s_userCntGbl; }
|
||||
void user4p(void* userp) { user4u(VNUser{userp}); }
|
||||
int user4() const { return user4u().toInt(); }
|
||||
void user4(int val) { user4u(VNUser{val}); }
|
||||
int user4Inc(int val=1) { int v=user4(); user4(v+val); return v; }
|
||||
int user4SetOnce() { int v=user4(); if (!v) user4(1); return v; } // Better for cache than user4Inc()
|
||||
AstNode* user4p() const VL_MT_STABLE { return user4u().toNodep(); }
|
||||
void user4u(const VNUser& user) { m_user4u = user; m_user4Cnt = VNUser4InUse::s_userCntGbl; }
|
||||
void user4p(void* userp) { user4u(VNUser{userp}); }
|
||||
void user4(int val) { user4u(VNUser{val}); }
|
||||
int user4() const { return user4u().toInt(); }
|
||||
int user4Inc(int val = 1) { int v = user4(); user4(v + val); return v; }
|
||||
int user4SetOnce() { int v = user4(); if (!v) user4(1); return v; } // Better for cache than user4Inc()
|
||||
static void user4ClearTree() { VNUser4InUse::clear(); } // Clear userp()'s across the entire tree
|
||||
|
||||
VNUser user5u() const VL_MT_STABLE {
|
||||
VNUser user5u() const VL_MT_STABLE {
|
||||
// Slows things down measurably, so disabled by default
|
||||
//UASSERT_STATIC(VNUser5InUse::s_userBusy, "userp set w/o busy");
|
||||
return ((m_user5Cnt==VNUser5InUse::s_userCntGbl) ? m_user5u : VNUser{0});
|
||||
//UASSERT_STATIC(VNUser5InUse::s_userBusy, "user5p used without AstUserInUse");
|
||||
return ((m_user5Cnt == VNUser5InUse::s_userCntGbl) ? m_user5u : VNUser{0});
|
||||
}
|
||||
AstNode* user5p() const VL_MT_STABLE { return user5u().toNodep(); }
|
||||
void user5u(const VNUser& user) { m_user5u=user; m_user5Cnt=VNUser5InUse::s_userCntGbl; }
|
||||
void user5p(void* userp) { user5u(VNUser{userp}); }
|
||||
int user5() const { return user5u().toInt(); }
|
||||
void user5(int val) { user5u(VNUser{val}); }
|
||||
int user5Inc(int val=1) { int v=user5(); user5(v+val); return v; }
|
||||
int user5SetOnce() { int v=user5(); if (!v) user5(1); return v; } // Better for cache than user5Inc()
|
||||
AstNode* user5p() const VL_MT_STABLE { return user5u().toNodep(); }
|
||||
void user5u(const VNUser& user) { m_user5u = user; m_user5Cnt = VNUser5InUse::s_userCntGbl; }
|
||||
void user5p(void* userp) { user5u(VNUser{userp}); }
|
||||
void user5(int val) { user5u(VNUser{val}); }
|
||||
int user5() const { return user5u().toInt(); }
|
||||
int user5Inc(int val = 1) { int v = user5(); user5(v + val); return v; }
|
||||
int user5SetOnce() { int v = user5(); if (!v) user5(1); return v; } // Better for cache than user5Inc()
|
||||
static void user5ClearTree() { VNUser5InUse::clear(); } // Clear userp()'s across the entire tree
|
||||
// clang-format on
|
||||
|
||||
@@ -1867,6 +1935,7 @@ public:
|
||||
void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate
|
||||
void dtypeSetEmptyQueue() { dtypep(findEmptyQueueDType()); }
|
||||
void dtypeSetVoid() { dtypep(findVoidDType()); }
|
||||
void dtypeSetStream() { dtypep(findStreamDType()); }
|
||||
|
||||
// Data type locators
|
||||
AstNodeDType* findBitDType() const { return findBasicDType(VBasicDTypeKwd::LOGIC); }
|
||||
@@ -1878,6 +1947,7 @@ public:
|
||||
AstNodeDType* findCHandleDType() const { return findBasicDType(VBasicDTypeKwd::CHANDLE); }
|
||||
AstNodeDType* findEmptyQueueDType() const;
|
||||
AstNodeDType* findVoidDType() const;
|
||||
AstNodeDType* findStreamDType() const;
|
||||
AstNodeDType* findQueueIndexDType() const;
|
||||
AstNodeDType* findBitDType(int width, int widthMin, VSigning numeric) const;
|
||||
AstNodeDType* findLogicDType(int width, int widthMin, VSigning numeric) const;
|
||||
@@ -1888,9 +1958,9 @@ public:
|
||||
static AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
|
||||
|
||||
// METHODS - dump and error
|
||||
void v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex);
|
||||
void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex);
|
||||
void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN
|
||||
VL_REQUIRES(V3Error::s().m_mutex);
|
||||
VL_RELEASE(V3Error::s().m_mutex);
|
||||
string warnContextPrimary() const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
return fileline()->warnContextPrimary();
|
||||
}
|
||||
@@ -1928,10 +1998,6 @@ public:
|
||||
AstNode* belowp); // When calling, "this" is second argument
|
||||
|
||||
// METHODS - Iterate on a tree
|
||||
// Clone or return nullptr if nullptr
|
||||
static AstNode* cloneTreeNull(AstNode* nodep, bool cloneNextLink) {
|
||||
return nodep ? nodep->cloneTree(cloneNextLink) : nullptr;
|
||||
}
|
||||
AstNode* cloneTree(bool cloneNextLink); // Not const, as sets clonep() on original nodep
|
||||
bool gateTree() { return gateTreeIter(); } // Is tree isGateOptimizable?
|
||||
inline bool sameTree(const AstNode* node2p) const; // Does tree of this == node2p?
|
||||
@@ -2065,24 +2131,34 @@ public:
|
||||
return nodep && privateTypeTest<T>(nodep) ? reinterpret_cast<const T*>(nodep) : nullptr;
|
||||
}
|
||||
|
||||
// For use via the VN_AS macro only
|
||||
// For use via privateAs or the VN_DBG_AS macro only
|
||||
template <typename T, typename E>
|
||||
static T* privateAs(AstNode* nodep) VL_PURE {
|
||||
static T* unsafePrivateAs(AstNode* nodep) VL_PURE {
|
||||
static_assert(!uselessCast<T, E>(), "Unnecessary VN_AS, node known to have target type.");
|
||||
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_AS, node cannot be this type.");
|
||||
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
|
||||
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
|
||||
<< "'");
|
||||
return reinterpret_cast<T*>(nodep);
|
||||
}
|
||||
template <typename T, typename E>
|
||||
static const T* privateAs(const AstNode* nodep) VL_PURE {
|
||||
static const T* unsafePrivateAs(const AstNode* nodep) VL_PURE {
|
||||
static_assert(!uselessCast<T, E>(), "Unnecessary VN_AS, node known to have target type.");
|
||||
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_AS, node cannot be this type.");
|
||||
return reinterpret_cast<const T*>(nodep);
|
||||
}
|
||||
|
||||
// For use via the VN_AS macro only
|
||||
template <typename T, typename E>
|
||||
static T* privateAs(AstNode* nodep) VL_PURE {
|
||||
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
|
||||
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
|
||||
<< "'");
|
||||
return reinterpret_cast<const T*>(nodep);
|
||||
return unsafePrivateAs<T, E>(nodep);
|
||||
}
|
||||
template <typename T, typename E>
|
||||
static const T* privateAs(const AstNode* nodep) VL_PURE {
|
||||
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
|
||||
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
|
||||
<< "'");
|
||||
return unsafePrivateAs<T, E>(nodep);
|
||||
}
|
||||
|
||||
// Predicate that returns true if the given 'nodep' might have a descendant of type 'T_Node'.
|
||||
|
||||
+8
-4
@@ -154,12 +154,15 @@ AstCExpr::AstCExpr(FileLine* fl, const string& textStmt, int setwidth, bool clea
|
||||
}
|
||||
|
||||
AstVarRef::AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarRef(fl, varp->name(), varp, access) {}
|
||||
: ASTGEN_SUPER_VarRef(fl, varp, access) {}
|
||||
// This form only allowed post-link (see above)
|
||||
AstVarRef::AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarRef(fl, varscp->varp()->name(), varscp->varp(), access) {
|
||||
: ASTGEN_SUPER_VarRef(fl, varscp->varp(), access) {
|
||||
varScopep(varscp);
|
||||
}
|
||||
|
||||
string AstVarRef::name() const { return varp() ? varp()->name() : "<null>"; }
|
||||
|
||||
bool AstVarRef::same(const AstVarRef* samep) const {
|
||||
if (varScopep()) {
|
||||
return (varScopep() == samep->varScopep() && access() == samep->access());
|
||||
@@ -173,13 +176,14 @@ bool AstVarRef::sameNoLvalue(AstVarRef* samep) const {
|
||||
return (varScopep() == samep->varScopep());
|
||||
} else {
|
||||
return (selfPointer() == samep->selfPointer()
|
||||
&& (!selfPointer().empty() || !samep->selfPointer().empty())
|
||||
&& (!selfPointer().isEmpty() || !samep->selfPointer().isEmpty())
|
||||
&& varp()->name() == samep->varp()->name());
|
||||
}
|
||||
}
|
||||
|
||||
AstVarXRef::AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarXRef(fl, varp->name(), varp, access)
|
||||
: ASTGEN_SUPER_VarXRef(fl, varp, access)
|
||||
, m_name{varp->name()}
|
||||
, m_dotted{dotted} {
|
||||
dtypeFrom(varp);
|
||||
}
|
||||
|
||||
+33
-13
@@ -126,13 +126,13 @@ public:
|
||||
const char* charIQWN() const {
|
||||
return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I");
|
||||
}
|
||||
string cType(const string& name, bool forFunc, bool isRef) const VL_MT_STABLE;
|
||||
string cType(const string& name, bool forFunc, bool isRef) const;
|
||||
// Represents a C++ LiteralType? (can be constexpr)
|
||||
bool isLiteralType() const VL_MT_STABLE;
|
||||
|
||||
private:
|
||||
class CTypeRecursed;
|
||||
CTypeRecursed cTypeRecurse(bool compound) const VL_MT_STABLE;
|
||||
CTypeRecursed cTypeRecurse(bool compound) const;
|
||||
};
|
||||
class AstNodeArrayDType VL_NOT_FINAL : public AstNodeDType {
|
||||
// Array data type, ie "some_dtype var_name [2:0]"
|
||||
@@ -199,12 +199,9 @@ class AstNodeUOrStructDType VL_NOT_FINAL : public AstNodeDType {
|
||||
// A struct or union; common handling
|
||||
// @astgen op1 := membersp : List[AstMemberDType]
|
||||
private:
|
||||
// TYPES
|
||||
using MemberNameMap = std::map<const std::string, AstMemberDType*>;
|
||||
// MEMBERS
|
||||
string m_name; // Name from upper typedef, if any
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
|
||||
MemberNameMap m_members;
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Package it will be emitted with
|
||||
const int m_uniqueNum;
|
||||
bool m_packed;
|
||||
bool m_isFourstate = false; // V3Width computes
|
||||
@@ -250,12 +247,6 @@ public:
|
||||
static bool packedUnsup() { return true; }
|
||||
void isFourstate(bool flag) { m_isFourstate = flag; }
|
||||
bool isFourstate() const override VL_MT_SAFE { return m_isFourstate; }
|
||||
void clearCache() { m_members.clear(); }
|
||||
void repairMemberCache();
|
||||
AstMemberDType* findMember(const string& name) const {
|
||||
const auto it = m_members.find(name);
|
||||
return (it == m_members.end()) ? nullptr : it->second;
|
||||
}
|
||||
static int lo() { return 0; }
|
||||
int hi() const { return dtypep()->width() - 1; } // Packed classes look like arrays
|
||||
VNumRange declRange() const { return VNumRange{hi(), lo()}; }
|
||||
@@ -1062,7 +1053,7 @@ private:
|
||||
// Post-width typedefs are removed and point to type directly
|
||||
AstNodeDType* m_refDTypep = nullptr; // data type pointed to, BELOW the AstTypedef
|
||||
string m_name; // Name of an AstTypedef
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Class/package in which it was defined
|
||||
public:
|
||||
AstRefDType(FileLine* fl, const string& name)
|
||||
: ASTGEN_SUPER_RefDType(fl)
|
||||
@@ -1077,6 +1068,7 @@ public:
|
||||
AstRefDType(FileLine* fl, FlagTypeOfExpr, AstNode* typeofp)
|
||||
: ASTGEN_SUPER_RefDType(fl) {
|
||||
this->typeofp(typeofp);
|
||||
if (AstNodeDType* const dtp = VN_CAST(typeofp, NodeDType)) refDTypep(dtp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstRefDType;
|
||||
// METHODS
|
||||
@@ -1188,6 +1180,34 @@ public:
|
||||
int widthTotalBytes() const override { return sizeof(std::map<std::string, std::string>); }
|
||||
bool isCompound() const override { return true; }
|
||||
};
|
||||
class AstStreamDType final : public AstNodeDType {
|
||||
// Stream data type, used only as data type of stream operations
|
||||
// Should behave like AstPackArrayDType, but it doesn't have a size
|
||||
public:
|
||||
explicit AstStreamDType(FileLine* fl)
|
||||
: ASTGEN_SUPER_StreamDType(fl) {
|
||||
dtypep(this);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstStreamDType;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
bool hasDType() const override { return true; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
bool undead() const override { return true; }
|
||||
AstNodeDType* subDTypep() const override VL_MT_SAFE { return nullptr; }
|
||||
AstNodeDType* virtRefDTypep() const override { return nullptr; }
|
||||
void virtRefDTypep(AstNodeDType* nodep) override {}
|
||||
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
// cppcheck-suppress csyleCast
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
// cppcheck-suppress csyleCast
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
// cppcheck-suppress csyleCast
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
int widthAlignBytes() const override { return 1; }
|
||||
int widthTotalBytes() const override { return 1; }
|
||||
bool isCompound() const override { return false; }
|
||||
};
|
||||
class AstUnsizedArrayDType final : public AstNodeDType {
|
||||
// Unsized/open-range Array data type, ie "some_dtype var_name []"
|
||||
// @astgen op1 := childDTypep : Optional[AstNodeDType] // moved to refDTypep() in V3Width
|
||||
|
||||
+78
-49
@@ -214,7 +214,7 @@ class AstNodeFTaskRef VL_NOT_FINAL : public AstNodeExpr {
|
||||
// @astgen op3 := pinsp : List[AstNodeExpr]
|
||||
// @astgen op4 := scopeNamep : Optional[AstScopeName]
|
||||
AstNodeFTask* m_taskp = nullptr; // [AfterLink] Pointer to task referenced
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Class/package of the task
|
||||
string m_name; // Name of variable
|
||||
string m_dotted; // Dotted part of scope the name()ed task/func is under or ""
|
||||
string m_inlinedDots; // Dotted hierarchy flattened out
|
||||
@@ -360,14 +360,8 @@ class AstNodeCond VL_NOT_FINAL : public AstNodeTriop {
|
||||
// @astgen alias op2 := thenp
|
||||
// @astgen alias op3 := elsep
|
||||
protected:
|
||||
AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep)
|
||||
: AstNodeTriop{t, fl, condp, thenp, elsep} {
|
||||
if (thenp) {
|
||||
dtypeFrom(thenp);
|
||||
} else if (elsep) {
|
||||
dtypeFrom(elsep);
|
||||
}
|
||||
}
|
||||
AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp,
|
||||
AstNodeExpr* elsep);
|
||||
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstNodeCond;
|
||||
@@ -449,21 +443,19 @@ class AstNodeVarRef VL_NOT_FINAL : public AstNodeExpr {
|
||||
VAccess m_access; // Left hand side assignment
|
||||
AstVar* m_varp; // [AfterLink] Pointer to variable itself
|
||||
AstVarScope* m_varScopep = nullptr; // Varscope for hierarchy
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
|
||||
string m_name; // Name of variable
|
||||
string m_selfPointer; // Output code object pointer (e.g.: 'this')
|
||||
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Class/package of the variable
|
||||
VSelfPointerText m_selfPointer
|
||||
= VSelfPointerText{VSelfPointerText::Empty()}; // Output code object
|
||||
// pointer (e.g.: 'this')
|
||||
protected:
|
||||
AstNodeVarRef(VNType t, FileLine* fl, const string& name, const VAccess& access)
|
||||
AstNodeVarRef(VNType t, FileLine* fl, const VAccess& access)
|
||||
: AstNodeExpr{t, fl}
|
||||
, m_access{access}
|
||||
, m_name{name} {
|
||||
, m_access{access} {
|
||||
varp(nullptr);
|
||||
}
|
||||
AstNodeVarRef(VNType t, FileLine* fl, const string& name, AstVar* varp, const VAccess& access)
|
||||
AstNodeVarRef(VNType t, FileLine* fl, AstVar* varp, const VAccess& access)
|
||||
: AstNodeExpr{t, fl}
|
||||
, m_access{access}
|
||||
, m_name{name} {
|
||||
, m_access{access} {
|
||||
// May have varp==nullptr
|
||||
this->varp(varp);
|
||||
}
|
||||
@@ -474,8 +466,6 @@ public:
|
||||
const char* broken() const override;
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
void cloneRelink() override;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
void name(const string& name) override { m_name = name; }
|
||||
VAccess access() const { return m_access; }
|
||||
void access(const VAccess& flag) { m_access = flag; } // Avoid using this; Set in constructor
|
||||
AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable
|
||||
@@ -485,9 +475,11 @@ public:
|
||||
}
|
||||
AstVarScope* varScopep() const { return m_varScopep; }
|
||||
void varScopep(AstVarScope* varscp) { m_varScopep = varscp; }
|
||||
string selfPointer() const { return m_selfPointer; }
|
||||
void selfPointer(const string& value) { m_selfPointer = value; }
|
||||
string selfPointerProtect(bool useSelfForThis) const;
|
||||
const VSelfPointerText& selfPointer() const { return m_selfPointer; }
|
||||
void selfPointer(const VSelfPointerText& selfPointer) { m_selfPointer = selfPointer; }
|
||||
string selfPointerProtect(bool useSelfForThis) const {
|
||||
return selfPointer().protect(useSelfForThis, protect());
|
||||
}
|
||||
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
|
||||
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
|
||||
// Know no children, and hot function, so skip iterator for speed
|
||||
@@ -593,21 +585,13 @@ class AstCMethodHard final : public AstNodeExpr {
|
||||
string m_name; // Name of method
|
||||
bool m_pure = false; // Pure optimizable
|
||||
public:
|
||||
AstCMethodHard(FileLine* fl, AstNodeExpr* fromp, VFlagChildDType, const string& name,
|
||||
AstNodeExpr* pinsp = nullptr)
|
||||
: ASTGEN_SUPER_CMethodHard(fl)
|
||||
, m_name{name} {
|
||||
// TODO: this constructor is exactly the same as the other, bar the ignored tag argument
|
||||
this->fromp(fromp);
|
||||
this->addPinsp(pinsp);
|
||||
dtypep(nullptr); // V3Width will resolve
|
||||
}
|
||||
AstCMethodHard(FileLine* fl, AstNodeExpr* fromp, const string& name,
|
||||
AstNodeExpr* pinsp = nullptr)
|
||||
: ASTGEN_SUPER_CMethodHard(fl)
|
||||
, m_name{name} {
|
||||
this->fromp(fromp);
|
||||
this->addPinsp(pinsp);
|
||||
setPurity();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCMethodHard;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
@@ -617,11 +601,13 @@ public:
|
||||
return (m_name == asamep->m_name);
|
||||
}
|
||||
bool isPure() const override { return m_pure; }
|
||||
void pure(bool flag) { m_pure = flag; }
|
||||
int instrCount() const override;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
|
||||
private:
|
||||
void setPurity();
|
||||
};
|
||||
class AstCast final : public AstNodeExpr {
|
||||
// Cast to appropriate data type
|
||||
@@ -721,7 +707,7 @@ class AstClassOrPackageRef final : public AstNodeExpr {
|
||||
private:
|
||||
string m_name;
|
||||
// Node not NodeModule to appease some early parser usage
|
||||
AstNode* m_classOrPackageNodep; // Package hierarchy
|
||||
AstNode* m_classOrPackageNodep; // Pointer to class/package referenced
|
||||
public:
|
||||
AstClassOrPackageRef(FileLine* fl, const string& name, AstNode* classOrPackageNodep,
|
||||
AstPin* paramsp)
|
||||
@@ -1024,6 +1010,34 @@ public:
|
||||
// May return nullptr on parse failure.
|
||||
static AstConst* parseParamLiteral(FileLine* fl, const string& literal);
|
||||
};
|
||||
class AstCvtDynArrayToPacked final : public AstNodeExpr {
|
||||
// Cast from dynamic queue data type to packed array
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
public:
|
||||
AstCvtDynArrayToPacked(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_CvtDynArrayToPacked(fl) {
|
||||
this->fromp(fromp);
|
||||
dtypeFrom(dtp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCvtDynArrayToPacked;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
};
|
||||
class AstCvtPackedToDynArray final : public AstNodeExpr {
|
||||
// Cast from packed array to dynamic queue data type
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
public:
|
||||
AstCvtPackedToDynArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_CvtPackedToDynArray(fl) {
|
||||
this->fromp(fromp);
|
||||
dtypeFrom(dtp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCvtPackedToDynArray;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
};
|
||||
class AstDot final : public AstNodeExpr {
|
||||
// A dot separating paths in an AstVarXRef, AstFuncRef or AstTaskRef
|
||||
// These are eliminated in the link stage
|
||||
@@ -1062,7 +1076,7 @@ public:
|
||||
};
|
||||
class AstEnumItemRef final : public AstNodeExpr {
|
||||
AstEnumItem* m_itemp; // [AfterLink] Pointer to item
|
||||
AstNodeModule* m_classOrPackagep; // Package hierarchy
|
||||
AstNodeModule* m_classOrPackagep; // Class/package in which it was defined
|
||||
public:
|
||||
AstEnumItemRef(FileLine* fl, AstEnumItem* itemp, AstNodeModule* classOrPackagep)
|
||||
: ASTGEN_SUPER_EnumItemRef(fl)
|
||||
@@ -1107,6 +1121,7 @@ public:
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool isPure() const override { return false; }
|
||||
bool same(const AstNode*) const override { return true; }
|
||||
};
|
||||
class AstFError final : public AstNodeExpr {
|
||||
@@ -1313,7 +1328,9 @@ public:
|
||||
bool cleanOut() const override { return true; }
|
||||
};
|
||||
class AstImplication final : public AstNodeExpr {
|
||||
// Verilog |-> |=>
|
||||
// Verilog Implication Operator
|
||||
// Nonoverlapping "|=>"
|
||||
// Overlapping "|->" (doesn't currently use this - might make new Ast type)
|
||||
// @astgen op1 := lhsp : AstNodeExpr
|
||||
// @astgen op2 := rhsp : AstNodeExpr
|
||||
// @astgen op3 := sentreep : Optional[AstSenTree]
|
||||
@@ -1428,6 +1445,7 @@ class AstMemberSel final : public AstNodeExpr {
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
// Don't need the class we are extracting from, as the "fromp()"'s datatype can get us to it
|
||||
string m_name;
|
||||
VAccess m_access; // Read or write, as in AstNodeVarRef
|
||||
AstVar* m_varp = nullptr; // Post link, variable within class that is target of selection
|
||||
public:
|
||||
AstMemberSel(FileLine* fl, AstNodeExpr* fromp, VFlagChildDType, const string& name)
|
||||
@@ -1448,10 +1466,12 @@ public:
|
||||
void dump(std::ostream& str) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
void name(const string& name) override { m_name = name; }
|
||||
VAccess access() const { return m_access; }
|
||||
void access(const VAccess& flag) { m_access = flag; }
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return 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(); }
|
||||
AstVar* varp() const { return m_varp; }
|
||||
void varp(AstVar* nodep) { m_varp = nodep; }
|
||||
@@ -1911,7 +1931,7 @@ public:
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstStructSel final : public AstNodeExpr {
|
||||
// Unpacked struct member access
|
||||
// Unpacked struct/union member access
|
||||
// Parents: math|stmt
|
||||
// Children: varref, math
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
@@ -1929,7 +1949,10 @@ public:
|
||||
void name(const string& name) override { m_name = name; }
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanOut() const override {
|
||||
// Not a union
|
||||
return VN_IS(fromp()->dtypep()->skipRefp(), StructDType);
|
||||
}
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstStructSel* const sp = static_cast<const AstStructSel*>(samep);
|
||||
return m_name == sp->m_name;
|
||||
@@ -4022,16 +4045,19 @@ public:
|
||||
// === AstNodeCCall ===
|
||||
class AstCCall final : public AstNodeCCall {
|
||||
// C++ function call
|
||||
string m_selfPointer; // Output code object pointer (e.g.: 'this')
|
||||
|
||||
VSelfPointerText m_selfPointer
|
||||
= VSelfPointerText{VSelfPointerText::Empty()}; // Output code object
|
||||
// pointer (e.g.: 'this')
|
||||
public:
|
||||
AstCCall(FileLine* fl, AstCFunc* funcp, AstNodeExpr* argsp = nullptr)
|
||||
: ASTGEN_SUPER_CCall(fl, funcp, argsp) {}
|
||||
ASTGEN_MEMBERS_AstCCall;
|
||||
|
||||
string selfPointer() const { return m_selfPointer; }
|
||||
void selfPointer(const string& value) { m_selfPointer = value; }
|
||||
string selfPointerProtect(bool useSelfForThis) const;
|
||||
const VSelfPointerText& selfPointer() const { return m_selfPointer; }
|
||||
void selfPointer(const VSelfPointerText& selfPointer) { m_selfPointer = selfPointer; }
|
||||
string selfPointerProtect(bool useSelfForThis) const {
|
||||
return selfPointer().protect(useSelfForThis, protect());
|
||||
}
|
||||
};
|
||||
class AstCMethodCall final : public AstNodeCCall {
|
||||
// C++ method call
|
||||
@@ -4671,7 +4697,7 @@ public:
|
||||
ASTGEN_MEMBERS_AstCvtPackString;
|
||||
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAssign(lhs); }
|
||||
string emitVerilog() override { return "%f$_CAST(%l)"; }
|
||||
string emitC() override { return "VL_CVT_PACK_STR_N%lq(%lW, %li)"; }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
@@ -5349,15 +5375,15 @@ public:
|
||||
class AstVarRef final : public AstNodeVarRef {
|
||||
// A reference to a variable (lvalue or rvalue)
|
||||
public:
|
||||
AstVarRef(FileLine* fl, const string& name, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarRef(fl, name, nullptr, access) {}
|
||||
// This form only allowed post-link because output/wire compression may
|
||||
// lead to deletion of AstVar's
|
||||
inline AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access);
|
||||
// This form only allowed post-link (see above)
|
||||
inline AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access);
|
||||
ASTGEN_MEMBERS_AstVarRef;
|
||||
inline string name() const override; // * = Var name
|
||||
void dump(std::ostream& str) const override;
|
||||
const char* broken() const override;
|
||||
bool same(const AstNode* samep) const override;
|
||||
inline bool same(const AstVarRef* samep) const;
|
||||
inline bool sameNoLvalue(AstVarRef* samep) const;
|
||||
@@ -5369,14 +5395,17 @@ public:
|
||||
class AstVarXRef final : public AstNodeVarRef {
|
||||
// A VarRef to something in another module before AstScope.
|
||||
// Includes pin on a cell, as part of a ASSIGN statement to connect I/Os until AstScope
|
||||
string m_name;
|
||||
string m_dotted; // Dotted part of scope the name()'ed reference is under or ""
|
||||
string m_inlinedDots; // Dotted hierarchy flattened out
|
||||
public:
|
||||
AstVarXRef(FileLine* fl, const string& name, const string& dotted, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarXRef(fl, name, nullptr, access)
|
||||
: ASTGEN_SUPER_VarXRef(fl, nullptr, access)
|
||||
, m_name{name}
|
||||
, m_dotted{dotted} {}
|
||||
inline AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access);
|
||||
ASTGEN_MEMBERS_AstVarXRef;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
void dump(std::ostream& str) const override;
|
||||
string dotted() const { return m_dotted; }
|
||||
void dotted(const string& dotted) { m_dotted = dotted; }
|
||||
|
||||
+78
-37
@@ -32,7 +32,7 @@
|
||||
|
||||
class AstNodeBlock VL_NOT_FINAL : public AstNode {
|
||||
// A Begin/fork block
|
||||
// @astgen op1 := stmtsp : List[AstNode]
|
||||
// @astgen op2 := stmtsp : List[AstNode]
|
||||
// Parents: statement
|
||||
private:
|
||||
string m_name; // Name of block
|
||||
@@ -81,12 +81,12 @@ private:
|
||||
bool m_isConstructor : 1; // Class constructor
|
||||
bool m_isHideLocal : 1; // Verilog local
|
||||
bool m_isHideProtected : 1; // Verilog protected
|
||||
bool m_pure : 1; // DPI import pure (vs. virtual pure)
|
||||
bool m_dpiPure : 1; // DPI import pure (vs. virtual pure)
|
||||
bool m_pureVirtual : 1; // Pure virtual
|
||||
bool m_recursive : 1; // Recursive or part of recursion
|
||||
bool m_underGenerate : 1; // Under generate (for warning)
|
||||
bool m_virtual : 1; // Virtual method in class
|
||||
bool m_fromStd : 1; // Part of std
|
||||
bool m_needProcess : 1; // Implements part of a process that allocates std::process
|
||||
VLifetime m_lifetime; // Lifetime
|
||||
protected:
|
||||
AstNodeFTask(VNType t, FileLine* fl, const string& name, AstNode* stmtsp)
|
||||
@@ -107,22 +107,25 @@ protected:
|
||||
, m_isConstructor{false}
|
||||
, m_isHideLocal{false}
|
||||
, m_isHideProtected{false}
|
||||
, m_pure{false}
|
||||
, m_dpiPure{false}
|
||||
, m_pureVirtual{false}
|
||||
, m_recursive{false}
|
||||
, m_underGenerate{false}
|
||||
, m_virtual{false}
|
||||
, m_fromStd{false} {
|
||||
, m_needProcess{false} {
|
||||
addStmtsp(stmtsp);
|
||||
cname(name); // Might be overridden by dpi import/export
|
||||
}
|
||||
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstNodeFTask;
|
||||
virtual AstNodeFTask* cloneType(const string& name) = 0;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
bool maybePointedTo() const override { return true; }
|
||||
bool isGateOptimizable() const override { return !((m_dpiExport || m_dpiImport) && !m_pure); }
|
||||
bool isGateOptimizable() const override {
|
||||
return !((m_dpiExport || m_dpiImport) && !m_dpiPure);
|
||||
}
|
||||
// {AstFunc only} op1 = Range output variable
|
||||
void name(const string& name) override { m_name = name; }
|
||||
string cname() const { return m_cname; }
|
||||
@@ -162,8 +165,8 @@ public:
|
||||
void isHideLocal(bool flag) { m_isHideLocal = flag; }
|
||||
bool isHideProtected() const { return m_isHideProtected; }
|
||||
void isHideProtected(bool flag) { m_isHideProtected = flag; }
|
||||
void pure(bool flag) { m_pure = flag; }
|
||||
bool pure() const { return m_pure; }
|
||||
void dpiPure(bool flag) { m_dpiPure = flag; }
|
||||
bool dpiPure() const { return m_dpiPure; }
|
||||
void pureVirtual(bool flag) { m_pureVirtual = flag; }
|
||||
bool pureVirtual() const { return m_pureVirtual; }
|
||||
void recursive(bool flag) { m_recursive = flag; }
|
||||
@@ -172,11 +175,20 @@ public:
|
||||
bool underGenerate() const { return m_underGenerate; }
|
||||
void isVirtual(bool flag) { m_virtual = flag; }
|
||||
bool isVirtual() const { return m_virtual; }
|
||||
void isFromStd(bool flag) { m_fromStd = flag; }
|
||||
bool isFromStd() const { return m_fromStd; }
|
||||
void setNeedProcess() { m_needProcess = true; }
|
||||
bool needProcess() const { return m_needProcess; }
|
||||
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
||||
VLifetime lifetime() const { return m_lifetime; }
|
||||
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
|
||||
void propagateAttrFrom(const AstNodeFTask* fromp) {
|
||||
// Creating a wrapper with e.g. cloneType(); preserve some attributes
|
||||
classMethod(fromp->classMethod());
|
||||
isHideLocal(fromp->isHideLocal());
|
||||
isHideProtected(fromp->isHideProtected());
|
||||
isVirtual(fromp->isVirtual());
|
||||
lifetime(fromp->lifetime());
|
||||
underGenerate(fromp->underGenerate());
|
||||
}
|
||||
};
|
||||
class AstNodeFile VL_NOT_FINAL : public AstNode {
|
||||
// Emitted Output file
|
||||
@@ -578,7 +590,7 @@ private:
|
||||
bool m_isInline : 1; // Inline function
|
||||
bool m_isVirtual : 1; // Virtual function
|
||||
bool m_entryPoint : 1; // User may call into this top level function
|
||||
bool m_pure : 1; // Pure function
|
||||
bool m_dpiPure : 1; // Pure DPI function
|
||||
bool m_dpiContext : 1; // Declared as 'context' DPI import/export function
|
||||
bool m_dpiExportDispatcher : 1; // This is the DPI export entry point (i.e.: called by user)
|
||||
bool m_dpiExportImpl : 1; // DPI export implementation (called from DPI dispatcher via lookup)
|
||||
@@ -607,7 +619,7 @@ public:
|
||||
m_isVirtual = false;
|
||||
m_needProcess = false;
|
||||
m_entryPoint = false;
|
||||
m_pure = false;
|
||||
m_dpiPure = false;
|
||||
m_dpiContext = false;
|
||||
m_dpiExportDispatcher = false;
|
||||
m_dpiExportImpl = false;
|
||||
@@ -678,8 +690,8 @@ public:
|
||||
void setNeedProcess() { m_needProcess = true; }
|
||||
bool entryPoint() const { return m_entryPoint; }
|
||||
void entryPoint(bool flag) { m_entryPoint = flag; }
|
||||
bool pure() const { return m_pure; }
|
||||
void pure(bool flag) { m_pure = flag; }
|
||||
bool dpiPure() const { return m_dpiPure; }
|
||||
void dpiPure(bool flag) { m_dpiPure = flag; }
|
||||
bool dpiContext() const { return m_dpiContext; }
|
||||
void dpiContext(bool flag) { m_dpiContext = flag; }
|
||||
bool dpiExportDispatcher() const VL_MT_SAFE { return m_dpiExportDispatcher; }
|
||||
@@ -699,6 +711,16 @@ public:
|
||||
&& finalsp() == nullptr;
|
||||
}
|
||||
};
|
||||
class AstCLocalScope final : public AstNode {
|
||||
// Pack statements into an unnamed scope when generating C++
|
||||
// @astgen op1 := stmtsp : List[AstNode]
|
||||
public:
|
||||
AstCLocalScope(FileLine* fl, AstNode* stmtsp)
|
||||
: ASTGEN_SUPER_CLocalScope(fl) {
|
||||
this->addStmtsp(stmtsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCLocalScope;
|
||||
};
|
||||
class AstCUse final : public AstNode {
|
||||
// C++ use of a class or #include; indicates need of forward declaration
|
||||
// Parents: NODEMODULE
|
||||
@@ -1378,7 +1400,6 @@ public:
|
||||
void dump(std::ostream& str) const override;
|
||||
bool same(const AstNode* samep) const override;
|
||||
string nameDotless() const;
|
||||
string nameVlSym() const { return string{"vlSymsp->"} + nameDotless(); }
|
||||
AstNodeModule* modp() const { return m_modp; }
|
||||
//
|
||||
AstScope* aboveScopep() const VL_MT_SAFE { return m_aboveScopep; }
|
||||
@@ -1513,6 +1534,7 @@ class AstTypeTable final : public AstNode {
|
||||
AstEmptyQueueDType* m_emptyQueuep = nullptr;
|
||||
AstQueueDType* m_queueIndexp = nullptr;
|
||||
AstVoidDType* m_voidp = nullptr;
|
||||
AstStreamDType* m_streamp = nullptr;
|
||||
AstBasicDType* m_basicps[VBasicDTypeKwd::_ENUM_MAX]{};
|
||||
//
|
||||
using DetailedMap = std::map<VBasicTypeKey, AstBasicDType*>;
|
||||
@@ -1538,6 +1560,7 @@ public:
|
||||
AstEmptyQueueDType* findEmptyQueueDType(FileLine* fl);
|
||||
AstQueueDType* findQueueIndexDType(FileLine* fl);
|
||||
AstVoidDType* findVoidDType(FileLine* fl);
|
||||
AstStreamDType* findStreamDType(FileLine* fl);
|
||||
void clearCache();
|
||||
void repairCache();
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
@@ -1810,7 +1833,7 @@ public:
|
||||
string dpiTmpVarType(const string& varName) const;
|
||||
// Return Verilator internal type for argument: CData, SData, IData, WData
|
||||
string vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc = "",
|
||||
bool asRef = false) const VL_MT_STABLE;
|
||||
bool asRef = false) const;
|
||||
string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc
|
||||
string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc
|
||||
string vlPropDecl(const string& propName) const; // Return VerilatorVarProps declaration
|
||||
@@ -1910,9 +1933,7 @@ public:
|
||||
bool isClassMember() const { return varType() == VVarType::MEMBER; }
|
||||
bool isStatementTemp() const { return (varType() == VVarType::STMTTEMP); }
|
||||
bool isXTemp() const { return (varType() == VVarType::XTEMP); }
|
||||
bool isParam() const VL_MT_SAFE {
|
||||
return (varType() == VVarType::LPARAM || varType() == VVarType::GPARAM);
|
||||
}
|
||||
bool isParam() const { return varType().isParam(); }
|
||||
bool isGParam() const { return (varType() == VVarType::GPARAM); }
|
||||
bool isGenVar() const { return (varType() == VVarType::GENVAR); }
|
||||
bool isBitLogic() const {
|
||||
@@ -1953,18 +1974,25 @@ public:
|
||||
string verilogKwd() const override;
|
||||
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
||||
VLifetime lifetime() const { return m_lifetime; }
|
||||
void propagateAttrFrom(AstVar* fromp) {
|
||||
void propagateAttrFrom(const AstVar* fromp) {
|
||||
// This is getting connected to fromp; keep attributes
|
||||
// Note the method below too
|
||||
if (fromp->attrFileDescr()) attrFileDescr(true);
|
||||
if (fromp->attrIsolateAssign()) attrIsolateAssign(true);
|
||||
if (fromp->isContinuously()) isContinuously(true);
|
||||
}
|
||||
void propagateWrapAttrFrom(const AstVar* fromp) {
|
||||
// Creating a function wrapper; keep attributes
|
||||
propagateAttrFrom(fromp);
|
||||
direction(fromp->direction());
|
||||
declDirection(fromp->declDirection());
|
||||
lifetime(fromp->lifetime());
|
||||
}
|
||||
bool gateMultiInputOptimizable() const {
|
||||
// Ok to gate optimize; must return false if propagateAttrFrom would do anything
|
||||
return !isUsedClock();
|
||||
}
|
||||
void combineType(AstVar* typevarp) {
|
||||
void combineType(const AstVar* typevarp) {
|
||||
// This is same as typevarp (for combining input & reg decls)
|
||||
// "this" is the input var. typevarp is the reg var.
|
||||
propagateAttrFrom(typevarp);
|
||||
@@ -2038,7 +2066,7 @@ public:
|
||||
class AstBegin final : public AstNodeBlock {
|
||||
// A Begin/end named block, only exists shortly after parsing until linking
|
||||
// Parents: statement
|
||||
// @astgen op2 := genforp : Optional[AstNode]
|
||||
// @astgen op1 := genforp : Optional[AstNode]
|
||||
|
||||
bool m_generate; // Underneath a generate
|
||||
const bool m_implied; // Not inserted by user
|
||||
@@ -2057,6 +2085,7 @@ public:
|
||||
};
|
||||
class AstFork final : public AstNodeBlock {
|
||||
// A fork named block
|
||||
// @astgen op1 := initsp : List[AstNode]
|
||||
// Parents: statement
|
||||
// Children: statements
|
||||
private:
|
||||
@@ -2082,6 +2111,24 @@ public:
|
||||
}
|
||||
ASTGEN_MEMBERS_AstFunc;
|
||||
bool hasDType() const override { return true; }
|
||||
AstNodeFTask* cloneType(const string& name) override {
|
||||
return new AstFunc{fileline(), name, nullptr, nullptr};
|
||||
}
|
||||
};
|
||||
class AstLet final : public AstNodeFTask {
|
||||
// Verilog "let" statement
|
||||
// Parents: MODULE
|
||||
// stmtp is always a StmtExpr as Let always returns AstNodeExpr
|
||||
public:
|
||||
AstLet(FileLine* fl, const string& name)
|
||||
: ASTGEN_SUPER_Let(fl, name, nullptr) {}
|
||||
ASTGEN_MEMBERS_AstLet;
|
||||
bool hasDType() const override { return true; }
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(!VN_IS(stmtsp(), StmtExpr));
|
||||
return nullptr;
|
||||
}
|
||||
AstNodeFTask* cloneType(const string& name) override { return new AstLet{fileline(), name}; }
|
||||
};
|
||||
class AstProperty final : public AstNodeFTask {
|
||||
// A property inside a module
|
||||
@@ -2090,6 +2137,9 @@ public:
|
||||
: ASTGEN_SUPER_Property(fl, name, stmtp) {}
|
||||
ASTGEN_MEMBERS_AstProperty;
|
||||
bool hasDType() const override { return true; }
|
||||
AstNodeFTask* cloneType(const string& name) override {
|
||||
return new AstProperty{fileline(), name, nullptr};
|
||||
}
|
||||
};
|
||||
class AstTask final : public AstNodeFTask {
|
||||
// A task inside a module
|
||||
@@ -2097,6 +2147,9 @@ public:
|
||||
AstTask(FileLine* fl, const string& name, AstNode* stmtp)
|
||||
: ASTGEN_SUPER_Task(fl, name, stmtp) {}
|
||||
ASTGEN_MEMBERS_AstTask;
|
||||
AstNodeFTask* cloneType(const string& name) override {
|
||||
return new AstTask{fileline(), name, nullptr};
|
||||
}
|
||||
};
|
||||
|
||||
// === AstNodeFile ===
|
||||
@@ -2135,17 +2188,13 @@ public:
|
||||
// === AstNodeModule ===
|
||||
class AstClass final : public AstNodeModule {
|
||||
// @astgen op4 := extendsp : List[AstClassExtends]
|
||||
// TYPES
|
||||
using MemberNameMap = std::map<const std::string, AstNode*>;
|
||||
// MEMBERS
|
||||
MemberNameMap m_members; // Members or method children
|
||||
AstClassPackage* m_classOrPackagep = nullptr; // Class package this is under
|
||||
AstClassPackage* m_classOrPackagep = nullptr; // Package it will be emitted with
|
||||
bool m_extended = false; // Is extension or extended by other classes
|
||||
bool m_interfaceClass = false; // Interface class
|
||||
bool m_needRNG = false; // Need RNG, uses srandom/randomize
|
||||
bool m_virtual = false; // Virtual class
|
||||
bool m_parameterized = false; // Parameterized class
|
||||
void insertCache(AstNode* nodep);
|
||||
|
||||
public:
|
||||
AstClass(FileLine* fl, const string& name)
|
||||
@@ -2160,16 +2209,7 @@ public:
|
||||
AstClassPackage* classOrPackagep() const VL_MT_SAFE { return m_classOrPackagep; }
|
||||
void classOrPackagep(AstClassPackage* classpackagep) { m_classOrPackagep = classpackagep; }
|
||||
AstNode* membersp() const { return stmtsp(); }
|
||||
void addMembersp(AstNode* nodep) {
|
||||
insertCache(nodep);
|
||||
addStmtsp(nodep);
|
||||
}
|
||||
void clearCache() { m_members.clear(); }
|
||||
void repairCache();
|
||||
AstNode* findMember(const string& name) const {
|
||||
const auto it = m_members.find(name);
|
||||
return (it == m_members.end()) ? nullptr : it->second;
|
||||
}
|
||||
void addMembersp(AstNode* nodep) { addStmtsp(nodep); }
|
||||
bool isExtended() const { return m_extended; }
|
||||
void isExtended(bool flag) { m_extended = flag; }
|
||||
bool isInterfaceClass() const { return m_interfaceClass; }
|
||||
@@ -2185,6 +2225,7 @@ public:
|
||||
static bool isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp);
|
||||
// Return the lowest class extended from, or this class
|
||||
AstClass* baseMostClassp();
|
||||
static bool isCacheableChild(const AstNode* nodep);
|
||||
};
|
||||
class AstClassPackage final : public AstNodeModule {
|
||||
// The static information portion of a class (treated similarly to a package)
|
||||
|
||||
+116
-62
@@ -25,6 +25,7 @@
|
||||
#include "V3Hasher.h"
|
||||
#include "V3PartitionGraph.h" // Just for mtask dumping
|
||||
#include "V3String.h"
|
||||
#include "V3Width.h"
|
||||
|
||||
#include "V3Ast__gen_macros.h" // Generated by 'astgen'
|
||||
|
||||
@@ -63,7 +64,7 @@ void AstNodeFTaskRef::cloneRelink() {
|
||||
bool AstNodeFTaskRef::isPure() const {
|
||||
// TODO: For non-DPI functions we could traverse the AST of function's body to determine
|
||||
// pureness.
|
||||
return this->taskp() && this->taskp()->dpiImport() && this->taskp()->pure();
|
||||
return this->taskp() && this->taskp()->dpiImport() && this->taskp()->dpiPure();
|
||||
}
|
||||
|
||||
bool AstNodeFTaskRef::isGateOptimizable() const { return m_taskp && m_taskp->isGateOptimizable(); }
|
||||
@@ -83,12 +84,6 @@ void AstNodeVarRef::cloneRelink() {
|
||||
}
|
||||
}
|
||||
|
||||
string AstNodeVarRef::selfPointerProtect(bool useSelfForThis) const {
|
||||
const string& sp
|
||||
= useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer();
|
||||
return VIdProtect::protectWordsIf(sp, protect());
|
||||
}
|
||||
|
||||
void AstAddrOfCFunc::cloneRelink() {
|
||||
if (m_funcp && m_funcp->clonep()) m_funcp = m_funcp->clonep();
|
||||
}
|
||||
@@ -103,29 +98,8 @@ int AstNodeSel::bitConst() const {
|
||||
return (constp ? constp->toSInt() : 0);
|
||||
}
|
||||
|
||||
void AstNodeUOrStructDType::repairMemberCache() {
|
||||
clearCache();
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
if (m_members.find(itemp->name()) != m_members.end()) {
|
||||
itemp->v3error("Duplicate declaration of member name: " << itemp->prettyNameQ());
|
||||
} else {
|
||||
m_members.emplace(itemp->name(), itemp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* AstNodeUOrStructDType::broken() const {
|
||||
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
|
||||
std::unordered_set<AstMemberDType*> exists;
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
exists.insert(itemp);
|
||||
}
|
||||
for (MemberNameMap::const_iterator it = m_members.begin(); it != m_members.end(); ++it) {
|
||||
if (VL_UNCOVERABLE(exists.find(it->second) == exists.end())) {
|
||||
this->v3error("Internal: Structure member broken: " << it->first);
|
||||
return "member broken";
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -147,14 +121,22 @@ const char* AstNodeCCall::broken() const {
|
||||
BROKEN_RTN(m_funcp && !m_funcp->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
bool AstNodeCCall::isPure() const { return funcp()->pure(); }
|
||||
bool AstNodeCCall::isPure() const { return funcp()->dpiPure(); }
|
||||
|
||||
string AstCCall::selfPointerProtect(bool useSelfForThis) const {
|
||||
const string& sp
|
||||
= useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer();
|
||||
return VIdProtect::protectWordsIf(sp, protect());
|
||||
AstNodeCond::AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp,
|
||||
AstNodeExpr* elsep)
|
||||
: AstNodeTriop{t, fl, condp, thenp, elsep} {
|
||||
UASSERT_OBJ(thenp, this, "No thenp expression");
|
||||
UASSERT_OBJ(elsep, this, "No elsep expression");
|
||||
if (thenp->isClassHandleValue() && elsep->isClassHandleValue()) {
|
||||
// Get the most-deriving class type that both arguments can be casted to.
|
||||
AstNodeDType* const commonClassTypep = V3Width::getCommonClassTypep(thenp, elsep);
|
||||
UASSERT_OBJ(commonClassTypep, this, "No common base class exists");
|
||||
dtypep(commonClassTypep);
|
||||
} else {
|
||||
dtypeFrom(thenp);
|
||||
}
|
||||
}
|
||||
|
||||
void AstNodeCond::numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
|
||||
const V3Number& ths) {
|
||||
if (lhs.isNeqZero()) {
|
||||
@@ -399,7 +381,7 @@ string AstVar::verilogKwd() const {
|
||||
}
|
||||
|
||||
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc,
|
||||
bool asRef) const VL_MT_STABLE {
|
||||
bool asRef) const {
|
||||
UASSERT_OBJ(!forReturn, this,
|
||||
"Internal data is never passed as return, but as first argument");
|
||||
string ostatic;
|
||||
@@ -716,12 +698,12 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef) const VL_MT_STABLE {
|
||||
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef) const {
|
||||
const CTypeRecursed info = cTypeRecurse(false);
|
||||
return info.render(name, isRef);
|
||||
}
|
||||
|
||||
AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const VL_MT_STABLE {
|
||||
AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
||||
// Legacy compound argument currently just passed through and unused
|
||||
CTypeRecursed info;
|
||||
|
||||
@@ -1071,6 +1053,15 @@ AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) {
|
||||
return m_voidp;
|
||||
}
|
||||
|
||||
AstStreamDType* AstTypeTable::findStreamDType(FileLine* fl) {
|
||||
if (VL_UNLIKELY(!m_streamp)) {
|
||||
AstStreamDType* const newp = new AstStreamDType{fl};
|
||||
addTypesp(newp);
|
||||
m_streamp = newp;
|
||||
}
|
||||
return m_streamp;
|
||||
}
|
||||
|
||||
AstQueueDType* AstTypeTable::findQueueIndexDType(FileLine* fl) {
|
||||
if (VL_UNLIKELY(!m_queueIndexp)) {
|
||||
AstQueueDType* const newp = new AstQueueDType{fl, AstNode::findUInt32DType(), nullptr};
|
||||
@@ -1418,29 +1409,10 @@ const char* AstClassPackage::broken() const {
|
||||
void AstClassPackage::cloneRelink() {
|
||||
if (m_classp && m_classp->clonep()) m_classp = m_classp->clonep();
|
||||
}
|
||||
void AstClass::insertCache(AstNode* nodep) {
|
||||
const bool doit = (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|
||||
|| (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto())
|
||||
|| VN_IS(nodep, CFunc));
|
||||
if (doit) {
|
||||
if (m_members.find(nodep->name()) != m_members.end()) {
|
||||
nodep->v3error("Duplicate declaration of member name: " << nodep->prettyNameQ());
|
||||
} else {
|
||||
m_members.emplace(nodep->name(), nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
void AstClass::repairCache() {
|
||||
clearCache();
|
||||
for (auto* itemp = membersp(); itemp; itemp = itemp->nextp()) {
|
||||
if (const auto* const scopep = VN_CAST(itemp, Scope)) {
|
||||
for (auto* blockp = scopep->blocksp(); blockp; blockp = blockp->nextp()) {
|
||||
insertCache(blockp);
|
||||
}
|
||||
} else {
|
||||
insertCache(itemp);
|
||||
}
|
||||
}
|
||||
bool AstClass::isCacheableChild(const AstNode* nodep) {
|
||||
return (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|
||||
|| (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto())
|
||||
|| VN_IS(nodep, CFunc));
|
||||
}
|
||||
AstClass* AstClass::baseMostClassp() {
|
||||
AstClass* basep = this;
|
||||
@@ -1667,6 +1639,12 @@ AstNodeUOrStructDType* AstMemberDType::getChildStructp() const {
|
||||
return VN_CAST(subdtp, NodeUOrStructDType); // Maybe nullptr
|
||||
}
|
||||
|
||||
bool AstMemberSel::same(const AstNode* samep) const {
|
||||
const AstMemberSel* const sp = static_cast<const AstMemberSel*>(samep);
|
||||
return sp != nullptr && access() == sp->access() && fromp()->same(sp->fromp())
|
||||
&& name() == sp->name() && varp()->same(sp->varp());
|
||||
}
|
||||
|
||||
void AstMemberSel::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
str << " -> ";
|
||||
@@ -2055,6 +2033,10 @@ void AstVoidDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "void";
|
||||
}
|
||||
void AstStreamDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "stream";
|
||||
}
|
||||
void AstVarScope::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (isTrace()) str << " [T]";
|
||||
@@ -2097,6 +2079,10 @@ void AstVarRef::dump(std::ostream& str) const {
|
||||
str << "UNLINKED";
|
||||
}
|
||||
}
|
||||
const char* AstVarRef::broken() const {
|
||||
BROKEN_RTN(!varp());
|
||||
return AstNodeVarRef::broken();
|
||||
}
|
||||
bool AstVarRef::same(const AstNode* samep) const {
|
||||
return same(static_cast<const AstVarRef*>(samep));
|
||||
}
|
||||
@@ -2301,7 +2287,7 @@ void AstCFile::dump(std::ostream& str) const {
|
||||
void AstCFunc::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (slow()) str << " [SLOW]";
|
||||
if (pure()) str << " [PURE]";
|
||||
if (dpiPure()) str << " [DPIPURE]";
|
||||
if (isStatic()) str << " [STATIC]";
|
||||
if (dpiExportDispatcher()) str << " [DPIED]";
|
||||
if (dpiExportImpl()) str << " [DPIEI]";
|
||||
@@ -2331,7 +2317,7 @@ void AstCAwait::dump(std::ostream& str) const {
|
||||
int AstCMethodHard::instrCount() const {
|
||||
if (AstBasicDType* const basicp = fromp()->dtypep()->basicp()) {
|
||||
// TODO: add a more structured description of library methods, rather than using string
|
||||
// matching. See #3715.
|
||||
// matching. See issue #3715.
|
||||
if (basicp->isTriggerVec() && m_name == "word") {
|
||||
// This is an important special case for scheduling so we compute it precisely,
|
||||
// it is simply a load.
|
||||
@@ -2340,6 +2326,74 @@ int AstCMethodHard::instrCount() const {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void AstCMethodHard::setPurity() {
|
||||
static const std::map<std::string, bool> isPureMethod{{"andNot", false},
|
||||
{"any", true},
|
||||
{"assign", false},
|
||||
{"at", true},
|
||||
{"atBack", true},
|
||||
{"awaitingCurrentTime", true},
|
||||
{"clear", false},
|
||||
{"clearFired", false},
|
||||
{"commit", false},
|
||||
{"delay", false},
|
||||
{"done", false},
|
||||
{"erase", false},
|
||||
{"evaluate", false},
|
||||
{"evaluation", false},
|
||||
{"exists", true},
|
||||
{"find", true},
|
||||
{"find_first", true},
|
||||
{"find_first_index", true},
|
||||
{"find_index", true},
|
||||
{"find_last", true},
|
||||
{"find_last_index", true},
|
||||
{"fire", false},
|
||||
{"first", false},
|
||||
{"init", false},
|
||||
{"insert", false},
|
||||
{"isFired", true},
|
||||
{"isTriggered", true},
|
||||
{"join", false},
|
||||
{"last", false},
|
||||
{"max", true},
|
||||
{"min", true},
|
||||
{"neq", true},
|
||||
{"next", false},
|
||||
{"pop", false},
|
||||
{"pop_back", false},
|
||||
{"pop_front", false},
|
||||
{"prev", false},
|
||||
{"push", false},
|
||||
{"push_back", false},
|
||||
{"push_front", false},
|
||||
{"r_and", true},
|
||||
{"r_or", true},
|
||||
{"r_product", true},
|
||||
{"r_sum", true},
|
||||
{"r_xor", true},
|
||||
{"renew", false},
|
||||
{"renew_copy", false},
|
||||
{"resume", false},
|
||||
{"reverse", false},
|
||||
{"rsort", false},
|
||||
{"set", false},
|
||||
{"shuffle", false},
|
||||
{"size", true},
|
||||
{"slice", true},
|
||||
{"sliceBackBack", true},
|
||||
{"sliceFrontBack", true},
|
||||
{"sort", false},
|
||||
{"thisOr", false},
|
||||
{"trigger", false},
|
||||
{"unique", true},
|
||||
{"unique_index", true},
|
||||
{"word", true}};
|
||||
|
||||
auto isPureIt = isPureMethod.find(name());
|
||||
UASSERT_OBJ(isPureIt != isPureMethod.end(), this, "Unknown purity of method " + name());
|
||||
m_pure = isPureIt->second;
|
||||
}
|
||||
const char* AstCFunc::broken() const {
|
||||
BROKEN_RTN((m_scopep && !m_scopep->brokeExists()));
|
||||
return nullptr;
|
||||
|
||||
+4
-38
@@ -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 {
|
||||
private:
|
||||
// NODE STATE
|
||||
@@ -105,7 +72,6 @@ private:
|
||||
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
|
||||
int m_ifDepth = 0; // Current if depth
|
||||
bool m_keepBegins = false; // True if begins should not be inlined
|
||||
std::set<AstVar*> m_staticFuncVars; // Static variables from m_ftaskp
|
||||
|
||||
// METHODS
|
||||
|
||||
@@ -212,7 +178,10 @@ private:
|
||||
m_ftaskp = nodep;
|
||||
m_liftedp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
RenameStaticVisitor{m_staticFuncVars, m_ftaskp, nodep};
|
||||
nodep->foreach([&](AstInitialStatic* const initp) {
|
||||
initp->unlinkFrBack();
|
||||
m_ftaskp->addHereThisAsNext(initp);
|
||||
});
|
||||
if (m_liftedp) {
|
||||
// Place lifted nodes at beginning of stmtsp, so Var nodes appear before referenced
|
||||
if (AstNode* const stmtsp = nodep->stmtsp()) {
|
||||
@@ -222,7 +191,6 @@ private:
|
||||
nodep->addStmtsp(m_liftedp);
|
||||
m_liftedp = nullptr;
|
||||
}
|
||||
m_staticFuncVars.clear();
|
||||
m_ftaskp = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -266,7 +234,6 @@ private:
|
||||
nodep->name(newName);
|
||||
nodep->unlinkFrBack();
|
||||
m_ftaskp->addHereThisAsNext(nodep);
|
||||
m_staticFuncVars.insert(nodep);
|
||||
nodep->funcLocal(false);
|
||||
} else if (m_unnamedScope != "") {
|
||||
// Rename it
|
||||
@@ -377,7 +344,6 @@ private:
|
||||
void visit(AstVarRef* nodep) override {
|
||||
if (nodep->varp()->user1()) { // It was converted
|
||||
UINFO(9, " relinVarRef " << nodep << endl);
|
||||
nodep->name(nodep->varp()->name());
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
+31
-12
@@ -147,22 +147,25 @@ bool V3Broken::isLinkable(const AstNode* nodep) { return s_linkableTable.isLinka
|
||||
// Check every node in tree
|
||||
|
||||
class BrokenCheckVisitor final : public VNVisitorConst {
|
||||
bool m_inScope = false; // Under AstScope
|
||||
|
||||
// Constants for marking we are under/not under a node
|
||||
const uint8_t m_brokenCntCurrentNotUnder = s_brokenCntGlobal.get(); // Top bit is clear
|
||||
const uint8_t m_brokenCntCurrentUnder = m_brokenCntCurrentNotUnder | 0x80; // Top bit is set
|
||||
|
||||
// Current CFunc, if any
|
||||
const AstCFunc* m_cfuncp = nullptr;
|
||||
// STATE - across all visitors
|
||||
// All local variables declared in current function
|
||||
std::unordered_set<const AstVar*> m_localVars;
|
||||
std::set<const AstVar*> m_localVars;
|
||||
// Variable references in current function that do not reference an in-scope local
|
||||
std::unordered_map<const AstVar*, const AstNodeVarRef*> m_suspectRefs;
|
||||
std::map<const AstVar*, const AstNodeVarRef*> m_suspectRefs;
|
||||
// Local variables declared in the scope of the current statement
|
||||
std::vector<std::unordered_set<const AstVar*>> m_localsStack;
|
||||
|
||||
// STATE - for current visit position (use VL_RESTORER)
|
||||
const AstCFunc* m_cfuncp = nullptr; // Current CFunc, if any
|
||||
bool m_inScope = false; // Under AstScope
|
||||
std::set<std::string> m_cFuncNames; // CFunc by name in current class/module
|
||||
|
||||
private:
|
||||
// METHODS
|
||||
static void checkWidthMin(const AstNode* nodep) {
|
||||
UASSERT_OBJ(nodep->width() == nodep->widthMin()
|
||||
|| v3Global.widthMinUsage() != VWidthMinUsage::MATCHES_WIDTH,
|
||||
@@ -220,6 +223,7 @@ private:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// VISITORS
|
||||
void visit(AstNodeAssign* nodep) override {
|
||||
processAndIterate(nodep);
|
||||
UASSERT_OBJ(!(v3Global.assertDTypesResolved() && nodep->brokeLhsMustBeLvalue()
|
||||
@@ -235,10 +239,20 @@ private:
|
||||
}
|
||||
void visit(AstScope* nodep) override {
|
||||
VL_RESTORER(m_inScope);
|
||||
{
|
||||
m_inScope = true;
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
m_inScope = true;
|
||||
VL_RESTORER(m_cFuncNames);
|
||||
m_cFuncNames.clear();
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_cFuncNames);
|
||||
m_cFuncNames.clear();
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
void visit(AstNodeUOrStructDType* nodep) override {
|
||||
VL_RESTORER(m_cFuncNames);
|
||||
m_cFuncNames.clear();
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
processAndIterate(nodep);
|
||||
@@ -260,12 +274,19 @@ private:
|
||||
}
|
||||
void visit(AstCFunc* nodep) override {
|
||||
UASSERT_OBJ(!m_cfuncp, nodep, "Nested AstCFunc");
|
||||
VL_RESTORER(m_cfuncp);
|
||||
m_cfuncp = nodep;
|
||||
m_localVars.clear();
|
||||
m_suspectRefs.clear();
|
||||
m_localsStack.clear();
|
||||
pushLocalScope();
|
||||
|
||||
// Check for duplicate names, otherwise linker might just ignore them
|
||||
string nameArgs = nodep->name();
|
||||
if (!nodep->argTypes().empty()) nameArgs += "(" + nodep->argTypes() + ")";
|
||||
UASSERT_OBJ(m_cFuncNames.emplace(nameArgs).second, nodep,
|
||||
"Duplicate cfunc name: '" << nameArgs << "'");
|
||||
|
||||
processAndIterate(nodep);
|
||||
|
||||
// Check suspect references are all to non-locals
|
||||
@@ -273,8 +294,6 @@ private:
|
||||
UASSERT_OBJ(m_localVars.count(pair.first) == 0, pair.second,
|
||||
"Local variable not in scope where referenced: " << pair.first);
|
||||
}
|
||||
|
||||
m_cfuncp = nullptr;
|
||||
}
|
||||
void visit(AstNodeIf* nodep) override {
|
||||
// Each branch is a separate local variable scope
|
||||
|
||||
+2
-2
@@ -117,7 +117,7 @@ public:
|
||||
callp->argTypes("vlSymsp");
|
||||
} else {
|
||||
if (m_type.isCoverage()) callp->argTypes("first");
|
||||
callp->selfPointer("this");
|
||||
callp->selfPointer(VSelfPointerText{VSelfPointerText::This()});
|
||||
}
|
||||
rootFuncp->addStmtsp(callp->makeStmt());
|
||||
}
|
||||
@@ -229,7 +229,7 @@ void V3CCtors::evalAsserts() {
|
||||
// if (signal & CONST(upper_non_clean_mask)) { fail; }
|
||||
AstVarRef* const vrefp
|
||||
= new AstVarRef{varp->fileline(), varp, VAccess::READ};
|
||||
vrefp->selfPointer("this");
|
||||
vrefp->selfPointer(VSelfPointerText{VSelfPointerText::This()});
|
||||
AstNodeExpr* newp = vrefp;
|
||||
if (varp->isWide()) {
|
||||
newp = new AstWordSel{
|
||||
|
||||
+19
-32
@@ -28,9 +28,11 @@
|
||||
#include "V3CUse.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3FileLine.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
@@ -46,52 +48,31 @@ class CUseVisitor final : public VNVisitor {
|
||||
|
||||
// MEMBERS
|
||||
AstNodeModule* const m_modp; // Current module
|
||||
std::set<std::pair<VUseType, std::string>> m_didUse; // What we already used
|
||||
bool m_dtypesImplOnly = false;
|
||||
std::map<std::string, std::pair<FileLine*, VUseType>> m_didUse; // What we already used
|
||||
|
||||
// METHODS
|
||||
void addNewUse(AstNode* nodep, VUseType useType, const string& name) {
|
||||
if (m_dtypesImplOnly
|
||||
&& (useType == VUseType::INT_INCLUDE || useType == VUseType::INT_FWD_CLASS))
|
||||
return;
|
||||
|
||||
if (m_didUse.emplace(useType, name).second) {
|
||||
AstCUse* const newp = new AstCUse{nodep->fileline(), useType, name};
|
||||
m_modp->addStmtsp(newp);
|
||||
UINFO(8, "Insert " << newp << endl);
|
||||
auto e = m_didUse.emplace(name, std::make_pair(nodep->fileline(), useType));
|
||||
if (e.second || ((e.first->second.second & useType) != useType)) {
|
||||
e.first->second.second = e.first->second.second | useType;
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstClassRefDType* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
|
||||
}
|
||||
void visit(AstCFunc* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateAndNextNull(nodep->argsp());
|
||||
|
||||
{
|
||||
VL_RESTORER(m_dtypesImplOnly);
|
||||
m_dtypesImplOnly = true;
|
||||
|
||||
iterateAndNextNull(nodep->initsp());
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
iterateAndNextNull(nodep->finalsp());
|
||||
}
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
}
|
||||
void visit(AstCCall* nodep) override { return; }
|
||||
void visit(AstCReturn* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (m_dtypesImplOnly) {
|
||||
for (AstNode* exprp = nodep->op1p(); exprp; exprp = exprp->nextp()) {
|
||||
if (exprp->dtypep()) iterate(exprp->dtypep());
|
||||
}
|
||||
} else {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
UASSERT(!nodep->user1SetOnce(), "Visited same return twice.");
|
||||
iterate(nodep->lhsp()->dtypep());
|
||||
}
|
||||
void visit(AstNodeDType* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (nodep->virtRefDTypep()) iterate(nodep->virtRefDTypep());
|
||||
if (nodep->virtRefDType2p()) iterate(nodep->virtRefDType2p());
|
||||
|
||||
@@ -106,7 +87,7 @@ class CUseVisitor final : public VNVisitor {
|
||||
}
|
||||
void visit(AstNode* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (nodep->dtypep() && !nodep->dtypep()->user1()) iterate(nodep->dtypep());
|
||||
if (nodep->dtypep()) iterate(nodep->dtypep());
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstCell* nodep) override {
|
||||
@@ -121,6 +102,12 @@ public:
|
||||
explicit CUseVisitor(AstNodeModule* modp)
|
||||
: m_modp(modp) {
|
||||
iterate(modp);
|
||||
|
||||
for (auto& used : m_didUse) {
|
||||
AstCUse* const newp = new AstCUse{used.second.first, used.second.second, used.first};
|
||||
m_modp->addStmtsp(newp);
|
||||
UINFO(8, "Insert " << newp << endl);
|
||||
}
|
||||
}
|
||||
~CUseVisitor() override = default;
|
||||
VL_UNCOPYABLE(CUseVisitor);
|
||||
|
||||
+83
-40
@@ -139,32 +139,42 @@ private:
|
||||
std::array<AstNode*, 1 << CASE_OVERLAP_WIDTH> m_valueItem;
|
||||
|
||||
// METHODS
|
||||
bool caseIsEnumComplete(AstCase* nodep, uint32_t numCases) {
|
||||
// Return true if case is across an enum, and every value in the case
|
||||
// statement corresponds to one of the enum values
|
||||
if (!nodep->uniquePragma() && !nodep->unique0Pragma()) return false;
|
||||
AstEnumDType* const enumDtp
|
||||
//! Determine whether we should check case items are complete
|
||||
//! @return Enum's dtype if should check, nullptr if shouldn't
|
||||
const AstEnumDType* getEnumCompletionCheckDType(const AstCase* const nodep) {
|
||||
if (!nodep->uniquePragma() && !nodep->unique0Pragma()) return nullptr;
|
||||
const AstEnumDType* const enumDtp
|
||||
= VN_CAST(nodep->exprp()->dtypep()->skipRefToEnump(), EnumDType);
|
||||
if (!enumDtp) return false; // Case isn't enum
|
||||
AstBasicDType* const basicp = enumDtp->subDTypep()->basicp();
|
||||
if (!basicp) return false; // Not simple type (perhaps IEEE illegal)
|
||||
if (basicp->width() > 32) return false;
|
||||
// Find all case values into a set
|
||||
std::set<uint32_t> caseSet;
|
||||
for (uint32_t i = 0; i < numCases; ++i) { // All case items
|
||||
if (m_valueItem[i]) caseSet.emplace(i);
|
||||
}
|
||||
// Find all enum values into a set
|
||||
std::set<uint32_t> enumSet;
|
||||
for (AstEnumItem* itemp = enumDtp->itemsp(); itemp;
|
||||
if (!enumDtp) return nullptr; // Case isn't enum
|
||||
const AstBasicDType* const basicp = enumDtp->subDTypep()->basicp();
|
||||
if (!basicp) return nullptr; // Not simple type (perhaps IEEE illegal)
|
||||
if (basicp->width() > 32) return nullptr;
|
||||
return enumDtp;
|
||||
}
|
||||
//! @return True if case items are complete, false if there are uncovered enums
|
||||
bool checkCaseEnumComplete(const AstCase* const nodep, const AstEnumDType* const dtype) {
|
||||
const uint32_t numCases = 1UL << m_caseWidth;
|
||||
for (AstEnumItem* itemp = dtype->itemsp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), EnumItem)) {
|
||||
AstConst* const econstp = VN_AS(itemp->valuep(), Const);
|
||||
const uint32_t val = econstp->toUInt();
|
||||
// UINFO(9, "Complete enum item " << val << ": " << itemp << endl);
|
||||
enumSet.emplace(val);
|
||||
V3Number nummask{itemp, econstp->width()};
|
||||
nummask.opBitsNonX(econstp->num());
|
||||
const uint32_t mask = nummask.toUInt();
|
||||
V3Number numval{itemp, econstp->width()};
|
||||
numval.opBitsOne(econstp->num());
|
||||
const uint32_t val = numval.toUInt();
|
||||
|
||||
for (uint32_t i = 0; i < numCases; ++i) {
|
||||
if ((i & mask) == val) {
|
||||
if (!m_valueItem[i]) {
|
||||
nodep->v3warn(CASEINCOMPLETE, "Enum item " << itemp->prettyNameQ()
|
||||
<< " not covered by case\n");
|
||||
return false; // enum has uncovered value by case items
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If sets match, all covered
|
||||
return (caseSet == enumSet);
|
||||
return true; // enum is fully covered
|
||||
}
|
||||
bool isCaseTreeFast(AstCase* nodep) {
|
||||
int width = 0;
|
||||
@@ -193,6 +203,8 @@ private:
|
||||
// We can cheat and use uint32_t's because we only support narrow case's
|
||||
bool reportedOverlap = false;
|
||||
bool reportedSubcase = false;
|
||||
bool hasDefaultCase = false;
|
||||
std::map<AstNode*, AstCaseItem*> caseItemMap; // case condition -> case item
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), CaseItem)) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp; icondp = icondp->nextp()) {
|
||||
@@ -202,6 +214,7 @@ private:
|
||||
if (neverItem(nodep, iconstp)) {
|
||||
// X in casez can't ever be executed
|
||||
} else {
|
||||
const bool isCondWildcard = iconstp->num().isAnyXZ();
|
||||
V3Number nummask{itemp, iconstp->width()};
|
||||
nummask.opBitsNonX(iconstp->num());
|
||||
const uint32_t mask = nummask.toUInt();
|
||||
@@ -210,16 +223,17 @@ private:
|
||||
const uint32_t val = numval.toUInt();
|
||||
|
||||
uint32_t firstOverlap = 0;
|
||||
bool foundOverlap = false;
|
||||
AstNode* overlappedCondp = nullptr;
|
||||
bool foundHit = false;
|
||||
for (uint32_t i = 0; i < numCases; ++i) {
|
||||
if ((i & mask) == val) {
|
||||
if (!m_valueItem[i]) {
|
||||
m_valueItem[i] = itemp;
|
||||
m_valueItem[i] = icondp;
|
||||
caseItemMap[icondp] = itemp;
|
||||
foundHit = true;
|
||||
} else if (!foundOverlap) {
|
||||
} else if (!overlappedCondp) {
|
||||
firstOverlap = i;
|
||||
foundOverlap = true;
|
||||
overlappedCondp = m_valueItem[i];
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
}
|
||||
}
|
||||
@@ -227,9 +241,19 @@ private:
|
||||
if (!nodep->priorityPragma()) {
|
||||
// If this case statement doesn't have the priority
|
||||
// keyword, we want to warn on any overlap.
|
||||
if (!reportedOverlap && foundOverlap) {
|
||||
icondp->v3warn(CASEOVERLAP, "Case values overlap (example pattern 0x"
|
||||
<< std::hex << firstOverlap << ")");
|
||||
if (!reportedOverlap && overlappedCondp) {
|
||||
std::ostringstream examplePattern;
|
||||
if (isCondWildcard) {
|
||||
examplePattern << " (example pattern 0x" << std::hex
|
||||
<< firstOverlap << ")";
|
||||
}
|
||||
icondp->v3warn(CASEOVERLAP,
|
||||
"Case conditions overlap"
|
||||
<< examplePattern.str() << "\n"
|
||||
<< icondp->warnContextPrimary() << '\n'
|
||||
<< overlappedCondp->warnOther()
|
||||
<< "... Location of overlapping condition\n"
|
||||
<< overlappedCondp->warnContextSecondary());
|
||||
reportedOverlap = true;
|
||||
}
|
||||
} else {
|
||||
@@ -240,7 +264,11 @@ private:
|
||||
if (!reportedSubcase && !foundHit) {
|
||||
icondp->v3warn(CASEOVERLAP,
|
||||
"Case item ignored: every matching value is covered "
|
||||
"by an earlier item");
|
||||
"by an earlier condition\n"
|
||||
<< icondp->warnContextPrimary() << '\n'
|
||||
<< overlappedCondp->warnOther()
|
||||
<< "... Location of previous condition\n"
|
||||
<< overlappedCondp->warnContextPrimary());
|
||||
reportedSubcase = true;
|
||||
}
|
||||
}
|
||||
@@ -251,17 +279,28 @@ private:
|
||||
for (uint32_t i = 0; i < numCases; ++i) {
|
||||
if (!m_valueItem[i]) m_valueItem[i] = itemp;
|
||||
}
|
||||
caseItemMap[itemp] = itemp;
|
||||
hasDefaultCase = true;
|
||||
}
|
||||
}
|
||||
if (!caseIsEnumComplete(nodep, numCases)) {
|
||||
for (uint32_t i = 0; i < numCases; ++i) {
|
||||
if (!m_valueItem[i]) {
|
||||
nodep->v3warn(CASEINCOMPLETE, "Case values incompletely covered "
|
||||
"(example pattern 0x"
|
||||
<< std::hex << i << ")");
|
||||
if (!hasDefaultCase) {
|
||||
const AstEnumDType* const dtype = getEnumCompletionCheckDType(nodep);
|
||||
if (dtype) {
|
||||
if (!checkCaseEnumComplete(nodep, dtype)) {
|
||||
// checkCaseEnumComplete has already warned of incompletion
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i = 0; i < numCases; ++i) {
|
||||
if (!m_valueItem[i]) { // has uncovered case
|
||||
nodep->v3warn(CASEINCOMPLETE, "Case values incompletely covered "
|
||||
"(example pattern 0x"
|
||||
<< std::hex << i << ")");
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,8 +313,10 @@ private:
|
||||
// Convert valueItem from AstCaseItem* to the expression
|
||||
// Not done earlier, as we may now have a nullptr because it's just a ";" NOP branch
|
||||
for (uint32_t i = 0; i < numCases; ++i) {
|
||||
if (AstCaseItem* const itemp = VN_AS(m_valueItem[i], CaseItem)) {
|
||||
m_valueItem[i] = itemp->stmtsp();
|
||||
if (AstNode* const condp = m_valueItem[i]) {
|
||||
AstCaseItem* caseItemp = caseItemMap[condp];
|
||||
UASSERT(caseItemp, "caseItemp should exist");
|
||||
m_valueItem[i] = caseItemp->stmtsp();
|
||||
}
|
||||
}
|
||||
return true; // All is fine
|
||||
@@ -543,10 +584,12 @@ private:
|
||||
}
|
||||
}
|
||||
//--------------------
|
||||
void visit(AstNode* nodep) override {
|
||||
if (VN_IS(nodep, Always)) m_alwaysp = nodep;
|
||||
void visit(AstAlways* nodep) override {
|
||||
VL_RESTORER(m_alwaysp)
|
||||
m_alwaysp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
|
||||
+1
-1
@@ -67,6 +67,7 @@ private:
|
||||
nodep->editCountInc();
|
||||
nodep->classOrPackagep(packagep);
|
||||
packagep->classp(nodep);
|
||||
packagep->timeunit(nodep->timeunit());
|
||||
v3Global.rootp()->addModulesp(packagep);
|
||||
// Add package to hierarchy
|
||||
AstCell* const cellp = new AstCell{packagep->fileline(),
|
||||
@@ -105,7 +106,6 @@ private:
|
||||
m_prefix = nodep->name() + "__02e"; // .
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
nodep->repairCache();
|
||||
}
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
// Visit for NodeModules that are not AstClass (AstClass is-a AstNodeModule)
|
||||
|
||||
+1
-5
@@ -95,6 +95,7 @@ private:
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), DynArrayDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), ClassRefDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), QueueDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), StreamDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) {
|
||||
} else {
|
||||
@@ -234,11 +235,6 @@ private:
|
||||
operandTriop(nodep);
|
||||
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 {
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
|
||||
+102
-38
@@ -1799,7 +1799,7 @@ private:
|
||||
UASSERT_OBJ((rstart + rwidth) == lstart, nodep,
|
||||
"tried to merge two selects which are not adjacent");
|
||||
AstSel* const newselp = new AstSel{
|
||||
lselp->fromp()->fileline(), rselp->fromp()->cloneTree(false), rstart, lwidth + rwidth};
|
||||
lselp->fromp()->fileline(), rselp->fromp()->unlinkFrBack(), rstart, lwidth + rwidth};
|
||||
UINFO(5, "merged two adjacent sel " << lselp << " and " << rselp << " to one " << newselp
|
||||
<< endl);
|
||||
|
||||
@@ -1809,6 +1809,7 @@ private:
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
void replaceConcatMerge(AstConcat* nodep) {
|
||||
// {llp OP lrp, rlp OP rrp} => {llp, rlp} OP {lrp, rrp}, where OP = AND/OR/XOR
|
||||
AstNodeBiop* const lp = VN_AS(nodep->lhsp(), NodeBiop);
|
||||
AstNodeBiop* const rp = VN_AS(nodep->rhsp(), NodeBiop);
|
||||
AstNodeExpr* const llp = lp->lhsp()->cloneTree(false);
|
||||
@@ -2052,7 +2053,7 @@ private:
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return true;
|
||||
}
|
||||
} else if (m_doV && VN_IS(nodep->lhsp(), Concat)) {
|
||||
} else if (m_doV && VN_IS(nodep->lhsp(), Concat) && nodep->isTreePureRecurse()) {
|
||||
bool need_temp = false;
|
||||
if (m_warn && !VN_IS(nodep, AssignDly)) { // Is same var on LHS and RHS?
|
||||
// Note only do this (need user4) when m_warn, which is
|
||||
@@ -2145,33 +2146,42 @@ private:
|
||||
return true;
|
||||
} else if (m_doV && VN_IS(nodep->rhsp(), StreamR)) {
|
||||
// The right-streaming operator on rhs of assignment does not
|
||||
// change the order of bits. Eliminate stream but keep its lhsp
|
||||
// Unlink the stuff
|
||||
AstNodeExpr* const srcp = VN_AS(nodep->rhsp(), StreamR)->lhsp()->unlinkFrBack();
|
||||
AstNode* const sizep = VN_AS(nodep->rhsp(), StreamR)->rhsp()->unlinkFrBack();
|
||||
AstNodeExpr* const streamp = VN_AS(nodep->rhsp(), StreamR)->unlinkFrBack();
|
||||
// change the order of bits. Eliminate stream but keep its lhsp.
|
||||
// Add a cast if needed.
|
||||
AstStreamR* const streamp = VN_AS(nodep->rhsp(), StreamR)->unlinkFrBack();
|
||||
AstNodeExpr* srcp = streamp->lhsp()->unlinkFrBack();
|
||||
AstNodeDType* const srcDTypep = srcp->dtypep();
|
||||
if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) {
|
||||
if (nodep->lhsp()->widthMin() > 64) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assignment of stream of dynamic "
|
||||
"array to a variable of size greater than 64");
|
||||
}
|
||||
srcp = new AstCvtDynArrayToPacked{srcp->fileline(), srcp, srcDTypep};
|
||||
}
|
||||
nodep->rhsp(srcp);
|
||||
// Cleanup
|
||||
VL_DO_DANGLING(sizep->deleteTree(), sizep);
|
||||
VL_DO_DANGLING(streamp->deleteTree(), streamp);
|
||||
// Further reduce, any of the nodes may have more reductions.
|
||||
return true;
|
||||
} else if (m_doV && VN_IS(nodep->lhsp(), StreamL)) {
|
||||
// Push the stream operator to the rhs of the assignment statement
|
||||
const int dWidth = VN_AS(nodep->lhsp(), StreamL)->lhsp()->width();
|
||||
const int sWidth = nodep->rhsp()->width();
|
||||
// Unlink the stuff
|
||||
AstNodeExpr* const dstp = VN_AS(nodep->lhsp(), StreamL)->lhsp()->unlinkFrBack();
|
||||
AstNodeExpr* streamp = VN_AS(nodep->lhsp(), StreamL)->unlinkFrBack();
|
||||
AstNodeExpr* streamp = nodep->lhsp()->unlinkFrBack();
|
||||
AstNodeExpr* const dstp = VN_AS(streamp, StreamL)->lhsp()->unlinkFrBack();
|
||||
AstNodeExpr* const srcp = nodep->rhsp()->unlinkFrBack();
|
||||
const int sWidth = srcp->width();
|
||||
const int dWidth = dstp->width();
|
||||
// Connect the rhs to the stream operator and update its width
|
||||
VN_AS(streamp, StreamL)->lhsp(srcp);
|
||||
streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED);
|
||||
// Shrink the RHS if necessary
|
||||
if (sWidth > dWidth) {
|
||||
if (VN_IS(srcp->dtypep(), DynArrayDType) || VN_IS(srcp->dtypep(), QueueDType)
|
||||
|| VN_IS(srcp->dtypep(), UnpackArrayDType)) {
|
||||
streamp->dtypeSetStream();
|
||||
} else {
|
||||
streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED);
|
||||
}
|
||||
if (dWidth == 0) {
|
||||
streamp = new AstCvtPackedToDynArray{nodep->fileline(), streamp, dstp->dtypep()};
|
||||
} else if (sWidth > dWidth) {
|
||||
streamp = new AstSel{streamp->fileline(), streamp, sWidth - dWidth, dWidth};
|
||||
}
|
||||
// Link the nodes back in
|
||||
nodep->lhsp(dstp);
|
||||
nodep->rhsp(streamp);
|
||||
return true;
|
||||
@@ -2179,23 +2189,35 @@ private:
|
||||
// The right stream operator on lhs of assignment statement does
|
||||
// not reorder bits. However, if the rhs is wider than the lhs,
|
||||
// then we select bits from the left-most, not the right-most.
|
||||
const int dWidth = VN_AS(nodep->lhsp(), StreamR)->lhsp()->width();
|
||||
const int sWidth = nodep->rhsp()->width();
|
||||
// Unlink the stuff
|
||||
AstNodeExpr* const dstp = VN_AS(nodep->lhsp(), StreamR)->lhsp()->unlinkFrBack();
|
||||
AstNode* const sizep = VN_AS(nodep->lhsp(), StreamR)->rhsp()->unlinkFrBack();
|
||||
AstNodeExpr* const streamp = VN_AS(nodep->lhsp(), StreamR)->unlinkFrBack();
|
||||
AstNodeExpr* const streamp = nodep->lhsp()->unlinkFrBack();
|
||||
AstNodeExpr* const dstp = VN_AS(streamp, StreamR)->lhsp()->unlinkFrBack();
|
||||
AstNodeExpr* srcp = nodep->rhsp()->unlinkFrBack();
|
||||
if (sWidth > dWidth) {
|
||||
const int sWidth = srcp->width();
|
||||
const int dWidth = dstp->width();
|
||||
if (dWidth == 0) {
|
||||
srcp = new AstCvtPackedToDynArray{nodep->fileline(), srcp, dstp->dtypep()};
|
||||
} else if (sWidth > dWidth) {
|
||||
srcp = new AstSel{streamp->fileline(), srcp, sWidth - dWidth, dWidth};
|
||||
}
|
||||
nodep->lhsp(dstp);
|
||||
nodep->rhsp(srcp);
|
||||
// Cleanup
|
||||
VL_DO_DANGLING(sizep->deleteTree(), sizep);
|
||||
VL_DO_DANGLING(streamp->deleteTree(), streamp);
|
||||
// Further reduce, any of the nodes may have more reductions.
|
||||
return true;
|
||||
} else if (m_doV && VN_IS(nodep->rhsp(), StreamL)) {
|
||||
AstNodeDType* const lhsDtypep = nodep->lhsp()->dtypep();
|
||||
AstStreamL* streamp = VN_AS(nodep->rhsp(), StreamL);
|
||||
AstNodeExpr* const srcp = streamp->lhsp();
|
||||
const AstNodeDType* const srcDTypep = srcp->dtypep();
|
||||
if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) {
|
||||
if (lhsDtypep->widthMin() > 64) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assignment of stream of dynamic "
|
||||
"array to a variable of size greater than 64");
|
||||
}
|
||||
srcp->unlinkFrBack();
|
||||
streamp->lhsp(new AstCvtDynArrayToPacked{srcp->fileline(), srcp, lhsDtypep});
|
||||
streamp->dtypeFrom(lhsDtypep);
|
||||
}
|
||||
} else if (m_doV && replaceAssignMultiSel(nodep)) {
|
||||
return true;
|
||||
}
|
||||
@@ -2283,6 +2305,13 @@ private:
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstCLocalScope* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->stmtsp()) {
|
||||
nodep->unlinkFrBack();
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstScope* nodep) override {
|
||||
// No ASSIGNW removals under scope, we've long eliminated INITIALs
|
||||
VL_RESTORER(m_wremove);
|
||||
@@ -3175,8 +3204,17 @@ private:
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstFuncRef* nodep) override {
|
||||
void visit(AstNodeCCall* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
m_hasJumpDelay = true; // As don't analyze inside tasks for timing controls
|
||||
}
|
||||
void visit(AstNodeFTaskRef* nodep) override {
|
||||
// Note excludes AstFuncRef as other visitor below
|
||||
iterateChildren(nodep);
|
||||
m_hasJumpDelay = true; // As don't analyze inside tasks for timing controls
|
||||
}
|
||||
void visit(AstFuncRef* nodep) override {
|
||||
visit(static_cast<AstNodeFTaskRef*>(nodep));
|
||||
if (m_params) { // Only parameters force us to do constant function call propagation
|
||||
replaceWithSimulation(nodep);
|
||||
}
|
||||
@@ -3650,6 +3688,7 @@ private:
|
||||
public:
|
||||
// Processing Mode Enum
|
||||
enum ProcMode : uint8_t {
|
||||
PROC_PARAMS_NOWARN,
|
||||
PROC_PARAMS,
|
||||
PROC_GENERATE,
|
||||
PROC_LIVE,
|
||||
@@ -3665,16 +3704,18 @@ public:
|
||||
, m_concswapNames{globalPass ? ("__Vconcswap_" + cvtToStr(s_globalPassNum++)) : ""} {
|
||||
// clang-format off
|
||||
switch (pmode) {
|
||||
case PROC_PARAMS: m_doV = true; m_doNConst = true; m_params = true;
|
||||
m_required = true; break;
|
||||
case PROC_GENERATE: m_doV = true; m_doNConst = true; m_params = true;
|
||||
m_required = true; m_doGenerate = true; break;
|
||||
case PROC_LIVE: break;
|
||||
case PROC_V_WARN: m_doV = true; m_doNConst = true; m_warn = true; break;
|
||||
case PROC_V_NOWARN: m_doV = true; m_doNConst = true; break;
|
||||
case PROC_V_EXPENSIVE: m_doV = true; m_doNConst = true; m_doExpensive = true; break;
|
||||
case PROC_CPP: m_doV = false; m_doNConst = true; m_doCpp = true; break;
|
||||
default: v3fatalSrc("Bad case"); break;
|
||||
case PROC_PARAMS_NOWARN: m_doV = true; m_doNConst = true; m_params = true;
|
||||
m_required = false; break;
|
||||
case PROC_PARAMS: m_doV = true; m_doNConst = true; m_params = true;
|
||||
m_required = true; break;
|
||||
case PROC_GENERATE: m_doV = true; m_doNConst = true; m_params = true;
|
||||
m_required = true; m_doGenerate = true; break;
|
||||
case PROC_LIVE: break;
|
||||
case PROC_V_WARN: m_doV = true; m_doNConst = true; m_warn = true; break;
|
||||
case PROC_V_NOWARN: m_doV = true; m_doNConst = true; break;
|
||||
case PROC_V_EXPENSIVE: m_doV = true; m_doNConst = true; m_doExpensive = true; break;
|
||||
case PROC_CPP: m_doV = false; m_doNConst = true; m_doCpp = true; break;
|
||||
default: v3fatalSrc("Bad case"); break;
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
@@ -3722,6 +3763,29 @@ AstNode* V3Const::constifyParamsEdit(AstNode* nodep) {
|
||||
return nodep;
|
||||
}
|
||||
|
||||
//! Constify this cell node's parameter list if possible
|
||||
//! @return Pointer to the edited node.
|
||||
AstNode* V3Const::constifyParamsNoWarnEdit(AstNode* nodep) {
|
||||
// if (debug() > 0) nodep->dumpTree("- forceConPRE : ");
|
||||
// Resize even if the node already has a width, because buried in the tree
|
||||
// we may have a node we just created with signing, etc, that isn't sized yet.
|
||||
|
||||
// Make sure we've sized everything first
|
||||
nodep = V3Width::widthParamsEdit(nodep);
|
||||
ConstVisitor visitor{ConstVisitor::PROC_PARAMS_NOWARN, /* globalPass: */ false};
|
||||
if (AstVar* const varp = VN_CAST(nodep, Var)) {
|
||||
// If a var wants to be constified, it's really a param, and
|
||||
// we want the value to be constant. We aren't passed just the
|
||||
// init value because we need widthing above to handle the var's type.
|
||||
if (varp->valuep()) visitor.mainAcceptEdit(varp->valuep());
|
||||
} else {
|
||||
nodep = visitor.mainAcceptEdit(nodep);
|
||||
}
|
||||
// Because we do edits, nodep links may get trashed and core dump this.
|
||||
// if (debug() > 0) nodep->dumpTree("- forceConDONE: ");
|
||||
return nodep;
|
||||
}
|
||||
|
||||
//! Force this cell node's parameter list to become a constant inside generate.
|
||||
//! If we are inside a generated "if", "case" or "for", we don't want to
|
||||
//! trigger warnings when we deal with the width. It is possible that these
|
||||
|
||||
@@ -30,6 +30,10 @@ public:
|
||||
static AstNodeExpr* constifyParamsEdit(AstNodeExpr* exprp) {
|
||||
return VN_AS(constifyParamsEdit(static_cast<AstNode*>(exprp)), NodeExpr);
|
||||
}
|
||||
static AstNode* constifyParamsNoWarnEdit(AstNode* nodep);
|
||||
static AstNodeExpr* constifyParamsNoWarnEdit(AstNodeExpr* exprp) {
|
||||
return VN_AS(constifyParamsNoWarnEdit(static_cast<AstNode*>(exprp)), NodeExpr);
|
||||
}
|
||||
static AstNode* constifyGenerateParamsEdit(AstNode* nodep);
|
||||
// Only do constant pushing, without removing dead logic
|
||||
static void constifyAllLive(AstNetlist* nodep);
|
||||
|
||||
@@ -288,7 +288,6 @@ private:
|
||||
VL_RESTORER(m_sideEffect);
|
||||
m_inAssign = true;
|
||||
m_sideEffect = false;
|
||||
if (assignInAssign) m_sideEffect = true;
|
||||
iterateAndNextNull(nodep->rhsp());
|
||||
checkAll(nodep);
|
||||
// Has to be direct assignment without any EXTRACTing.
|
||||
|
||||
+9
-1
@@ -551,7 +551,9 @@ private:
|
||||
}
|
||||
if (!lhsp->backp()) VL_DO_DANGLING(pushDeletep(lhsp), lhsp);
|
||||
} else {
|
||||
iterateChildren(nodep);
|
||||
iterate(nodep->lhsp());
|
||||
m_inDly = false;
|
||||
iterate(nodep->rhsp());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -623,6 +625,12 @@ private:
|
||||
m_inLoop = true;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstExprStmt* nodep) override {
|
||||
VL_RESTORER(m_inDly);
|
||||
// Restoring is needed, because AstExprStmt may contain assignments
|
||||
m_inDly = false;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
//--------------------
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
+37
-16
@@ -46,6 +46,10 @@ private:
|
||||
|
||||
// TYPES
|
||||
using FuncMmap = std::multimap<std::string, AstCFunc*>;
|
||||
struct ScopeSelfPtr final {
|
||||
VSelfPointerText thisPtr = VSelfPointerText{VSelfPointerText::Empty()};
|
||||
VSelfPointerText vlSymsPtr = VSelfPointerText{VSelfPointerText::Empty()};
|
||||
};
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
@@ -53,6 +57,7 @@ private:
|
||||
const AstCFunc* m_funcp = nullptr; // Current function
|
||||
bool m_modSingleton = false; // m_modp is only instantiated once
|
||||
FuncMmap m_modFuncs; // Name of public functions added
|
||||
std::map<const AstScope*, ScopeSelfPtr> m_scopeToSelf; // Scope to self pointers
|
||||
|
||||
// METHODS
|
||||
|
||||
@@ -68,12 +73,32 @@ private:
|
||||
return (instances == 1);
|
||||
}
|
||||
|
||||
// Construct a 'this' self pointer for the given scope
|
||||
VSelfPointerText scopeThis(const AstScope* scopep) {
|
||||
auto& ret = m_scopeToSelf[scopep];
|
||||
if (ret.thisPtr.isEmpty()) {
|
||||
string name = scopep->name();
|
||||
string::size_type pos;
|
||||
if ((pos = name.rfind('.')) != string::npos) name.erase(0, pos + 1);
|
||||
ret.thisPtr = VSelfPointerText{VSelfPointerText::This(), name};
|
||||
}
|
||||
return ret.thisPtr;
|
||||
}
|
||||
// Construct a 'vlSyms' self pointer for the given scope
|
||||
VSelfPointerText scopeVlSyms(const AstScope* scopep) {
|
||||
auto& ret = m_scopeToSelf[scopep];
|
||||
if (ret.vlSymsPtr.isEmpty()) {
|
||||
ret.vlSymsPtr = VSelfPointerText{VSelfPointerText::VlSyms(), scopep->nameDotless()};
|
||||
}
|
||||
return ret.vlSymsPtr;
|
||||
}
|
||||
|
||||
// Construct the best self pointer to reference an object in 'scopep' from a CFunc in
|
||||
// 'm_scopep'. Result may be relative ("this->[...]") or absolute ("vlSyms->[...]").
|
||||
//
|
||||
// Using relative references allows V3Combine'ing code across multiple instances of the same
|
||||
// module.
|
||||
string descopedSelfPointer(const AstScope* scopep) {
|
||||
VSelfPointerText descopedSelfPointer(const AstScope* scopep) {
|
||||
UASSERT(scopep, "Var/Func not scoped");
|
||||
// Static functions can't use relative references via 'this->'
|
||||
const bool relativeRefOk = !m_funcp->isStatic();
|
||||
@@ -85,23 +110,20 @@ private:
|
||||
if (VN_IS(scopep->modp(), Class)) {
|
||||
// Direct reference to class members are from within the class itself, references from
|
||||
// outside the class must go via AstMemberSel
|
||||
return "this";
|
||||
return VSelfPointerText{VSelfPointerText::This()};
|
||||
} else if (relativeRefOk && scopep == m_scopep) {
|
||||
return "this";
|
||||
return VSelfPointerText{VSelfPointerText::This()};
|
||||
} else if (relativeRefOk && !m_modSingleton && scopep->aboveScopep() == m_scopep
|
||||
&& VN_IS(scopep->modp(), Module)) {
|
||||
// Reference to scope of instance directly under this module, can just "this->cell",
|
||||
// which can potentially be V3Combined, but note this requires one extra pointer
|
||||
// dereference which is slower, so we only use it if the source scope is not a
|
||||
// singleton.
|
||||
string name = scopep->name();
|
||||
string::size_type pos;
|
||||
if ((pos = name.rfind('.')) != string::npos) name.erase(0, pos + 1);
|
||||
return "this->" + name;
|
||||
return scopeThis(scopep);
|
||||
} else {
|
||||
// Reference to something elsewhere, or relative references are disabled. Use global
|
||||
// variable
|
||||
return "(&" + scopep->nameVlSym() + ")";
|
||||
return scopeVlSyms(scopep);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,11 +185,10 @@ private:
|
||||
if (moreOfSame) {
|
||||
AstIf* const ifp = new AstIf{
|
||||
funcp->fileline(),
|
||||
new AstEq{
|
||||
funcp->fileline(), new AstCExpr{funcp->fileline(), "this", 64},
|
||||
new AstCExpr{funcp->fileline(),
|
||||
string{"&("} + funcp->scopep()->nameVlSym() + ")",
|
||||
64}},
|
||||
new AstEq{funcp->fileline(),
|
||||
new AstCExpr{funcp->fileline(), "this", 64},
|
||||
new AstCExpr{funcp->fileline(),
|
||||
scopeVlSyms(funcp->scopep()).asString(), 64}},
|
||||
returnp};
|
||||
newfuncp->addStmtsp(ifp);
|
||||
} else {
|
||||
@@ -225,15 +246,15 @@ private:
|
||||
const AstScope* const scopep = nodep->varScopep()->scopep();
|
||||
if (varp->isFuncLocal()) {
|
||||
// Reference to function locals need no self pointer
|
||||
nodep->selfPointer("");
|
||||
nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()});
|
||||
} else if (scopep->modp() == v3Global.rootp()->constPoolp()->modp()) {
|
||||
// Reference to constant pool value need no self pointer
|
||||
nodep->selfPointer("");
|
||||
nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()});
|
||||
} else {
|
||||
nodep->selfPointer(descopedSelfPointer(scopep));
|
||||
}
|
||||
nodep->varScopep(nullptr);
|
||||
UINFO(9, " refout " << nodep << " selfPtr=" << nodep->selfPointer() << endl);
|
||||
UINFO(9, " refout " << nodep << " selfPtr=" << nodep->selfPointer().asString() << endl);
|
||||
}
|
||||
void visit(AstCCall* nodep) override {
|
||||
// UINFO(9, " " << nodep << endl);
|
||||
|
||||
+2
-2
@@ -503,11 +503,11 @@ public:
|
||||
inline bool inlined() const;
|
||||
|
||||
// Methods that allow DfgVertex to participate in error reporting/messaging
|
||||
void v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
|
||||
m_filelinep->v3errorEnd(str);
|
||||
}
|
||||
void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN
|
||||
VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
VL_RELEASE(V3Error::s().m_mutex) {
|
||||
m_filelinep->v3errorEndFatal(str);
|
||||
}
|
||||
string warnContextPrimary() const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
|
||||
+2
-2
@@ -123,9 +123,9 @@ struct V3DfgPeepholeContext final {
|
||||
const std::string m_label; // Label to apply to stats
|
||||
|
||||
// Enable flags for each optimization
|
||||
bool m_enabled[VDfgPeepholePattern::_ENUM_END];
|
||||
std::array<bool, VDfgPeepholePattern::_ENUM_END> m_enabled;
|
||||
// Count of applications for each optimization (for statistics)
|
||||
VDouble0 m_count[VDfgPeepholePattern::_ENUM_END];
|
||||
std::array<VDouble0, VDfgPeepholePattern::_ENUM_END> m_count;
|
||||
|
||||
explicit V3DfgPeepholeContext(const std::string& label);
|
||||
~V3DfgPeepholeContext();
|
||||
|
||||
+5
-4
@@ -62,7 +62,7 @@ public:
|
||||
static string symClassAssign() {
|
||||
return symClassName() + "* const __restrict vlSymsp VL_ATTR_UNUSED = vlSelf->vlSymsp;\n";
|
||||
}
|
||||
static string prefixNameProtect(const AstNode* nodep) VL_MT_STABLE { // C++ name with prefix
|
||||
static string prefixNameProtect(const AstNode* nodep) { // C++ name with prefix
|
||||
return v3Global.opt.modPrefix() + "_" + VIdProtect::protect(nodep->name());
|
||||
}
|
||||
static bool isAnonOk(const AstVar* varp) {
|
||||
@@ -117,11 +117,12 @@ public:
|
||||
static void forModCUse(const AstNodeModule* modp, VUseType useType, F action) {
|
||||
for (AstNode* itemp = modp->stmtsp(); itemp; itemp = itemp->nextp()) {
|
||||
if (AstCUse* const usep = VN_CAST(itemp, CUse)) {
|
||||
if (usep->useType() == useType) {
|
||||
if (usep->useType().isInclude()) {
|
||||
if (usep->useType().containsAny(useType)) {
|
||||
if (usep->useType().containsAny(VUseType::INT_INCLUDE)) {
|
||||
action("#include \"" + prefixNameProtect(usep) + ".h\"\n");
|
||||
continue; // Forward declaration is not necessary
|
||||
}
|
||||
if (usep->useType().isFwdClass()) {
|
||||
if (usep->useType().containsAny(VUseType::INT_FWD_CLASS)) {
|
||||
action("class " + prefixNameProtect(usep) + ";\n");
|
||||
}
|
||||
}
|
||||
|
||||
+9
-4
@@ -108,7 +108,7 @@ void EmitCFunc::emitOpName(AstNode* nodep, const string& format, AstNode* lhsp,
|
||||
UASSERT_OBJ(m_wideTempRefp, nodep,
|
||||
"Wide Op w/ no temp, perhaps missing op in V3EmitC?");
|
||||
COMMA;
|
||||
if (!m_wideTempRefp->selfPointer().empty()) {
|
||||
if (!m_wideTempRefp->selfPointer().isEmpty()) {
|
||||
emitDereference(m_wideTempRefp->selfPointerProtect(m_useSelfForThis));
|
||||
}
|
||||
puts(m_wideTempRefp->varp()->nameProtect());
|
||||
@@ -314,6 +314,7 @@ void EmitCFunc::displayNode(AstNode* nodep, AstScopeName* scopenamep, const stri
|
||||
|
||||
// Convert Verilog display to C printf formats
|
||||
// "%0t" becomes "%d"
|
||||
VL_RESTORER(m_emitDispState);
|
||||
m_emitDispState.clear();
|
||||
string vfmt;
|
||||
string::const_iterator pos = vformat.begin();
|
||||
@@ -419,7 +420,7 @@ void EmitCFunc::emitCCallArgs(const AstNodeCCall* nodep, const string& selfPoint
|
||||
}
|
||||
if (nodep->funcp()->needProcess()) {
|
||||
if (comma) puts(", ");
|
||||
if (VN_IS(nodep->backp(), CAwait)) {
|
||||
if (VN_IS(nodep->backp(), CAwait) || !nodep->funcp()->isCoroutine()) {
|
||||
puts("vlProcess");
|
||||
} else {
|
||||
puts("std::make_shared<VlProcess>()");
|
||||
@@ -452,6 +453,10 @@ void EmitCFunc::emitCvtPackStr(AstNode* nodep) {
|
||||
putbs("std::string{");
|
||||
putsQuoted(constp->num().toString());
|
||||
puts("}");
|
||||
} else if (VN_IS(nodep->dtypep(), StreamDType)) {
|
||||
putbs("VL_CVT_PACK_STR_ND(");
|
||||
iterateAndNextConstNull(nodep);
|
||||
puts(")");
|
||||
} else {
|
||||
putbs("VL_CVT_PACK_STR_N");
|
||||
emitIQW(nodep);
|
||||
@@ -512,7 +517,7 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string
|
||||
if (!assigntop) {
|
||||
puts(assignString);
|
||||
} else {
|
||||
if (!assigntop->selfPointer().empty()) {
|
||||
if (!assigntop->selfPointer().isEmpty()) {
|
||||
emitDereference(assigntop->selfPointerProtect(m_useSelfForThis));
|
||||
}
|
||||
puts(assigntop->varp()->nameProtect());
|
||||
@@ -534,7 +539,7 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string
|
||||
if (!assigntop) {
|
||||
puts(assignString);
|
||||
} else {
|
||||
if (!assigntop->selfPointer().empty()) {
|
||||
if (!assigntop->selfPointer().isEmpty()) {
|
||||
emitDereference(assigntop->selfPointerProtect(m_useSelfForThis));
|
||||
}
|
||||
puts(assigntop->varp()->nameProtect());
|
||||
|
||||
+111
-28
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "V3EmitCConstInit.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3MemberMap.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
@@ -115,6 +116,7 @@ public:
|
||||
|
||||
class EmitCFunc VL_NOT_FINAL : public EmitCConstInit {
|
||||
private:
|
||||
VMemberMap m_memberMap;
|
||||
AstVarRef* m_wideTempRefp = nullptr; // Variable that _WW macros should be setting
|
||||
int m_labelNum = 0; // Next label number
|
||||
int m_splitSize = 0; // # of cfunc nodes placed into output file
|
||||
@@ -148,6 +150,22 @@ protected:
|
||||
bool m_useSelfForThis = false; // Replace "this" with "vlSelf"
|
||||
const AstNodeModule* m_modp = nullptr; // Current module being emitted
|
||||
const AstCFunc* m_cfuncp = nullptr; // Current function being emitted
|
||||
bool m_instantiatesOwnProcess = false;
|
||||
|
||||
bool constructorNeedsProcess(const AstClass* const classp) {
|
||||
const AstNode* const newp = m_memberMap.findMember(classp, "new");
|
||||
if (!newp) return false;
|
||||
const AstCFunc* const ctorp = VN_CAST(newp, CFunc);
|
||||
if (!ctorp) return false;
|
||||
UASSERT_OBJ(ctorp->isConstructor(), ctorp, "`new` is not a constructor!");
|
||||
return ctorp->needProcess();
|
||||
}
|
||||
|
||||
bool constructorNeedsProcess(const AstNodeDType* const dtypep) {
|
||||
if (const AstClassRefDType* const crefdtypep = VN_CAST(dtypep, ClassRefDType))
|
||||
return constructorNeedsProcess(crefdtypep->classp());
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
@@ -174,12 +192,10 @@ public:
|
||||
}
|
||||
void emitScIQW(AstVar* nodep) {
|
||||
UASSERT_OBJ(nodep->isSc(), nodep, "emitting SystemC operator on non-SC variable");
|
||||
// clang-format off
|
||||
puts(nodep->isScBigUint() ? "SB"
|
||||
: nodep->isScUint() ? "SU"
|
||||
: nodep->isScBv() ? "SW"
|
||||
: (nodep->isScQuad() ? "SQ" : "SI"));
|
||||
// clang-format on
|
||||
: (nodep->isScQuad() ? "SQ" : "SI"));
|
||||
}
|
||||
void emitDatap(AstNode* nodep) {
|
||||
// When passing to a function with va_args the compiler doesn't
|
||||
@@ -212,13 +228,34 @@ public:
|
||||
comma = true;
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
string optionalProcArg(const T* const nodep) {
|
||||
return (nodep && constructorNeedsProcess(nodep)) ? "vlProcess, " : "";
|
||||
}
|
||||
const AstCNew* getSuperNewCallRecursep(AstNode* const nodep) {
|
||||
// Get the super.new call
|
||||
if (!nodep) return nullptr;
|
||||
if (const AstCNew* const cnewp = VN_CAST(nodep, CNew)) return cnewp;
|
||||
if (const AstStmtExpr* const stmtp = VN_CAST(nodep, StmtExpr)) {
|
||||
if (const AstCNew* const cnewp = VN_CAST(stmtp->exprp(), CNew)) return cnewp;
|
||||
}
|
||||
if (const AstJumpBlock* const blockp = VN_CAST(nodep, JumpBlock)) {
|
||||
if (const AstCNew* const cnewp = getSuperNewCallRecursep(blockp->stmtsp())) {
|
||||
return cnewp;
|
||||
}
|
||||
}
|
||||
if (const AstCNew* const cnewp = getSuperNewCallRecursep(nodep->nextp())) return cnewp;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
using EmitCConstInit::visit;
|
||||
void visit(AstCFunc* nodep) override {
|
||||
VL_RESTORER(m_useSelfForThis);
|
||||
VL_RESTORER(m_cfuncp);
|
||||
VL_RESTORER(m_instantiatesOwnProcess)
|
||||
m_cfuncp = nodep;
|
||||
m_instantiatesOwnProcess = false;
|
||||
|
||||
splitSizeInc(nodep);
|
||||
|
||||
@@ -231,20 +268,16 @@ public:
|
||||
if (!nodep->baseCtors().empty()) {
|
||||
puts(": ");
|
||||
puts(nodep->baseCtors());
|
||||
puts("(vlSymsp");
|
||||
const AstClass* const classp = VN_CAST(nodep->scopep()->modp(), Class);
|
||||
// Find call to super.new to get the arguments
|
||||
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
AstNode* exprp;
|
||||
if (VN_IS(stmtp, StmtExpr)) {
|
||||
exprp = VN_CAST(stmtp, StmtExpr)->exprp();
|
||||
} else {
|
||||
exprp = stmtp;
|
||||
}
|
||||
if (AstCNew* const newRefp = VN_CAST(exprp, CNew)) {
|
||||
putCommaIterateNext(newRefp->argsp(), true);
|
||||
break;
|
||||
}
|
||||
if (classp && constructorNeedsProcess(classp)) {
|
||||
puts("(vlProcess, vlSymsp");
|
||||
} else {
|
||||
puts("(vlSymsp");
|
||||
}
|
||||
const AstCNew* const superNewCallp = getSuperNewCallRecursep(nodep->stmtsp());
|
||||
UASSERT_OBJ(superNewCallp, nodep, "super.new call not found");
|
||||
putCommaIterateNext(superNewCallp->argsp(), true);
|
||||
puts(")");
|
||||
}
|
||||
puts(" {\n");
|
||||
@@ -265,6 +298,23 @@ public:
|
||||
puts(nodep->isLoose() ? "__" : "::");
|
||||
puts(nodep->nameProtect() + "\\n\"); );\n");
|
||||
|
||||
// Instantiate a process class if it's going to be needed somewhere later
|
||||
nodep->forall([&](const AstNodeCCall* ccallp) -> bool {
|
||||
if (ccallp->funcp()->needProcess()
|
||||
&& (ccallp->funcp()->isCoroutine() == VN_IS(ccallp->backp(), CAwait))) {
|
||||
if (!nodep->needProcess() && !m_instantiatesOwnProcess) {
|
||||
m_instantiatesOwnProcess = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (m_instantiatesOwnProcess) {
|
||||
AstNode* const vlprocp = new AstCStmt{
|
||||
nodep->fileline(), "VlProcessRef vlProcess = std::make_shared<VlProcess>();\n"};
|
||||
nodep->stmtsp()->addHereThisAsNext(vlprocp);
|
||||
}
|
||||
|
||||
for (AstNode* subnodep = nodep->argsp(); subnodep; subnodep = subnodep->nextp()) {
|
||||
if (AstVar* const varp = VN_CAST(subnodep, Var)) {
|
||||
if (varp->isFuncReturn()) emitVarDecl(varp);
|
||||
@@ -295,6 +345,19 @@ public:
|
||||
emitVarDecl(nodep);
|
||||
}
|
||||
|
||||
void visit(AstCvtDynArrayToPacked* nodep) override {
|
||||
puts("VL_DYN_TO_");
|
||||
emitIQW(nodep);
|
||||
puts("<");
|
||||
const AstNodeDType* const elemDTypep = nodep->fromp()->dtypep()->subDTypep();
|
||||
putbs(elemDTypep->cType("", false, false));
|
||||
puts(">(");
|
||||
iterateAndNextConstNull(nodep->fromp());
|
||||
puts(", ");
|
||||
puts(cvtToStr(elemDTypep->widthMin()));
|
||||
puts(")");
|
||||
}
|
||||
|
||||
void visit(AstNodeAssign* nodep) override {
|
||||
bool paren = true;
|
||||
bool decind = false;
|
||||
@@ -349,6 +412,19 @@ public:
|
||||
puts(cvtToStr(nodep->widthMin()) + ",");
|
||||
iterateAndNextConstNull(nodep->lhsp());
|
||||
puts(", ");
|
||||
} else if (const AstCvtPackedToDynArray* const castp
|
||||
= VN_CAST(nodep->rhsp(), CvtPackedToDynArray)) {
|
||||
puts("VL_ASSIGN_DYN_Q<");
|
||||
putbs(castp->dtypep()->subDTypep()->cType("", false, false));
|
||||
puts(">(");
|
||||
iterateAndNextConstNull(nodep->lhsp());
|
||||
puts(", ");
|
||||
puts(cvtToStr(castp->dtypep()->subDTypep()->widthMin()));
|
||||
puts(", ");
|
||||
puts(cvtToStr(castp->fromp()->widthMin()));
|
||||
puts(", ");
|
||||
rhs = false;
|
||||
iterateAndNextConstNull(castp->fromp());
|
||||
} else if (nodep->isWide() && VN_IS(nodep->lhsp(), VarRef) //
|
||||
&& !VN_IS(nodep->rhsp(), CExpr) //
|
||||
&& !VN_IS(nodep->rhsp(), CMethodHard) //
|
||||
@@ -356,7 +432,8 @@ public:
|
||||
&& !VN_IS(nodep->rhsp(), AssocSel) //
|
||||
&& !VN_IS(nodep->rhsp(), MemberSel) //
|
||||
&& !VN_IS(nodep->rhsp(), StructSel) //
|
||||
&& !VN_IS(nodep->rhsp(), ArraySel)) {
|
||||
&& !VN_IS(nodep->rhsp(), ArraySel) //
|
||||
&& !VN_IS(nodep->rhsp(), ExprStmt)) {
|
||||
// Wide functions assign into the array directly, don't need separate assign statement
|
||||
m_wideTempRefp = VN_AS(nodep->lhsp(), VarRef);
|
||||
paren = false;
|
||||
@@ -383,19 +460,17 @@ public:
|
||||
void visit(AstAssocSel* nodep) override {
|
||||
iterateAndNextConstNull(nodep->fromp());
|
||||
putbs(".at(");
|
||||
AstAssocArrayDType* const adtypep = VN_AS(nodep->fromp()->dtypep(), AssocArrayDType);
|
||||
AstAssocArrayDType* const adtypep
|
||||
= VN_AS(nodep->fromp()->dtypep()->skipRefp(), AssocArrayDType);
|
||||
UASSERT_OBJ(adtypep, nodep, "Associative select on non-associative type");
|
||||
if (adtypep->keyDTypep()->isWide()) {
|
||||
emitCvtWideArray(nodep->bitp(), nodep->fromp());
|
||||
} else {
|
||||
iterateAndNextConstNull(nodep->bitp());
|
||||
}
|
||||
iterateAndNextConstNull(nodep->bitp());
|
||||
puts(")");
|
||||
}
|
||||
void visit(AstWildcardSel* nodep) override {
|
||||
iterateAndNextConstNull(nodep->fromp());
|
||||
putbs(".at(");
|
||||
AstWildcardArrayDType* const adtypep = VN_AS(nodep->fromp()->dtypep(), WildcardArrayDType);
|
||||
AstWildcardArrayDType* const adtypep
|
||||
= VN_AS(nodep->fromp()->dtypep()->skipRefp(), WildcardArrayDType);
|
||||
UASSERT_OBJ(adtypep, nodep, "Wildcard select on non-wildcard-associative type");
|
||||
iterateAndNextConstNull(nodep->bitp());
|
||||
puts(")");
|
||||
@@ -419,7 +494,7 @@ public:
|
||||
puts(funcNameProtect(funcp));
|
||||
} else {
|
||||
// Calling regular method/function
|
||||
if (!nodep->selfPointer().empty()) {
|
||||
if (!nodep->selfPointer().isEmpty()) {
|
||||
emitDereference(nodep->selfPointerProtect(m_useSelfForThis));
|
||||
}
|
||||
puts(funcp->nameProtect());
|
||||
@@ -443,8 +518,9 @@ public:
|
||||
// super.new case
|
||||
return;
|
||||
}
|
||||
// assignment case
|
||||
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", vlSymsp");
|
||||
// assignment case;
|
||||
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", "
|
||||
+ optionalProcArg(nodep->dtypep()) + "vlSymsp");
|
||||
putCommaIterateNext(nodep->argsp(), true);
|
||||
puts(")");
|
||||
}
|
||||
@@ -832,6 +908,11 @@ public:
|
||||
iterateAndNextConstNull(nodep->endStmtsp());
|
||||
puts("}\n");
|
||||
}
|
||||
void visit(AstCLocalScope* nodep) override {
|
||||
puts("{\n");
|
||||
iterateAndNextConstNull(nodep->stmtsp());
|
||||
puts("}\n");
|
||||
}
|
||||
void visit(AstJumpGo* nodep) override {
|
||||
puts("goto __Vlabel" + cvtToStr(nodep->labelp()->blockp()->labelNum()) + ";\n");
|
||||
}
|
||||
@@ -1026,6 +1107,7 @@ public:
|
||||
UASSERT_OBJ(!emitSimpleOk(nodep), nodep, "Triop cannot be described in a simple way");
|
||||
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nodep->thsp());
|
||||
}
|
||||
void visit(AstCvtPackString* nodep) override { emitCvtPackStr(nodep->lhsp()); }
|
||||
void visit(AstRedXor* nodep) override {
|
||||
if (nodep->lhsp()->isWide()) {
|
||||
visit(static_cast<AstNodeUniop*>(nodep));
|
||||
@@ -1088,7 +1170,8 @@ public:
|
||||
puts(")");
|
||||
}
|
||||
void visit(AstNewCopy* nodep) override {
|
||||
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", ");
|
||||
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", "
|
||||
+ optionalProcArg(nodep->dtypep()));
|
||||
puts("*"); // i.e. make into a reference
|
||||
iterateAndNextConstNull(nodep->rhsp());
|
||||
puts(")");
|
||||
@@ -1182,7 +1265,7 @@ public:
|
||||
} else if (varp->isIfaceRef()) {
|
||||
puts(nodep->selfPointerProtect(m_useSelfForThis));
|
||||
return;
|
||||
} else if (!nodep->selfPointer().empty()) {
|
||||
} else if (!nodep->selfPointer().isEmpty()) {
|
||||
emitDereference(nodep->selfPointerProtect(m_useSelfForThis));
|
||||
}
|
||||
puts(nodep->varp()->nameProtect());
|
||||
|
||||
@@ -380,11 +380,10 @@ class EmitCHeader final : public EmitCConstInit {
|
||||
std::set<string> cuse_set;
|
||||
auto add_to_cuse_set = [&](string s) { cuse_set.insert(s); };
|
||||
|
||||
forModCUse(modp, VUseType::INT_INCLUDE, add_to_cuse_set);
|
||||
forModCUse(modp, VUseType::INT_FWD_CLASS, add_to_cuse_set);
|
||||
forModCUse(modp, VUseType::INT_FWD_CLASS | VUseType::INT_INCLUDE, add_to_cuse_set);
|
||||
if (const AstClassPackage* const packagep = VN_CAST(modp, ClassPackage)) {
|
||||
forModCUse(packagep->classp(), VUseType::INT_INCLUDE, add_to_cuse_set);
|
||||
forModCUse(packagep->classp(), VUseType::INT_FWD_CLASS, add_to_cuse_set);
|
||||
forModCUse(packagep->classp(), VUseType::INT_INCLUDE | VUseType::INT_FWD_CLASS,
|
||||
add_to_cuse_set);
|
||||
}
|
||||
|
||||
for (const string& s : cuse_set) puts(s);
|
||||
|
||||
+12
-13
@@ -59,18 +59,18 @@ class EmitCGatherDependencies final : VNVisitorConst {
|
||||
}
|
||||
}
|
||||
}
|
||||
void addSelfDependency(const string& selfPointer, AstNode* nodep) {
|
||||
if (selfPointer.empty()) {
|
||||
void addSelfDependency(VSelfPointerText selfPointer, AstNode* nodep) {
|
||||
if (selfPointer.isEmpty()) {
|
||||
// No self pointer (e.g.: function locals, const pool values, loose static methods),
|
||||
// so no dependency
|
||||
} else if (VString::startsWith(selfPointer, "this")) {
|
||||
} else if (selfPointer.hasThis()) {
|
||||
// Dereferencing 'this', we need the definition of this module, which is also the
|
||||
// module that contains the variable.
|
||||
addModDependency(EmitCParentModule::get(nodep));
|
||||
} else {
|
||||
// Must be an absolute reference
|
||||
UASSERT_OBJ(selfPointer.find("vlSymsp") != string::npos, nodep,
|
||||
"Unknown self pointer: '" << selfPointer << "'");
|
||||
UASSERT_OBJ(selfPointer.isVlSym(), nodep,
|
||||
"Unknown self pointer: '" << selfPointer.asString() << "'");
|
||||
// Dereferencing vlSymsp, so we need it's definition...
|
||||
addSymsDependency();
|
||||
}
|
||||
@@ -147,7 +147,7 @@ class EmitCGatherDependencies final : VNVisitorConst {
|
||||
}
|
||||
|
||||
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};
|
||||
return std::move(visitor.m_dependencies);
|
||||
}
|
||||
@@ -566,8 +566,7 @@ class EmitCImp final : EmitCFunc {
|
||||
~EmitCImp() override = default;
|
||||
|
||||
public:
|
||||
static void main(const AstNodeModule* modp, bool slow,
|
||||
std::deque<AstCFile*>& cfilesr) VL_MT_STABLE {
|
||||
static void main(const AstNodeModule* modp, bool slow, std::deque<AstCFile*>& cfilesr) {
|
||||
EmitCImp{modp, slow, cfilesr};
|
||||
}
|
||||
};
|
||||
@@ -678,7 +677,7 @@ class EmitCTrace final : EmitCFunc {
|
||||
string fstvt;
|
||||
// Doubles have special decoding properties, so must indicate if a double
|
||||
if (nodep->dtypep()->basicp()->isDouble()) {
|
||||
if (vartype == VVarType::GPARAM || vartype == VVarType::LPARAM) {
|
||||
if (vartype.isParam()) {
|
||||
fstvt = "FST_VT_VCD_REAL_PARAMETER";
|
||||
} else {
|
||||
fstvt = "FST_VT_VCD_REAL";
|
||||
@@ -932,11 +931,11 @@ void V3EmitC::emitcImp() {
|
||||
const AstNodeModule* const modp = VN_AS(nodep, NodeModule);
|
||||
cfiles.emplace_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); }));
|
||||
cfiles.emplace_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); }));
|
||||
}
|
||||
|
||||
@@ -944,12 +943,12 @@ void V3EmitC::emitcImp() {
|
||||
if (v3Global.opt.trace() && !v3Global.opt.lintOnly()) {
|
||||
cfiles.emplace_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);
|
||||
}));
|
||||
cfiles.emplace_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);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -234,6 +234,12 @@ class EmitCModel final : public EmitCFunc {
|
||||
puts("const char* hierName() const override final;\n");
|
||||
puts("const char* modelName() const override final;\n");
|
||||
puts("unsigned threads() const override final;\n");
|
||||
puts("/// Prepare for cloning the model at the process level (e.g. fork in Linux)\n");
|
||||
puts("/// Release necessary resources. Called before cloning.\n");
|
||||
puts("void prepareClone() const;\n");
|
||||
puts("/// Re-init after cloning the model at the process level (e.g. fork in Linux)\n");
|
||||
puts("/// Re-allocate necessary resources. Called after cloning.\n");
|
||||
puts("void atClone() const;\n");
|
||||
if (v3Global.opt.trace()) {
|
||||
puts("std::unique_ptr<VerilatedTraceConfig> traceConfig() const override final;\n");
|
||||
}
|
||||
@@ -479,6 +485,15 @@ class EmitCModel final : public EmitCFunc {
|
||||
+ "\"; }\n");
|
||||
puts("unsigned " + topClassName() + "::threads() const { return "
|
||||
+ cvtToStr(std::max(1, v3Global.opt.threads())) + "; }\n");
|
||||
puts("void " + topClassName()
|
||||
+ "::prepareClone() const { contextp()->prepareClone(); }\n");
|
||||
puts("void " + topClassName() + "::atClone() const {\n");
|
||||
if (v3Global.opt.threads() > 1) {
|
||||
puts("vlSymsp->__Vm_threadPoolp = static_cast<VlThreadPool*>(");
|
||||
}
|
||||
puts("contextp()->threadPoolpOnClone()");
|
||||
if (v3Global.opt.threads() > 1) puts(")");
|
||||
puts(";\n}\n");
|
||||
|
||||
if (v3Global.opt.trace()) {
|
||||
puts("std::unique_ptr<VerilatedTraceConfig> " + topClassName()
|
||||
|
||||
+1
-1
@@ -468,7 +468,7 @@ void EmitCSyms::emitSymHdr() {
|
||||
|
||||
if (v3Global.opt.mtasks()) {
|
||||
puts("\n// MULTI-THREADING\n");
|
||||
puts("VlThreadPool* const __Vm_threadPoolp;\n");
|
||||
puts("VlThreadPool* __Vm_threadPoolp;\n");
|
||||
puts("bool __Vm_even_cycle__ico = false;\n");
|
||||
puts("bool __Vm_even_cycle__act = false;\n");
|
||||
puts("bool __Vm_even_cycle__nba = false;\n");
|
||||
|
||||
+9
-2
@@ -664,6 +664,12 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
|
||||
iterateAndNextConstNull(nodep->pinsp());
|
||||
puts(")");
|
||||
}
|
||||
void visit(AstCCall* nodep) override {
|
||||
puts(nodep->funcp()->name());
|
||||
puts("(");
|
||||
iterateAndNextConstNull(nodep->argsp());
|
||||
puts(")");
|
||||
}
|
||||
void visit(AstArg* nodep) override { iterateAndNextConstNull(nodep->exprp()); }
|
||||
void visit(AstPrintTimeScale* nodep) override {
|
||||
puts(nodep->verilogKwd());
|
||||
@@ -676,10 +682,11 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
|
||||
putfs(nodep, nodep->varScopep()->prettyName());
|
||||
} else {
|
||||
if (nodep->varp()) {
|
||||
if (nodep->selfPointer().empty()) {
|
||||
if (nodep->selfPointer().isEmpty()) {
|
||||
putfs(nodep, nodep->varp()->prettyName());
|
||||
} else {
|
||||
putfs(nodep, nodep->selfPointer() + "->");
|
||||
putfs(nodep, nodep->selfPointer().asString());
|
||||
putfs(nodep, "->");
|
||||
puts(nodep->varp()->prettyName());
|
||||
}
|
||||
} else {
|
||||
|
||||
+44
-14
@@ -222,20 +222,18 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra)
|
||||
#ifndef V3ERROR_NO_GLOBAL_
|
||||
if (dumpTreeLevel() || debug()) {
|
||||
V3Broken::allowMidvisitorCheck(true);
|
||||
V3ThreadPool::s().requestExclusiveAccess([&]() VL_REQUIRES(m_mutex) {
|
||||
if (dumpTreeLevel()) {
|
||||
v3Global.rootp()->dumpTreeFile(
|
||||
v3Global.debugFilename("final.tree", 990));
|
||||
}
|
||||
if (debug()) {
|
||||
execErrorExitCb();
|
||||
V3Stats::statsFinalAll(v3Global.rootp());
|
||||
V3Stats::statsReport();
|
||||
}
|
||||
// Abort in exclusive access to make sure other threads
|
||||
// don't change error code
|
||||
vlAbortOrExit();
|
||||
});
|
||||
const V3ThreadPool::ScopedExclusiveAccess exclusiveAccess;
|
||||
if (dumpTreeLevel()) {
|
||||
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("final.tree", 990));
|
||||
}
|
||||
if (debug()) {
|
||||
execErrorExitCb();
|
||||
V3Stats::statsFinalAll(v3Global.rootp());
|
||||
V3Stats::statsReport();
|
||||
}
|
||||
// Abort in exclusive access to make sure other threads
|
||||
// don't change error code
|
||||
vlAbortOrExit();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -293,3 +291,35 @@ void V3Error::vlAbort() {
|
||||
VL_GCOV_DUMP();
|
||||
std::abort();
|
||||
}
|
||||
void V3Error::v3errorAcquireLock() VL_ACQUIRE(s().m_mutex) {
|
||||
#ifndef V3ERROR_NO_GLOBAL_
|
||||
V3Error::s().m_mutex.lockCheckStopRequest(
|
||||
[]() -> void { V3ThreadPool::s().waitIfStopRequested(); });
|
||||
#else
|
||||
V3Error::s().m_mutex.lock();
|
||||
#endif
|
||||
}
|
||||
std::ostringstream& V3Error::v3errorPrep(V3ErrorCode code) VL_ACQUIRE(s().m_mutex) {
|
||||
v3errorAcquireLock();
|
||||
s().v3errorPrep(code);
|
||||
return v3errorStr();
|
||||
}
|
||||
std::ostringstream& V3Error::v3errorPrepFileLine(V3ErrorCode code, const char* file, int line)
|
||||
VL_ACQUIRE(s().m_mutex) {
|
||||
v3errorPrep(code) << file << ":" << std::dec << line << ": ";
|
||||
return v3errorStr();
|
||||
}
|
||||
std::ostringstream& V3Error::v3errorStr() VL_REQUIRES(s().m_mutex) { return s().v3errorStr(); }
|
||||
void V3Error::v3errorEnd(std::ostringstream& sstr, const string& extra) VL_RELEASE(s().m_mutex) {
|
||||
s().v3errorEnd(sstr, extra);
|
||||
V3Error::s().m_mutex.unlock();
|
||||
}
|
||||
|
||||
void v3errorEnd(std::ostringstream& sstr) VL_RELEASE(V3Error::s().m_mutex) {
|
||||
V3Error::v3errorEnd(sstr);
|
||||
}
|
||||
void v3errorEndFatal(std::ostringstream& sstr) VL_RELEASE(V3Error::s().m_mutex) {
|
||||
V3Error::v3errorEnd(sstr);
|
||||
assert(0); // LCOV_EXCL_LINE
|
||||
VL_UNREACHABLE;
|
||||
}
|
||||
|
||||
+53
-80
@@ -60,6 +60,7 @@ public:
|
||||
I_TRACING, // Tracing is on/off from /*verilator tracing_on/off*/
|
||||
I_UNUSED, // Unused genvar, parameter or signal message (Backward Compatibility)
|
||||
// Error codes:
|
||||
E_LIFETIME, // Error: Reference to a variable might outlive the variable.
|
||||
E_NEEDTIMINGOPT, // Error: --timing/--no-timing option not specified
|
||||
E_NOTIMING, // Timing control encountered with --no-timing
|
||||
E_PORTSHORT, // Error: Output port is connected to a constant, electrical short
|
||||
@@ -98,6 +99,7 @@ public:
|
||||
ENUMVALUE, // Error: enum type needs explicit cast
|
||||
EOFNEWLINE, // End-of-file missing newline
|
||||
GENCLK, // Generated Clock. Historical, never issued.
|
||||
GENUNNAMED, // Generate unnamed, without label
|
||||
HIERBLOCK, // Ignored hierarchical block setting
|
||||
IFDEPTH, // If statements too deep
|
||||
IGNOREDRETURN, // Ignoring return value (function as task)
|
||||
@@ -113,6 +115,7 @@ public:
|
||||
LATCH, // Latch detected outside of always_latch block
|
||||
LITENDIAN, // Little endian, renamed to ASCRANGE
|
||||
MINTYPMAXDLY, // Unsupported: min/typ/max delay expressions
|
||||
MISINDENT, // Misleading indentation
|
||||
MODDUP, // Duplicate module
|
||||
MULTIDRIVEN, // Driven from multiple blocks
|
||||
MULTITOP, // Multiple top level modules
|
||||
@@ -183,7 +186,7 @@ public:
|
||||
// Boolean
|
||||
" I_CELLDEFINE", " I_COVERAGE", " I_DEF_NETTYPE_WIRE", " I_LINT", " I_TIMING", " I_TRACING", " I_UNUSED",
|
||||
// Errors
|
||||
"NEEDTIMINGOPT", "NOTIMING", "PORTSHORT", "TASKNSVAR", "UNSUPPORTED",
|
||||
"LIFETIME", "NEEDTIMINGOPT", "NOTIMING", "PORTSHORT", "TASKNSVAR", "UNSUPPORTED",
|
||||
// Warnings
|
||||
" EC_FIRST_WARN",
|
||||
"ALWCOMBORDER", "ASCRANGE", "ASSIGNDLY", "ASSIGNIN", "BADSTDPRAGMA",
|
||||
@@ -191,11 +194,12 @@ public:
|
||||
"CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA",
|
||||
"CMPCONST", "COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG",
|
||||
"DECLFILENAME", "DEFPARAM", "DEPRECATED",
|
||||
"ENCAPSULATED", "ENDLABEL", "ENUMVALUE", "EOFNEWLINE", "GENCLK", "HIERBLOCK",
|
||||
"ENCAPSULATED", "ENDLABEL", "ENUMVALUE", "EOFNEWLINE", "GENCLK", "GENUNNAMED",
|
||||
"HIERBLOCK",
|
||||
"IFDEPTH", "IGNOREDRETURN",
|
||||
"IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE",
|
||||
"INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE",
|
||||
"LATCH", "LITENDIAN", "MINTYPMAXDLY", "MODDUP",
|
||||
"LATCH", "LITENDIAN", "MINTYPMAXDLY", "MISINDENT", "MODDUP",
|
||||
"MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOLATCH", "NULLPORT", "PINCONNECTEMPTY",
|
||||
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PROCASSWIRE",
|
||||
"PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY",
|
||||
@@ -234,18 +238,18 @@ public:
|
||||
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 == CMPCONST || m_e == COLONPLUS || m_e == IMPLICIT || m_e == IMPLICITSTATIC
|
||||
|| m_e == LATCH || m_e == NEWERSTD || m_e == PINMISSING || m_e == REALCVT
|
||||
|| m_e == STATICVAR || m_e == UNSIGNED || m_e == WIDTH || m_e == WIDTHTRUNC
|
||||
|| m_e == WIDTHEXPAND || m_e == WIDTHXZEXPAND);
|
||||
|| m_e == LATCH || m_e == MISINDENT || m_e == NEWERSTD || m_e == PINMISSING
|
||||
|| m_e == REALCVT || m_e == STATICVAR || m_e == UNSIGNED || m_e == WIDTH
|
||||
|| m_e == WIDTHTRUNC || m_e == WIDTHEXPAND || m_e == WIDTHXZEXPAND);
|
||||
}
|
||||
// Warnings that are style only
|
||||
bool styleError() const VL_MT_SAFE {
|
||||
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 == IMPORTSTAR || m_e == INCABSPATH || m_e == PINCONNECTEMPTY
|
||||
|| m_e == PINNOCONNECT || m_e == SYNCASYNCNET || m_e == UNDRIVEN
|
||||
|| m_e == UNUSEDGENVAR || m_e == UNUSEDPARAM || m_e == UNUSEDSIGNAL
|
||||
|| m_e == VARHIDDEN);
|
||||
|| m_e == GENUNNAMED || m_e == IMPORTSTAR || m_e == INCABSPATH
|
||||
|| m_e == PINCONNECTEMPTY || m_e == PINNOCONNECT || m_e == SYNCASYNCNET
|
||||
|| m_e == UNDRIVEN || m_e == UNUSEDGENVAR || m_e == UNUSEDPARAM
|
||||
|| m_e == UNUSEDSIGNAL || m_e == VARHIDDEN);
|
||||
}
|
||||
// Warnings that are unused only
|
||||
bool unusedError() const VL_MT_SAFE {
|
||||
@@ -282,9 +286,13 @@ inline std::ostream& operator<<(std::ostream& os, const V3ErrorCode& rhs) {
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
class V3Error;
|
||||
|
||||
class V3ErrorGuarded final {
|
||||
// Should only be used by V3ErrorGuarded::m_mutex is already locked
|
||||
// contains guarded members
|
||||
friend class V3Error;
|
||||
|
||||
public:
|
||||
using MessagesSet = std::set<std::string>;
|
||||
using ErrorExitCb = void (*)(void);
|
||||
@@ -315,6 +323,16 @@ private:
|
||||
= MAX_ERRORS; // Option: --error-limit Number of errors before exit
|
||||
bool m_warnFatal VL_GUARDED_BY(m_mutex) = true; // Option: --warnFatal Warnings are fatal
|
||||
std::ostringstream m_errorStr VL_GUARDED_BY(m_mutex); // Error string being formed
|
||||
|
||||
void v3errorPrep(V3ErrorCode code) VL_REQUIRES(m_mutex) {
|
||||
m_errorStr.str("");
|
||||
m_errorCode = code;
|
||||
m_errorContexted = false;
|
||||
m_errorSuppressed = false;
|
||||
}
|
||||
std::ostringstream& v3errorStr() VL_REQUIRES(m_mutex) { return m_errorStr; }
|
||||
void v3errorEnd(std::ostringstream& sstr, const string& extra = "") VL_REQUIRES(m_mutex);
|
||||
|
||||
public:
|
||||
V3RecursiveMutex m_mutex; // Make sure only single thread is in class
|
||||
|
||||
@@ -335,7 +353,7 @@ public:
|
||||
v3errorEnd(
|
||||
(v3errorPrep(V3ErrorCode::EC_FATALEXIT),
|
||||
(v3errorStr() << "Exiting due to too many errors encountered; --error-limit="
|
||||
<< errorCount() << endl),
|
||||
<< errorCount() << std::endl),
|
||||
v3errorStr()));
|
||||
assert(0); // LCOV_EXCL_LINE
|
||||
VL_UNREACHABLE;
|
||||
@@ -357,13 +375,6 @@ public:
|
||||
int errorLimit() VL_REQUIRES(m_mutex) { return m_errorLimit; }
|
||||
void warnFatal(bool flag) VL_REQUIRES(m_mutex) { m_warnFatal = flag; }
|
||||
bool warnFatal() VL_REQUIRES(m_mutex) { return m_warnFatal; }
|
||||
void v3errorPrep(V3ErrorCode code) VL_REQUIRES(m_mutex) {
|
||||
m_errorStr.str("");
|
||||
m_errorCode = code;
|
||||
m_errorContexted = false;
|
||||
m_errorSuppressed = false;
|
||||
}
|
||||
std::ostringstream& v3errorStr() VL_REQUIRES(m_mutex) { return m_errorStr; }
|
||||
V3ErrorCode errorCode() VL_REQUIRES(m_mutex) { return m_errorCode; }
|
||||
bool errorContexted() VL_REQUIRES(m_mutex) { return m_errorContexted; }
|
||||
int warnCount() VL_REQUIRES(m_mutex) { return m_warnCount; }
|
||||
@@ -381,7 +392,6 @@ public:
|
||||
void describedWarnings(bool flag) VL_REQUIRES(m_mutex) { m_describedWarnings = flag; }
|
||||
int tellManual() VL_REQUIRES(m_mutex) { return m_tellManual; }
|
||||
void tellManual(int level) VL_REQUIRES(m_mutex) { m_tellManual = level; }
|
||||
void v3errorEnd(std::ostringstream& sstr, const string& extra = "") VL_REQUIRES(m_mutex);
|
||||
void suppressThisWarning() VL_REQUIRES(m_mutex);
|
||||
string warnContextNone() VL_REQUIRES(m_mutex) {
|
||||
errorContexted(true);
|
||||
@@ -508,66 +518,32 @@ public:
|
||||
|
||||
// Internals for v3error()/v3fatal() macros only
|
||||
// Error end takes the string stream to output, be careful to seek() as needed
|
||||
static void v3errorPrep(V3ErrorCode code) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
|
||||
const V3RecursiveLockGuard guard{s().m_mutex};
|
||||
s().v3errorPrep(code);
|
||||
}
|
||||
static std::ostringstream& v3errorStr() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
|
||||
const V3RecursiveLockGuard guard{s().m_mutex};
|
||||
return s().v3errorStr();
|
||||
}
|
||||
static void vlAbort();
|
||||
static void v3errorAcquireLock() VL_ACQUIRE(s().m_mutex);
|
||||
static std::ostringstream& v3errorPrep(V3ErrorCode code) VL_ACQUIRE(s().m_mutex);
|
||||
static std::ostringstream& v3errorPrepFileLine(V3ErrorCode code, const char* file, int line)
|
||||
VL_ACQUIRE(s().m_mutex);
|
||||
static std::ostringstream& v3errorStr() VL_REQUIRES(s().m_mutex);
|
||||
// static, but often overridden in classes.
|
||||
static void v3errorEnd(std::ostringstream& sstr, const string& extra = "")
|
||||
VL_MT_SAFE_EXCLUDES(s().m_mutex) VL_MT_SAFE {
|
||||
const V3RecursiveLockGuard guard{s().m_mutex};
|
||||
s().v3errorEnd(sstr, extra);
|
||||
}
|
||||
// We can't call 's().v3errorEnd' directly in 'v3ErrorEnd'/'v3errorEndFatal',
|
||||
// due to bug in GCC (tested on 11.3.0 version with --enable-m32)
|
||||
// causing internal error when backtrace is printed.
|
||||
// Instead use this wrapper.
|
||||
static void v3errorEndGuardedCall(std::ostringstream& sstr, const string& extra = "")
|
||||
VL_REQUIRES(s().m_mutex) VL_MT_SAFE {
|
||||
s().v3errorEnd(sstr, extra);
|
||||
}
|
||||
VL_RELEASE(s().m_mutex);
|
||||
static void vlAbort();
|
||||
};
|
||||
|
||||
// Global versions, so that if the class doesn't define a operator, we get the functions anyways.
|
||||
inline void v3errorEnd(std::ostringstream& sstr) VL_REQUIRES(V3Error::s().m_mutex) VL_MT_SAFE {
|
||||
V3Error::v3errorEndGuardedCall(sstr);
|
||||
}
|
||||
inline void v3errorEndFatal(std::ostringstream& sstr)
|
||||
VL_REQUIRES(V3Error::s().m_mutex) VL_MT_SAFE {
|
||||
V3Error::v3errorEndGuardedCall(sstr);
|
||||
assert(0); // LCOV_EXCL_LINE
|
||||
VL_UNREACHABLE;
|
||||
}
|
||||
|
||||
#ifndef V3ERROR_NO_GLOBAL_
|
||||
#define V3ErrorLockAndCheckStopRequested \
|
||||
V3Error::s().m_mutex.lockCheckStopRequest( \
|
||||
[]() -> void { V3ThreadPool::s().waitIfStopRequested(); })
|
||||
#else
|
||||
#define V3ErrorLockAndCheckStopRequested V3Error::s().m_mutex.lock()
|
||||
#endif
|
||||
// Global versions, so that if the class doesn't define an operator, we get the functions anyway.
|
||||
void v3errorEnd(std::ostringstream& sstr) VL_RELEASE(V3Error::s().m_mutex);
|
||||
void v3errorEndFatal(std::ostringstream& sstr) VL_RELEASE(V3Error::s().m_mutex) VL_ATTR_NORETURN;
|
||||
|
||||
// Theses allow errors using << operators: v3error("foo"<<"bar");
|
||||
// Careful, you can't put () around msg, as you would in most macro definitions
|
||||
// Note the commas are the comma operator, not separating arguments. These are needed to ensure
|
||||
// evaluation order as otherwise we couldn't ensure v3errorPrep is called first.
|
||||
// Note: due to limitations of clang thread-safety analysis, we can't use
|
||||
// lock guard here, instead we are locking the mutex as first operation in temporary,
|
||||
// but we are unlocking the mutex after function using comma operator.
|
||||
// This way macros should also work when they are in 'if' stmt without '{}'.
|
||||
#define v3warnCode(code, msg) \
|
||||
v3errorEnd((V3ErrorLockAndCheckStopRequested, V3Error::s().v3errorPrep(code), \
|
||||
(V3Error::s().v3errorStr() << msg), V3Error::s().v3errorStr())), \
|
||||
V3Error::s().m_mutex.unlock()
|
||||
// Careful, you can't put () around msg, as you would in most macro definitions.
|
||||
// 'V3Error::v3errorPrep(code) << msg' could be more efficient but the order of function calls in a
|
||||
// single statement can be arbitrary until C++17, thus make it possible to execute
|
||||
// V3Error::v3errorPrep that acquires the lock after functions in the msg may require it. So we use
|
||||
// the comma operator (,) to guarantee the execution order here.
|
||||
#define v3errorBuildMessage(prep, msg) \
|
||||
(prep, static_cast<std::ostringstream&>(V3Error::v3errorStr() << msg))
|
||||
#define v3warnCode(code, msg) v3errorEnd(v3errorBuildMessage(V3Error::v3errorPrep(code), msg))
|
||||
#define v3warnCodeFatal(code, msg) \
|
||||
v3errorEndFatal((V3ErrorLockAndCheckStopRequested, V3Error::s().v3errorPrep(code), \
|
||||
(V3Error::s().v3errorStr() << msg), V3Error::s().v3errorStr())), \
|
||||
V3Error::s().m_mutex.unlock()
|
||||
v3errorEndFatal(v3errorBuildMessage(V3Error::v3errorPrep(code), msg))
|
||||
#define v3warn(code, msg) v3warnCode(V3ErrorCode::code, msg)
|
||||
#define v3info(msg) v3warnCode(V3ErrorCode::EC_INFO, msg)
|
||||
#define v3error(msg) v3warnCode(V3ErrorCode::EC_ERROR, msg)
|
||||
@@ -576,24 +552,21 @@ inline void v3errorEndFatal(std::ostringstream& sstr)
|
||||
#define v3fatalExit(msg) v3warnCodeFatal(V3ErrorCode::EC_FATALEXIT, msg)
|
||||
// Use this instead of fatal() to mention the source code line.
|
||||
#define v3fatalSrc(msg) \
|
||||
v3warnCodeFatal(V3ErrorCode::EC_FATALSRC, \
|
||||
__FILE__ << ":" << std::dec << __LINE__ << ": " << msg)
|
||||
v3errorEndFatal(v3errorBuildMessage( \
|
||||
V3Error::v3errorPrepFileLine(V3ErrorCode::EC_FATALSRC, __FILE__, __LINE__), msg))
|
||||
// Use this when normal v3fatal is called in static method that overrides fileline.
|
||||
#define v3fatalStatic(msg) \
|
||||
(::v3errorEndFatal((V3ErrorLockAndCheckStopRequested, \
|
||||
V3Error::s().v3errorPrep(V3ErrorCode::EC_FATAL), \
|
||||
(V3Error::s().v3errorStr() << msg), V3Error::s().v3errorStr()))), \
|
||||
V3Error::s().m_mutex.unlock()
|
||||
::v3errorEndFatal(v3errorBuildMessage(V3Error::v3errorPrep(V3ErrorCode::EC_FATAL), msg))
|
||||
|
||||
#define UINFO(level, stmsg) \
|
||||
do { \
|
||||
if (VL_UNCOVERABLE(debug() >= (level))) { \
|
||||
cout << "- " << V3Error::lineStr(__FILE__, __LINE__) << stmsg; \
|
||||
std::cout << "- " << V3Error::lineStr(__FILE__, __LINE__) << stmsg; \
|
||||
} \
|
||||
} while (false)
|
||||
#define UINFONL(level, stmsg) \
|
||||
do { \
|
||||
if (VL_UNCOVERABLE(debug() >= (level))) { cout << stmsg; } \
|
||||
if (VL_UNCOVERABLE(debug() >= (level))) { std::cout << stmsg; } \
|
||||
} while (false)
|
||||
|
||||
#ifdef VL_DEBUG
|
||||
|
||||
+3
-1
@@ -202,7 +202,9 @@ private:
|
||||
new AstShiftR{fl, lhip,
|
||||
new AstConst{fl, static_cast<uint32_t>(nbitsonright)},
|
||||
VL_EDATASIZE}},
|
||||
new AstAnd{fl, new AstConst{fl, AstConst::SizedEData{}, ~VL_MASK_E(loffset)},
|
||||
new AstAnd{fl,
|
||||
new AstConst{fl, AstConst::SizedEData{},
|
||||
static_cast<uint32_t>(~VL_MASK_E(loffset))},
|
||||
new AstShiftL{fl, llowp,
|
||||
new AstConst{fl, static_cast<uint32_t>(loffset)},
|
||||
VL_EDATASIZE}}};
|
||||
|
||||
+2
-2
@@ -489,8 +489,8 @@ private:
|
||||
void startFilter(const string& command) {
|
||||
if (command == "") {} // Prevent Unused
|
||||
#ifdef INFILTER_PIPE
|
||||
int fd_stdin[2];
|
||||
int fd_stdout[2];
|
||||
int fd_stdin[2]; // Can't use std::array
|
||||
int fd_stdout[2]; // Can't use std::array
|
||||
constexpr int P_RD = 0;
|
||||
constexpr int P_WR = 1;
|
||||
|
||||
|
||||
+10
-8
@@ -20,6 +20,7 @@
|
||||
// clang-format off
|
||||
#include "V3Error.h"
|
||||
#include "V3FileLine.h"
|
||||
#include "V3Os.h"
|
||||
#include "V3String.h"
|
||||
#ifndef V3ERROR_NO_GLOBAL_
|
||||
# include "V3Global.h"
|
||||
@@ -286,12 +287,7 @@ FileLine* FileLine::copyOrSameFileLine() {
|
||||
return newp;
|
||||
}
|
||||
|
||||
string FileLine::filebasename() const VL_MT_SAFE {
|
||||
string name = filename();
|
||||
string::size_type pos;
|
||||
if ((pos = name.rfind('/')) != string::npos) name.erase(0, pos + 1);
|
||||
return name;
|
||||
}
|
||||
string FileLine::filebasename() const VL_MT_SAFE { return V3Os::filenameNonDir(filename()); }
|
||||
|
||||
string FileLine::filebasenameNoExt() const {
|
||||
string name = filebasename();
|
||||
@@ -383,7 +379,7 @@ bool FileLine::warnIsOff(V3ErrorCode code) const {
|
||||
|
||||
// cppverilator-suppress constParameter
|
||||
void FileLine::v3errorEnd(std::ostringstream& sstr, const string& extra)
|
||||
VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
VL_RELEASE(V3Error::s().m_mutex) {
|
||||
std::ostringstream nsstr;
|
||||
if (lastLineno()) nsstr << this;
|
||||
nsstr << sstr.str();
|
||||
@@ -400,7 +396,7 @@ void FileLine::v3errorEnd(std::ostringstream& sstr, const string& extra)
|
||||
nsstr << warnContextPrimary();
|
||||
}
|
||||
if (!m_waive) V3Waiver::addEntry(V3Error::s().errorCode(), filename(), sstr.str());
|
||||
V3Error::s().v3errorEnd(nsstr, lstr.str());
|
||||
V3Error::v3errorEnd(nsstr, lstr.str());
|
||||
}
|
||||
|
||||
string FileLine::warnMore() const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
@@ -433,6 +429,12 @@ string FileLine::source() const VL_MT_SAFE {
|
||||
} // LCOV_EXCL_STOP
|
||||
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 out = source();
|
||||
// Drop ignore trailing newline
|
||||
|
||||
+3
-2
@@ -241,6 +241,7 @@ public:
|
||||
// as the parser errors etc generally make more sense pointing at the last parse point
|
||||
int lineno() const VL_MT_SAFE { return m_lastLineno; }
|
||||
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
|
||||
FileLine* parent() const VL_MT_SAFE { return m_parent; }
|
||||
V3LangCode language() const { return singleton().numberToLang(filenameno()); }
|
||||
@@ -317,9 +318,9 @@ public:
|
||||
|
||||
// OPERATORS
|
||||
void v3errorEnd(std::ostringstream& str, const string& extra = "")
|
||||
VL_REQUIRES(V3Error::s().m_mutex);
|
||||
VL_RELEASE(V3Error::s().m_mutex);
|
||||
void v3errorEndFatal(std::ostringstream& str) VL_ATTR_NORETURN
|
||||
VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
VL_RELEASE(V3Error::s().m_mutex) {
|
||||
v3errorEnd(str);
|
||||
assert(0); // LCOV_EXCL_LINE
|
||||
VL_UNREACHABLE;
|
||||
|
||||
+651
@@ -0,0 +1,651 @@
|
||||
// -*- 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]
|
||||
// VARREF(var) -> MEMBERSEL(var->name, VARREF(dynscope)) (for write/RW refs)
|
||||
// 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.
|
||||
// There are two mechanisms that work together to achieve that. ForkVisitor
|
||||
// moves bodies of forked prcesses into new tasks, which results in them getting their
|
||||
// own scopes. The original statements get replaced with a call to the task which
|
||||
// passes the required variables by value.
|
||||
// The second mechanism, DynScopeVisitor, is designed to handle variables which can't be
|
||||
// captured by value and instead require a reference. Those variables get moved into an
|
||||
// "anonymous" object, ie. a class with appropriate fields gets generated and an object
|
||||
// of this class gets instantiated in place of the original variable declarations.
|
||||
// Any references to those variables are replaced with references to the object's field.
|
||||
// Since objects are reference-counted this ensures that the variables are accessible
|
||||
// as long as both the parent and the forked processes require them to be.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Fork.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3AstNodeExpr.h"
|
||||
#include "V3Error.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3MemberMap.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
class ForkDynScopeInstance final {
|
||||
public:
|
||||
AstClass* m_classp = nullptr; // Class for holding variables of dynamic scope
|
||||
AstClassRefDType* m_refDTypep = nullptr; // RefDType for the above
|
||||
AstVar* m_handlep = nullptr; // Class handle for holding variables of dynamic scope
|
||||
|
||||
// True if the instance exists
|
||||
bool initialized() const { return m_classp != nullptr; }
|
||||
};
|
||||
|
||||
class ForkDynScopeFrame final {
|
||||
private:
|
||||
// MEMBERS
|
||||
AstNodeModule* const m_modp; // Module to insert the scope into
|
||||
AstNode* const m_procp; // Procedure/block associated with that dynscope
|
||||
std::set<AstVar*> m_captures; // Variables to be moved into the dynscope
|
||||
ForkDynScopeInstance m_instance; // Nodes to be injected into the AST to create the dynscope
|
||||
|
||||
public:
|
||||
ForkDynScopeFrame(AstNodeModule* modp, AstNode* procp)
|
||||
: m_modp{modp}
|
||||
, m_procp{procp} {}
|
||||
|
||||
ForkDynScopeInstance& createInstancePrototype() {
|
||||
UASSERT_OBJ(!m_instance.initialized(), m_procp, "Dynamic scope already instantiated.");
|
||||
|
||||
m_instance.m_classp
|
||||
= new AstClass{m_procp->fileline(), generateDynScopeClassName(m_procp)};
|
||||
m_instance.m_refDTypep
|
||||
= new AstClassRefDType{m_procp->fileline(), m_instance.m_classp, nullptr};
|
||||
v3Global.rootp()->typeTablep()->addTypesp(m_instance.m_refDTypep);
|
||||
m_instance.m_handlep
|
||||
= new AstVar{m_procp->fileline(), VVarType::BLOCKTEMP,
|
||||
generateDynScopeHandleName(m_procp), m_instance.m_refDTypep};
|
||||
m_instance.m_handlep->funcLocal(true);
|
||||
m_instance.m_handlep->lifetime(VLifetime::AUTOMATIC);
|
||||
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
const ForkDynScopeInstance& instance() const { return m_instance; }
|
||||
void captureVarInsert(AstVar* varp) { m_captures.insert(varp); }
|
||||
bool captured(AstVar* varp) { return m_captures.count(varp) != 0; }
|
||||
AstNode* procp() const { return m_procp; }
|
||||
|
||||
void populateClass() {
|
||||
UASSERT_OBJ(m_instance.initialized(), m_procp, "No DynScope prototype");
|
||||
|
||||
// Move variables into the class
|
||||
for (AstVar* varp : m_captures) {
|
||||
if (varp->direction() == VDirection::INPUT) {
|
||||
varp = varp->cloneTree(false);
|
||||
varp->direction(VDirection::NONE);
|
||||
} else {
|
||||
varp->unlinkFrBack();
|
||||
}
|
||||
varp->funcLocal(false);
|
||||
varp->varType(VVarType::MEMBER);
|
||||
varp->lifetime(VLifetime::AUTOMATIC);
|
||||
varp->usedLoopIdx(false); // No longer unrollable
|
||||
m_instance.m_classp->addStmtsp(varp);
|
||||
}
|
||||
|
||||
// Create class's constructor
|
||||
AstFunc* const newp
|
||||
= new AstFunc{m_instance.m_classp->fileline(), "new", nullptr, nullptr};
|
||||
newp->isConstructor(true);
|
||||
newp->classMethod(true);
|
||||
newp->dtypep(newp->findVoidDType());
|
||||
m_instance.m_classp->addStmtsp(newp);
|
||||
}
|
||||
|
||||
void linkNodes(VMemberMap& memberMap) {
|
||||
UASSERT_OBJ(m_instance.initialized(), m_procp, "No dynamic scope prototype");
|
||||
UASSERT_OBJ(!linked(), m_instance.m_handlep, "Handle already linked");
|
||||
|
||||
if (VN_IS(m_procp, Fork)) {
|
||||
linkNodesOfFork(memberMap);
|
||||
return;
|
||||
}
|
||||
|
||||
AstNode* stmtp = getProcStmts();
|
||||
UASSERT(stmtp, "trying to instantiate dynamic scope while not under proc");
|
||||
VNRelinker stmtpHandle;
|
||||
stmtp->unlinkFrBackWithNext(&stmtpHandle);
|
||||
|
||||
// Find node after last variable declaration
|
||||
AstNode* initp = stmtp;
|
||||
while (initp && VN_IS(initp, Var)) initp = initp->nextp();
|
||||
UASSERT(stmtp, "Procedure lacks body");
|
||||
UASSERT(initp, "Procedure lacks statements besides declarations");
|
||||
|
||||
AstNew* const newp = new AstNew{m_procp->fileline(), nullptr};
|
||||
newp->taskp(VN_AS(memberMap.findMember(m_instance.m_classp, "new"), NodeFTask));
|
||||
newp->dtypep(m_instance.m_refDTypep);
|
||||
newp->classOrPackagep(m_instance.m_classp);
|
||||
|
||||
AstNode* const asgnp = new AstAssign{
|
||||
m_procp->fileline(),
|
||||
new AstVarRef{m_procp->fileline(), m_instance.m_handlep, VAccess::WRITE}, newp};
|
||||
|
||||
AstNode* initsp = nullptr; // Arguments need to be copied
|
||||
for (AstVar* varp : m_captures) {
|
||||
if (varp->direction() != VDirection::INPUT) continue;
|
||||
|
||||
AstMemberSel* const memberselp = new AstMemberSel{
|
||||
varp->fileline(),
|
||||
new AstVarRef{varp->fileline(), m_instance.m_handlep, VAccess::WRITE},
|
||||
varp->dtypep()};
|
||||
memberselp->name(varp->name());
|
||||
memberselp->varp(VN_AS(memberMap.findMember(m_instance.m_classp, varp->name()), Var));
|
||||
AstNode* initAsgnp
|
||||
= new AstAssign{varp->fileline(), memberselp,
|
||||
new AstVarRef{varp->fileline(), varp, VAccess::READ}};
|
||||
initsp = AstNode::addNext(initsp, initAsgnp);
|
||||
}
|
||||
if (initsp) AstNode::addNext(asgnp, initsp);
|
||||
|
||||
if (initp != stmtp) {
|
||||
initp->addHereThisAsNext(asgnp);
|
||||
} else {
|
||||
AstNode::addNext(asgnp, static_cast<AstNode*>(initp));
|
||||
stmtp = asgnp;
|
||||
}
|
||||
|
||||
AstNode::addNext(static_cast<AstNode*>(m_instance.m_handlep), stmtp);
|
||||
stmtpHandle.relink(m_instance.m_handlep);
|
||||
m_modp->addStmtsp(m_instance.m_classp);
|
||||
}
|
||||
|
||||
bool linked() const { return m_instance.initialized() && m_instance.m_handlep->backp(); }
|
||||
|
||||
private:
|
||||
AstAssign* instantiateDynScope(VMemberMap& memberMap) {
|
||||
AstNew* const newp = new AstNew{m_procp->fileline(), nullptr};
|
||||
newp->taskp(VN_AS(memberMap.findMember(m_instance.m_classp, "new"), NodeFTask));
|
||||
newp->dtypep(m_instance.m_refDTypep);
|
||||
newp->classOrPackagep(m_instance.m_classp);
|
||||
|
||||
return new AstAssign{
|
||||
m_procp->fileline(),
|
||||
new AstVarRef{m_procp->fileline(), m_instance.m_handlep, VAccess::WRITE}, newp};
|
||||
}
|
||||
|
||||
void linkNodesOfFork(VMemberMap& memberMap) {
|
||||
// Special case
|
||||
|
||||
AstFork* const forkp = VN_AS(m_procp, Fork);
|
||||
VNRelinker forkHandle;
|
||||
forkp->unlinkFrBack(&forkHandle);
|
||||
|
||||
AstBegin* const beginp = new AstBegin{
|
||||
forkp->fileline(),
|
||||
"_Vwrapped_" + (forkp->name().empty() ? cvtToHex(forkp) : forkp->name()),
|
||||
m_instance.m_handlep, false, true};
|
||||
forkHandle.relink(beginp);
|
||||
|
||||
AstNode* const instAsgnp = instantiateDynScope(memberMap);
|
||||
|
||||
beginp->stmtsp()->addNext(instAsgnp);
|
||||
beginp->stmtsp()->addNext(forkp);
|
||||
|
||||
if (forkp->initsp()) {
|
||||
forkp->initsp()->foreach([forkp](AstAssign* asgnp) {
|
||||
asgnp->unlinkFrBack();
|
||||
forkp->addHereThisAsNext(asgnp);
|
||||
});
|
||||
}
|
||||
UASSERT_OBJ(!forkp->initsp(), forkp, "Leftover nodes in block_item_declaration");
|
||||
|
||||
m_modp->addStmtsp(m_instance.m_classp);
|
||||
}
|
||||
|
||||
static string generateDynScopeClassName(const AstNode* fromp) {
|
||||
string n = "__VDynScope__" + (!fromp->name().empty() ? (fromp->name() + "__") : "ANON__")
|
||||
+ cvtToHex(fromp);
|
||||
return n;
|
||||
}
|
||||
|
||||
static string generateDynScopeHandleName(const AstNode* fromp) {
|
||||
return "__VDynScope_" + (fromp->name().empty() ? cvtToHex(fromp) : fromp->name());
|
||||
}
|
||||
|
||||
AstNode* getProcStmts() {
|
||||
AstNode* stmtsp = nullptr;
|
||||
if (!m_procp) return nullptr;
|
||||
if (AstBegin* beginp = VN_CAST(m_procp, Begin)) {
|
||||
stmtsp = beginp->stmtsp();
|
||||
} else if (AstNodeFTask* taskp = VN_CAST(m_procp, NodeFTask)) {
|
||||
stmtsp = taskp->stmtsp();
|
||||
} else {
|
||||
m_procp->v3fatalSrc("m_procp is not a begin block or a procedure");
|
||||
}
|
||||
return stmtsp;
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Dynamic scope visitor, creates classes and objects for dynamic scoping of variables and
|
||||
// replaces references to varibles that need a dynamic scope with references to object's
|
||||
// members
|
||||
|
||||
class DynScopeVisitor final : public VNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// AstVar::user1() -> int, timing-control fork nesting level of that variable
|
||||
// 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; // Module we are currently under
|
||||
AstNode* m_procp = nullptr; // Function/task/block we are currently under
|
||||
std::map<AstNode*, ForkDynScopeFrame*>
|
||||
m_frames; // Mapping from nodes to related DynScopeFrames
|
||||
VMemberMap m_memberMap; // Class member look-up
|
||||
int m_forkDepth = 0; // Number of asynchronous forks we are currently under
|
||||
bool m_afterTimingControl = false; // A timing control might've be executed in the current
|
||||
// process
|
||||
|
||||
// METHODS
|
||||
|
||||
ForkDynScopeFrame* frameOf(AstNode* nodep) {
|
||||
auto frameIt = m_frames.find(nodep);
|
||||
if (frameIt == m_frames.end()) return nullptr;
|
||||
return frameIt->second;
|
||||
}
|
||||
|
||||
const ForkDynScopeFrame* frameOf(AstNode* nodep) const {
|
||||
auto frameIt = m_frames.find(nodep);
|
||||
if (frameIt == m_frames.end()) return nullptr;
|
||||
return frameIt->second;
|
||||
}
|
||||
|
||||
ForkDynScopeFrame* pushDynScopeFrame(AstNode* procp) {
|
||||
ForkDynScopeFrame* const framep = new ForkDynScopeFrame{m_modp, procp};
|
||||
auto r = m_frames.emplace(std::make_pair(procp, framep));
|
||||
UASSERT_OBJ(r.second, m_modp, "Procedure already contains a frame");
|
||||
return framep;
|
||||
}
|
||||
|
||||
void replaceWithMemberSel(AstVarRef* refp, const ForkDynScopeInstance& dynScope) {
|
||||
VNRelinker handle;
|
||||
refp->unlinkFrBack(&handle);
|
||||
AstMemberSel* const membersel = new AstMemberSel{
|
||||
refp->fileline(), new AstVarRef{refp->fileline(), dynScope.m_handlep, refp->access()},
|
||||
refp->dtypep()};
|
||||
membersel->name(refp->varp()->name());
|
||||
if (refp->varp()->direction() == VDirection::INPUT) {
|
||||
membersel->varp(
|
||||
VN_AS(m_memberMap.findMember(dynScope.m_classp, refp->varp()->name()), Var));
|
||||
} else {
|
||||
membersel->varp(refp->varp());
|
||||
}
|
||||
handle.relink(membersel);
|
||||
VL_DO_DANGLING(refp->deleteTree(), refp);
|
||||
}
|
||||
|
||||
static bool hasAsyncFork(AstNode* nodep) {
|
||||
bool afork = false;
|
||||
nodep->foreach([&](AstFork* forkp) {
|
||||
if (!forkp->joinType().join()) afork = true;
|
||||
});
|
||||
return afork;
|
||||
}
|
||||
|
||||
void bindNodeToDynScope(AstNode* nodep, ForkDynScopeFrame* frame) {
|
||||
m_frames.emplace(std::make_pair(nodep, frame));
|
||||
}
|
||||
|
||||
bool needsDynScope(const AstVarRef* refp) const {
|
||||
return
|
||||
// Can this variable escape the scope
|
||||
((m_forkDepth > refp->varp()->user1()) && refp->varp()->isFuncLocal())
|
||||
&& (
|
||||
// Is it mutated
|
||||
(refp->varp()->isClassHandleValue() ? refp->user2() : refp->access().isWriteOrRW())
|
||||
// Or is it after a timing-control event
|
||||
|| m_afterTimingControl);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
if (!VN_IS(nodep, Class)) m_modp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNodeFTask* nodep) override {
|
||||
VL_RESTORER(m_procp);
|
||||
m_procp = nodep;
|
||||
if (hasAsyncFork(nodep)) pushDynScopeFrame(m_procp);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstBegin* nodep) override {
|
||||
VL_RESTORER(m_procp);
|
||||
m_procp = nodep;
|
||||
if (hasAsyncFork(nodep)) pushDynScopeFrame(m_procp);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstFork* nodep) override {
|
||||
VL_RESTORER(m_forkDepth);
|
||||
if (!nodep->joinType().join()) ++m_forkDepth;
|
||||
|
||||
const bool oldAfterTimingControl = m_afterTimingControl;
|
||||
|
||||
ForkDynScopeFrame* framep = nullptr;
|
||||
if (nodep->initsp()) framep = pushDynScopeFrame(nodep);
|
||||
|
||||
for (AstNode* stmtp = nodep->initsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
if (AstVar* varp = VN_CAST(stmtp, Var)) {
|
||||
// This can be probably optimized to detect cases in which dynscopes
|
||||
// could be avoided
|
||||
if (!framep->instance().initialized()) framep->createInstancePrototype();
|
||||
framep->captureVarInsert(varp);
|
||||
bindNodeToDynScope(varp, framep);
|
||||
} else {
|
||||
AstAssign* const asgnp = VN_CAST(stmtp, Assign);
|
||||
UASSERT_OBJ(asgnp, stmtp,
|
||||
"Invalid node under block item initialization part of fork");
|
||||
bindNodeToDynScope(asgnp->lhsp(), framep);
|
||||
iterate(asgnp->rhsp());
|
||||
}
|
||||
}
|
||||
|
||||
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
m_afterTimingControl = false;
|
||||
iterate(stmtp);
|
||||
}
|
||||
m_afterTimingControl = oldAfterTimingControl;
|
||||
if (nodep->isTimingControl()) m_afterTimingControl = true;
|
||||
}
|
||||
void visit(AstNodeFTaskRef* nodep) override {
|
||||
visit(static_cast<AstNodeExpr*>(nodep));
|
||||
// We are before V3Timing, so unfortnately we need to treat any calls as suspending,
|
||||
// just to be safe. This might be improved if we could propagate suspendability
|
||||
// before doing all the other timing-related stuff.
|
||||
m_afterTimingControl = true;
|
||||
}
|
||||
void visit(AstVar* nodep) override {
|
||||
nodep->user1(m_forkDepth);
|
||||
ForkDynScopeFrame* const framep = frameOf(m_procp);
|
||||
if (!framep) return; // Cannot be legally referenced from a fork
|
||||
bindNodeToDynScope(nodep, framep);
|
||||
}
|
||||
void visit(AstVarRef* nodep) override {
|
||||
ForkDynScopeFrame* const framep = frameOf(nodep->varp());
|
||||
if (!framep) return;
|
||||
|
||||
if (needsDynScope(nodep)) {
|
||||
if (!framep->instance().initialized()) framep->createInstancePrototype();
|
||||
framep->captureVarInsert(nodep->varp());
|
||||
}
|
||||
bindNodeToDynScope(nodep, framep);
|
||||
}
|
||||
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(AstNode* nodep) override {
|
||||
if (nodep->isTimingControl()) m_afterTimingControl = true;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit DynScopeVisitor(AstNetlist* nodep) {
|
||||
// Create Dynamic scope class prototypes and objects
|
||||
visit(nodep);
|
||||
|
||||
// Commit changes to AST
|
||||
bool typesAdded = false;
|
||||
for (auto frameIt : m_frames) {
|
||||
ForkDynScopeFrame* frame = frameIt.second;
|
||||
if (!frame->instance().initialized()) continue;
|
||||
|
||||
if (!frame->linked()) {
|
||||
frame->populateClass();
|
||||
frame->linkNodes(m_memberMap);
|
||||
typesAdded = true;
|
||||
}
|
||||
|
||||
if (AstVarRef* refp = VN_CAST(frameIt.first, VarRef)) {
|
||||
if (frame->captured(refp->varp())) replaceWithMemberSel(refp, frame->instance());
|
||||
}
|
||||
}
|
||||
|
||||
if (typesAdded) v3Global.rootp()->typeTablep()->repairCache();
|
||||
}
|
||||
~DynScopeVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// 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)
|
||||
|
||||
// METHODS
|
||||
|
||||
AstVar* captureRef(AstVarRef* 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
|
||||
m_capturedVarRefsp
|
||||
= AstNode::addNext(m_capturedVarRefsp, new AstArg{refp->fileline(), refp->name(),
|
||||
refp->cloneTree(false)});
|
||||
}
|
||||
return varp;
|
||||
}
|
||||
|
||||
AstTask* makeTask(FileLine* fl, AstNode* stmtsp, string name) {
|
||||
stmtsp = AstNode::addNext(static_cast<AstNode*>(m_capturedVarsp), stmtsp);
|
||||
AstTask* const taskp = new AstTask{fl, name, stmtsp};
|
||||
return taskp;
|
||||
}
|
||||
|
||||
string generateTaskName(AstNode* fromp, const string& kind) {
|
||||
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 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 string taskName = generateTaskName(stmtp, "__FORK_STMT_");
|
||||
taskp = makeTask(stmtp->fileline(), stmtp->unlinkFrBack(&handle), taskName);
|
||||
} else if (AstFork* forkp = VN_CAST(nodep, Fork)) {
|
||||
const 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};
|
||||
taskrefp->taskp(taskp);
|
||||
AstStmtExpr* const taskcallp = taskrefp->makeStmt();
|
||||
// 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
|
||||
explicit ForkVisitor(AstNetlist* nodep) { visit(nodep); }
|
||||
~ForkVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Fork class functions
|
||||
|
||||
void V3Fork::makeDynamicScopes(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DynScopeVisitor{nodep}; }
|
||||
V3Global::dumpCheckGlobalTree("fork_dynscope", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
void V3Fork::makeTasks(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ForkVisitor{nodep}; }
|
||||
V3Global::dumpCheckGlobalTree("fork", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// -*- 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:
|
||||
// Move/copy variables to "anonymous" objects if their lifetime might exceed the scope of a
|
||||
// procedure that declared them. Update the references apropriately.
|
||||
static void makeDynamicScopes(AstNetlist* nodep);
|
||||
// Create tasks out of blocks/statments that can outlive processes in which they were forked.
|
||||
// Return value: number of tasks created
|
||||
static void makeTasks(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
+15
-22
@@ -65,6 +65,7 @@ public:
|
||||
// Support classes
|
||||
|
||||
class GateEitherVertex VL_NOT_FINAL : public V3GraphVertex {
|
||||
VL_RTTI_IMPL(GateEitherVertex, V3GraphVertex)
|
||||
AstScope* const m_scopep; // Scope vertex refers to
|
||||
bool m_reducible = true; // True if this node should be able to be eliminated
|
||||
bool m_dedupable = true; // True if this node should be able to be deduped
|
||||
@@ -122,6 +123,7 @@ public:
|
||||
};
|
||||
|
||||
class GateVarVertex final : public GateEitherVertex {
|
||||
VL_RTTI_IMPL(GateVarVertex, GateEitherVertex)
|
||||
AstVarScope* const m_varScp;
|
||||
bool m_isTop = false;
|
||||
bool m_isClock = false;
|
||||
@@ -164,6 +166,7 @@ public:
|
||||
};
|
||||
|
||||
class GateLogicVertex final : public GateEitherVertex {
|
||||
VL_RTTI_IMPL(GateLogicVertex, GateEitherVertex)
|
||||
AstNode* const m_nodep;
|
||||
AstActive* const m_activep; // Under what active; nullptr is ok (under cfunc or such)
|
||||
const bool m_slow; // In slow block
|
||||
@@ -176,9 +179,7 @@ public:
|
||||
, m_slow{slow} {}
|
||||
~GateLogicVertex() override = default;
|
||||
// ACCESSORS
|
||||
string name() const override VL_MT_STABLE {
|
||||
return (cvtToHex(m_nodep) + "@" + scopep()->prettyName());
|
||||
}
|
||||
string name() const override { return (cvtToHex(m_nodep) + "@" + scopep()->prettyName()); }
|
||||
string dotColor() const override { return "purple"; }
|
||||
FileLine* fileline() const override { return nodep()->fileline(); }
|
||||
AstNode* nodep() const { return m_nodep; }
|
||||
@@ -333,7 +334,6 @@ private:
|
||||
AstActive* m_activep = nullptr; // Current active
|
||||
bool m_activeReducible = true; // Is activation block reducible?
|
||||
bool m_inSenItem = false; // Underneath AstSenItem; any varrefs are clocks
|
||||
bool m_inExprStmt = false; // Underneath ExprStmt; don't optimize LHS vars
|
||||
bool m_inSlow = false; // Inside a slow structure
|
||||
std::vector<AstNode*> m_optimized; // Logic blocks optimized
|
||||
|
||||
@@ -499,10 +499,6 @@ private:
|
||||
// the weight will increase
|
||||
if (nodep->access().isWriteOrRW()) {
|
||||
new V3GraphEdge{&m_graph, m_logicVertexp, vvertexp, 1};
|
||||
if (m_inExprStmt) {
|
||||
m_logicVertexp->clearReducibleAndDedupable("LHS var in ExprStmt");
|
||||
m_logicVertexp->setConsumed("LHS var in ExprStmt");
|
||||
}
|
||||
}
|
||||
if (nodep->access().isReadOrRW()) {
|
||||
new V3GraphEdge{&m_graph, vvertexp, m_logicVertexp, 1};
|
||||
@@ -518,11 +514,6 @@ private:
|
||||
iterateNewStmt(nodep, "User C Function", "User C Function");
|
||||
}
|
||||
void visit(AstClocking* nodep) override { iterateNewStmt(nodep, nullptr, nullptr); }
|
||||
void visit(AstExprStmt* nodep) override {
|
||||
VL_RESTORER(m_inExprStmt);
|
||||
m_inExprStmt = true;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstSenItem* nodep) override {
|
||||
VL_RESTORER(m_inSenItem);
|
||||
m_inSenItem = true;
|
||||
@@ -580,7 +571,7 @@ public:
|
||||
|
||||
void GateVisitor::optimizeSignals(bool allowMultiIn) {
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(itp);
|
||||
GateVarVertex* const vvertexp = itp->cast<GateVarVertex>();
|
||||
|
||||
// Consider "inlining" variables
|
||||
if (!vvertexp) continue;
|
||||
@@ -722,7 +713,7 @@ void GateVisitor::consumedMove() {
|
||||
// We need the "usually" block logic to do a better job at this
|
||||
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp;
|
||||
vertexp = vertexp->verticesNextp()) {
|
||||
if (const GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(vertexp)) {
|
||||
if (const GateVarVertex* const vvertexp = vertexp->cast<GateVarVertex>()) {
|
||||
if (!vvertexp->consumed() && !vvertexp->user()) {
|
||||
UINFO(8, "Unconsumed " << vvertexp->varScp() << endl);
|
||||
}
|
||||
@@ -746,7 +737,7 @@ void GateVisitor::consumedMove() {
|
||||
void GateVisitor::warnSignals() {
|
||||
AstNode::user2ClearTree();
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (const GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
||||
if (const GateVarVertex* const vvertexp = itp->cast<GateVarVertex>()) {
|
||||
const AstVarScope* const vscp = vvertexp->varScp();
|
||||
const AstNode* const sp = vvertexp->rstSyncNodep();
|
||||
const AstNode* const ap = vvertexp->rstAsyncNodep();
|
||||
@@ -907,6 +898,7 @@ class GateDedupeVarVisitor final : public VNVisitor {
|
||||
// (Note, the IF must be the only node under the always,
|
||||
// and the assign must be the only node under the if, other than the ifcond)
|
||||
// Any other ordering or node type, except for an AstComment, makes it not dedupable
|
||||
// AstExprStmt in the subtree of a node also makes the node not dedupable.
|
||||
private:
|
||||
// STATE
|
||||
GateDedupeHash m_ghash; // Hash used to find dupes of rhs of assign
|
||||
@@ -922,6 +914,7 @@ private:
|
||||
// non-blocking statements, but erring on side of caution here
|
||||
if (!m_assignp) {
|
||||
m_assignp = assignp;
|
||||
m_dedupable = !assignp->exists([&](AstExprStmt*) { return true; });
|
||||
} else {
|
||||
m_dedupable = false;
|
||||
}
|
||||
@@ -946,6 +939,7 @@ private:
|
||||
if (m_always && !m_ifCondp && !ifp->elsesp()) {
|
||||
// we're under an always, this is the first IF, and there's no else
|
||||
m_ifCondp = ifp->condp();
|
||||
m_dedupable = !m_ifCondp->exists([&](AstExprStmt*) { return true; });
|
||||
iterateAndNextNull(ifp->thensp());
|
||||
} else {
|
||||
m_dedupable = false;
|
||||
@@ -1145,14 +1139,14 @@ void GateVisitor::dedupe() {
|
||||
// Traverse starting from each of the clocks
|
||||
UINFO(9, "Gate dedupe() clocks:\n");
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
||||
if (GateVarVertex* const vvertexp = itp->cast<GateVarVertex>()) {
|
||||
if (vvertexp->isClock()) deduper.dedupeTree(vvertexp);
|
||||
}
|
||||
}
|
||||
// Traverse starting from each of the outputs
|
||||
UINFO(9, "Gate dedupe() outputs:\n");
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
||||
if (GateVarVertex* const vvertexp = itp->cast<GateVarVertex>()) {
|
||||
if (vvertexp->isTop() && vvertexp->varScp()->varp()->isWritable()) {
|
||||
deduper.dedupeTree(vvertexp);
|
||||
}
|
||||
@@ -1196,8 +1190,7 @@ private:
|
||||
for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep;) {
|
||||
V3GraphEdge* oldedgep = edgep;
|
||||
edgep = edgep->inNextp(); // for recursive since the edge could be deleted
|
||||
if (GateLogicVertex* const lvertexp
|
||||
= dynamic_cast<GateLogicVertex*>(oldedgep->fromp())) {
|
||||
if (GateLogicVertex* const lvertexp = oldedgep->fromp()->cast<GateLogicVertex>()) {
|
||||
if (AstNodeAssign* const assignp = VN_CAST(lvertexp->nodep(), NodeAssign)) {
|
||||
// if (lvertexp->outSize1() && VN_IS(assignp->lhsp(), Sel)) {
|
||||
if (VN_IS(assignp->lhsp(), Sel) && lvertexp->outSize1()) {
|
||||
@@ -1283,7 +1276,7 @@ void GateVisitor::mergeAssigns() {
|
||||
UINFO(6, "mergeAssigns\n");
|
||||
GateMergeAssignsGraphVisitor merger{&m_graph};
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
||||
if (GateVarVertex* const vvertexp = itp->cast<GateVarVertex>()) {
|
||||
merger.mergeAssignsTree(vvertexp);
|
||||
}
|
||||
}
|
||||
@@ -1469,7 +1462,7 @@ void GateVisitor::decomposeClkVectors() {
|
||||
AstNode::user2ClearTree();
|
||||
GateClkDecompGraphVisitor decomposer{&m_graph};
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (GateVarVertex* const vertp = dynamic_cast<GateVarVertex*>(itp)) {
|
||||
if (GateVarVertex* const vertp = itp->cast<GateVarVertex>()) {
|
||||
const AstVarScope* const vsp = vertp->varScp();
|
||||
if (vsp->varp()->attrClocker() == VVarAttrClocker::CLOCKER_YES) {
|
||||
if (vsp->varp()->width() > 1) {
|
||||
|
||||
+3
-3
@@ -19,8 +19,8 @@
|
||||
|
||||
// clang-format off
|
||||
#include "config_build.h"
|
||||
#ifndef HAVE_CONFIG_BUILD
|
||||
# error "Something failed during ./configure as config_build.h is incomplete. Perhaps you used autoreconf, don't."
|
||||
#ifndef HAVE_CONFIG_PACKAGE
|
||||
# error "Something failed during ./configure as config_package.h is incomplete. Perhaps you used autoreconf, don't."
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
@@ -102,7 +102,7 @@ class V3Global final {
|
||||
bool m_assertDTypesResolved = false; // Tree should have dtypep()'s
|
||||
bool m_assertScoped = false; // Tree is scoped
|
||||
bool m_constRemoveXs = false; // Const needs to strip any Xs
|
||||
// Experimenting with always requiring heavy, see (#2701)
|
||||
// Experimenting with always requiring heavy, see issue #2701
|
||||
bool m_needTraceDumper = false; // Need __Vm_dumperp in symbols
|
||||
bool m_dpi = false; // Need __Dpi include files
|
||||
bool m_hasEvents = false; // Design uses SystemVerilog named events
|
||||
|
||||
+3
-23
@@ -92,26 +92,6 @@ void V3GraphVertex::rerouteEdges(V3Graph* graphp) {
|
||||
bool V3GraphVertex::inSize1() const { return !inEmpty() && !inBeginp()->inNextp(); }
|
||||
bool V3GraphVertex::outSize1() const { return !outEmpty() && !outBeginp()->outNextp(); }
|
||||
|
||||
uint32_t V3GraphVertex::inHash() const {
|
||||
// We want the same hash ignoring the order of edges.
|
||||
// So we need an associative operator, like XOR.
|
||||
// However with XOR multiple edges to the same source will cancel out,
|
||||
// so we use ADD. (Generally call this only after removing duplicates though)
|
||||
uint32_t hash = 0;
|
||||
for (V3GraphEdge* edgep = this->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
hash += cvtToHash(edgep->fromp());
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
uint32_t V3GraphVertex::outHash() const {
|
||||
uint32_t hash = 0;
|
||||
for (V3GraphEdge* edgep = this->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
hash += cvtToHash(edgep->top());
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
V3GraphEdge* V3GraphVertex::findConnectingEdgep(GraphWay way, const V3GraphVertex* waywardp) {
|
||||
// O(edges) linear search. Searches search both nodes' edge lists in
|
||||
// parallel. The lists probably aren't _both_ huge, so this is
|
||||
@@ -129,7 +109,7 @@ V3GraphEdge* V3GraphVertex::findConnectingEdgep(GraphWay way, const V3GraphVerte
|
||||
}
|
||||
|
||||
// cppcheck-has-bug-suppress constParameter
|
||||
void V3GraphVertex::v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
void V3GraphVertex::v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
|
||||
std::ostringstream nsstr;
|
||||
nsstr << str.str();
|
||||
if (debug()) {
|
||||
@@ -139,11 +119,11 @@ void V3GraphVertex::v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Erro
|
||||
if (FileLine* const flp = fileline()) {
|
||||
flp->v3errorEnd(nsstr);
|
||||
} else {
|
||||
V3Error::s().v3errorEnd(nsstr);
|
||||
V3Error::v3errorEnd(nsstr);
|
||||
}
|
||||
}
|
||||
void V3GraphVertex::v3errorEndFatal(std::ostringstream& str) const
|
||||
VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
VL_RELEASE(V3Error::s().m_mutex) {
|
||||
v3errorEnd(str);
|
||||
assert(0); // LCOV_EXCL_LINE
|
||||
VL_UNREACHABLE;
|
||||
|
||||
+72
-4
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3List.h"
|
||||
#include "V3Rtti.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -173,6 +174,7 @@ public:
|
||||
//============================================================================
|
||||
|
||||
class V3GraphVertex VL_NOT_FINAL {
|
||||
VL_RTTI_IMPL_BASE(V3GraphVertex)
|
||||
// Vertices may be a 'gate'/wire statement OR a variable
|
||||
protected:
|
||||
friend class V3Graph;
|
||||
@@ -209,6 +211,40 @@ public:
|
||||
void unlinkEdges(V3Graph* graphp);
|
||||
void unlinkDelete(V3Graph* graphp);
|
||||
|
||||
// METHODS
|
||||
// Return true iff of type T
|
||||
template <typename T>
|
||||
bool is() const {
|
||||
static_assert(std::is_base_of<V3GraphVertex, T>::value,
|
||||
"'T' must be a subtype of V3GraphVertex");
|
||||
static_assert(std::is_same<typename std::remove_cv<T>::type,
|
||||
VTypeListFront<typename T::RttiThisAndBaseClassesList>>::value,
|
||||
"Missing VL_RTTI_IMPL(...) call in 'T'");
|
||||
return this->isInstanceOfClassWithId(T::rttiClassId());
|
||||
}
|
||||
|
||||
// Return cast to subtype T and assert of that type
|
||||
template <typename T>
|
||||
T* as() {
|
||||
UASSERT_OBJ(is<T>(), this, "V3GraphVertex is not of expected type");
|
||||
return static_cast<T*>(this);
|
||||
}
|
||||
template <typename T>
|
||||
const T* as() const {
|
||||
UASSERT_OBJ(is<T>(), this, "V3GraphVertex is not of expected type");
|
||||
return static_cast<const T*>(this);
|
||||
}
|
||||
|
||||
// Return cast to subtype T, else nullptr if different type
|
||||
template <typename T>
|
||||
T* cast() {
|
||||
return is<T>() ? static_cast<T*>(this) : nullptr;
|
||||
}
|
||||
template <typename T>
|
||||
const T* cast() const {
|
||||
return is<T>() ? static_cast<const T*>(this) : nullptr;
|
||||
}
|
||||
|
||||
// ACCESSORS
|
||||
virtual string name() const { return ""; }
|
||||
virtual string dotColor() const { return "black"; }
|
||||
@@ -240,16 +276,14 @@ public:
|
||||
V3GraphEdge* inBeginp() const { return m_ins.begin(); }
|
||||
bool inEmpty() const { return inBeginp() == nullptr; }
|
||||
bool inSize1() const;
|
||||
uint32_t inHash() const;
|
||||
V3GraphEdge* outBeginp() const { return m_outs.begin(); }
|
||||
bool outEmpty() const { return outBeginp() == nullptr; }
|
||||
bool outSize1() const;
|
||||
uint32_t outHash() const;
|
||||
V3GraphEdge* beginp(GraphWay way) const { return way.forward() ? outBeginp() : inBeginp(); }
|
||||
// METHODS
|
||||
/// Error reporting
|
||||
void v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex);
|
||||
void v3errorEndFatal(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex);
|
||||
void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex);
|
||||
void v3errorEndFatal(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex);
|
||||
/// Edges are routed around this vertex to point from "from" directly to "to"
|
||||
void rerouteEdges(V3Graph* graphp);
|
||||
/// Find the edge connecting ap and bp, where bp is wayward from ap.
|
||||
@@ -262,6 +296,7 @@ std::ostream& operator<<(std::ostream& os, V3GraphVertex* vertexp);
|
||||
//============================================================================
|
||||
|
||||
class V3GraphEdge VL_NOT_FINAL {
|
||||
VL_RTTI_IMPL_BASE(V3GraphEdge)
|
||||
// Wires/variables aren't edges. Edges have only a single to/from vertex
|
||||
public:
|
||||
// ENUMS
|
||||
@@ -308,6 +343,39 @@ public:
|
||||
}
|
||||
virtual ~V3GraphEdge() = default;
|
||||
// METHODS
|
||||
// Return true iff of type T
|
||||
template <typename T>
|
||||
bool is() const {
|
||||
static_assert(std::is_base_of<V3GraphEdge, T>::value,
|
||||
"'T' must be a subtype of V3GraphEdge");
|
||||
static_assert(std::is_same<typename std::remove_cv<T>::type,
|
||||
VTypeListFront<typename T::RttiThisAndBaseClassesList>>::value,
|
||||
"Missing VL_RTTI_IMPL(...) call in 'T'");
|
||||
return this->isInstanceOfClassWithId(T::rttiClassId());
|
||||
}
|
||||
|
||||
// Return cast to subtype T and assert of that type
|
||||
template <typename T>
|
||||
T* as() {
|
||||
UASSERT(is<T>(), "V3GraphEdge is not of expected type");
|
||||
return static_cast<T*>(this);
|
||||
}
|
||||
template <typename T>
|
||||
const T* as() const {
|
||||
UASSERT(is<T>(), "V3GraphEdge is not of expected type");
|
||||
return static_cast<const T*>(this);
|
||||
}
|
||||
|
||||
// Return cast to subtype T, else nullptr if different type
|
||||
template <typename T>
|
||||
T* cast() {
|
||||
return is<T>() ? static_cast<T*>(this) : nullptr;
|
||||
}
|
||||
template <typename T>
|
||||
const T* cast() const {
|
||||
return is<T>() ? static_cast<const T*>(this) : nullptr;
|
||||
}
|
||||
|
||||
virtual string name() const { return m_fromp->name() + "->" + m_top->name(); }
|
||||
virtual string dotLabel() const { return ""; }
|
||||
virtual string dotColor() const { return cutable() ? "yellowGreen" : "red"; }
|
||||
|
||||
@@ -32,6 +32,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
// Break the minimal number of backward edges to make the graph acyclic
|
||||
|
||||
class GraphAcycVertex final : public V3GraphVertex {
|
||||
VL_RTTI_IMPL(GraphAcycVertex, V3GraphVertex)
|
||||
// user() is used for various sub-algorithm pieces
|
||||
V3GraphVertex* const m_origVertexp; // Pointer to first vertex this represents
|
||||
protected:
|
||||
@@ -56,6 +57,7 @@ public:
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
class GraphAcycEdge final : public V3GraphEdge {
|
||||
VL_RTTI_IMPL(GraphAcycEdge, V3GraphEdge)
|
||||
// userp() is always used to point to the head original graph edge
|
||||
private:
|
||||
using OrigEdgeList = std::list<V3GraphEdge*>; // List of orig edges, see also GraphAcyc's decl
|
||||
|
||||
+1
-2
@@ -52,6 +52,7 @@ public:
|
||||
// Vertices and nodes
|
||||
|
||||
class V3GraphTestVertex VL_NOT_FINAL : public V3GraphVertex {
|
||||
VL_RTTI_IMPL(V3GraphTestVertex, V3GraphVertex)
|
||||
const string m_name;
|
||||
|
||||
public:
|
||||
@@ -293,10 +294,8 @@ public:
|
||||
void V3Graph::selfTest() {
|
||||
// Execute all of the tests
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
// clang-format off
|
||||
{ V3GraphTestStrong{}.run(); }
|
||||
{ V3GraphTestAcyc{}.run(); }
|
||||
{ V3GraphTestVars{}.run(); }
|
||||
{ V3GraphTestImport{}.run(); }
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
+7
-1
@@ -174,6 +174,9 @@ private:
|
||||
iterateConstNull(nodep->refDTypep());
|
||||
});
|
||||
}
|
||||
void visit(AstStreamDType* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {});
|
||||
}
|
||||
void visit(AstVoidDType* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {});
|
||||
}
|
||||
@@ -207,7 +210,7 @@ private:
|
||||
iterateConstNull(nodep->varScopep());
|
||||
} else {
|
||||
iterateConstNull(nodep->varp());
|
||||
m_hash += nodep->selfPointer();
|
||||
m_hash += nodep->selfPointer().asString();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -269,6 +272,9 @@ private:
|
||||
iterateConstNull(nodep->sensesp());
|
||||
});
|
||||
}
|
||||
void visit(AstCLocalScope* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
||||
}
|
||||
void visit(AstCoverInc* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
||||
iterateConstNull(nodep->declp());
|
||||
|
||||
@@ -384,7 +384,6 @@ private:
|
||||
nodep->v3fatalSrc("Null connection?");
|
||||
}
|
||||
}
|
||||
nodep->name(nodep->varp()->name());
|
||||
}
|
||||
void visit(AstVarXRef* nodep) override {
|
||||
// Track what scope it was originally under so V3LinkDot can resolve it
|
||||
|
||||
@@ -147,12 +147,6 @@ private:
|
||||
iterateAndNextConstNull(nodep->lsbp());
|
||||
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 {
|
||||
if (m_ignoreRemaining) return;
|
||||
// Nop.
|
||||
|
||||
+1
-1
@@ -432,7 +432,7 @@ private:
|
||||
// UINFO(4, " CFUNC " << nodep << endl);
|
||||
if (!m_tracingCall && !nodep->entryPoint()) return;
|
||||
m_tracingCall = false;
|
||||
if (nodep->dpiImportPrototype() && !nodep->pure()) {
|
||||
if (nodep->dpiImportPrototype() && !nodep->dpiPure()) {
|
||||
m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
|
||||
+1
-1
@@ -333,7 +333,7 @@ private:
|
||||
}
|
||||
for (V3GraphVertex* mtaskVxp = nodep->depGraphp()->verticesBeginp(); mtaskVxp;
|
||||
mtaskVxp = mtaskVxp->verticesNextp()) {
|
||||
const ExecMTask* const mtaskp = dynamic_cast<ExecMTask*>(mtaskVxp);
|
||||
const ExecMTask* const mtaskp = mtaskVxp->as<ExecMTask>();
|
||||
m_execMTaskp = mtaskp;
|
||||
m_sequence = 0;
|
||||
iterate(mtaskp->bodyp());
|
||||
|
||||
+4
-2
@@ -52,6 +52,7 @@ public:
|
||||
};
|
||||
|
||||
class LinkCellsVertex final : public V3GraphVertex {
|
||||
VL_RTTI_IMPL(LinkCellsVertex, V3GraphVertex)
|
||||
AstNodeModule* const m_modp;
|
||||
|
||||
public:
|
||||
@@ -69,6 +70,7 @@ public:
|
||||
};
|
||||
|
||||
class LibraryVertex final : public V3GraphVertex {
|
||||
VL_RTTI_IMPL(LibraryVertex, V3GraphVertex)
|
||||
public:
|
||||
explicit LibraryVertex(V3Graph* graphp)
|
||||
: V3GraphVertex{graphp} {}
|
||||
@@ -77,7 +79,7 @@ public:
|
||||
};
|
||||
|
||||
void LinkCellsGraph::loopsMessageCb(V3GraphVertex* vertexp) {
|
||||
if (const LinkCellsVertex* const vvertexp = dynamic_cast<LinkCellsVertex*>(vertexp)) {
|
||||
if (const LinkCellsVertex* const vvertexp = vertexp->cast<LinkCellsVertex>()) {
|
||||
vvertexp->modp()->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Recursive multiple modules (module instantiates "
|
||||
"something leading back to itself): "
|
||||
@@ -171,7 +173,7 @@ private:
|
||||
if (dumpGraphLevel()) m_graph.dumpDotFilePrefixed("linkcells");
|
||||
m_graph.rank();
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (const LinkCellsVertex* const vvertexp = dynamic_cast<LinkCellsVertex*>(itp)) {
|
||||
if (const LinkCellsVertex* const vvertexp = itp->cast<LinkCellsVertex>()) {
|
||||
// +1 so we leave level 1 for the new wrapper we'll make in a moment
|
||||
AstNodeModule* const modp = vvertexp->modp();
|
||||
modp->level(vvertexp->rank() + 1);
|
||||
|
||||
+191
-81
@@ -69,6 +69,7 @@
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Graph.h"
|
||||
#include "V3MemberMap.h"
|
||||
#include "V3String.h"
|
||||
#include "V3SymTable.h"
|
||||
|
||||
@@ -215,6 +216,7 @@ public:
|
||||
VSymGraph* symsp() { return &m_syms; }
|
||||
int stepNumber() const { return int(m_step); }
|
||||
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 forScopeCreation() const { return m_step == LDS_SCOPED; }
|
||||
|
||||
@@ -751,6 +753,8 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||
// METHODS
|
||||
void makeImplicitNew(AstClass* nodep) {
|
||||
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);
|
||||
nodep->addMembersp(newp);
|
||||
UINFO(8, "Made implicit new for " << nodep->name() << ": " << nodep << endl);
|
||||
@@ -1117,9 +1121,9 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||
m_statep->insertSym(m_curSymp, newvarp->name(), newvarp,
|
||||
nullptr /*classOrPackagep*/);
|
||||
}
|
||||
VL_RESTORER(m_ftaskp);
|
||||
m_ftaskp = nodep;
|
||||
iterateChildren(nodep);
|
||||
m_ftaskp = nullptr;
|
||||
}
|
||||
}
|
||||
void visit(AstClocking* nodep) override {
|
||||
@@ -1347,7 +1351,7 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||
if (!foundp && m_modSymp && nodep->name() == m_modSymp->nodep()->name()) {
|
||||
foundp = m_modSymp; // Conflicts with modname?
|
||||
}
|
||||
AstEnumItem* const findvarp = foundp ? VN_AS(foundp->nodep(), EnumItem) : nullptr;
|
||||
AstEnumItem* const findvarp = foundp ? VN_CAST(foundp->nodep(), EnumItem) : nullptr;
|
||||
bool ins = false;
|
||||
if (!foundp) {
|
||||
ins = true;
|
||||
@@ -1371,7 +1375,7 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||
<< nodep->warnContextPrimary() << '\n'
|
||||
<< foundp->nodep()->warnOther()
|
||||
<< "... Location of original declaration\n"
|
||||
<< nodep->warnContextSecondary());
|
||||
<< foundp->nodep()->warnContextSecondary());
|
||||
}
|
||||
ins = true;
|
||||
}
|
||||
@@ -1612,6 +1616,7 @@ private:
|
||||
AstPin* const pinp = new AstPin{nodep->fileline(),
|
||||
-1, // Pin# not relevant
|
||||
nodep->name(), exprp};
|
||||
pinp->param(true);
|
||||
cellp->addParamsp(pinp);
|
||||
}
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
@@ -2025,6 +2030,7 @@ private:
|
||||
// (except the default instances)
|
||||
// They are added to the set only in linkDotPrimary.
|
||||
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 {
|
||||
DotPosition m_dotPos; // Scope part of dotted resolution
|
||||
@@ -2060,40 +2066,38 @@ private:
|
||||
} m_ds; // State to preserve across recursions
|
||||
|
||||
// METHODS - Variables
|
||||
void createImplicitVar(VSymEnt* /*lookupSymp*/, AstVarRef* nodep, AstNodeModule* modp,
|
||||
VSymEnt* moduleSymp, bool noWarn) {
|
||||
AstVar* createImplicitVar(VSymEnt* /*lookupSymp*/, AstParseRef* nodep, AstNodeModule* modp,
|
||||
VSymEnt* moduleSymp, bool noWarn) {
|
||||
// Create implicit after warning
|
||||
if (!nodep->varp()) {
|
||||
if (!noWarn) {
|
||||
if (nodep->fileline()->warnIsOff(V3ErrorCode::I_DEF_NETTYPE_WIRE)) {
|
||||
const string suggest = m_statep->suggestSymFallback(moduleSymp, nodep->name(),
|
||||
LinkNodeMatcherVar{});
|
||||
nodep->v3error("Signal definition not found, and implicit disabled with "
|
||||
"`default_nettype: "
|
||||
<< nodep->prettyNameQ() << '\n'
|
||||
<< (suggest.empty() ? "" : nodep->warnMore() + suggest));
|
||||
if (!noWarn) {
|
||||
if (nodep->fileline()->warnIsOff(V3ErrorCode::I_DEF_NETTYPE_WIRE)) {
|
||||
const string suggest = m_statep->suggestSymFallback(moduleSymp, nodep->name(),
|
||||
LinkNodeMatcherVar{});
|
||||
nodep->v3error("Signal definition not found, and implicit disabled with "
|
||||
"`default_nettype: "
|
||||
<< nodep->prettyNameQ() << '\n'
|
||||
<< (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(),
|
||||
VFlagLogicPacked{}, 1};
|
||||
newp->trace(modp->modTrace());
|
||||
nodep->varp(newp);
|
||||
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*/);
|
||||
// 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(), 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) {
|
||||
// Return a variable if possible, auto converting a modport to variable
|
||||
@@ -2112,6 +2116,25 @@ private:
|
||||
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) {
|
||||
if (nodep->taskp() && VN_IS(nodep->taskp(), Task) && VN_IS(nodep, FuncRef)) {
|
||||
nodep->v3error("Illegal call of a task as a function: " << nodep->prettyNameQ());
|
||||
@@ -2120,7 +2143,7 @@ private:
|
||||
void checkNoDot(AstNode* nodep) {
|
||||
if (VL_UNLIKELY(m_ds.m_dotPos != DP_NONE)) {
|
||||
// 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()
|
||||
<< " in dotted expression");
|
||||
m_ds.m_dotErr = true;
|
||||
@@ -2223,11 +2246,30 @@ private:
|
||||
}
|
||||
}
|
||||
void importSymbolsFromExtended(AstClass* const nodep, AstClassExtends* const cextp) {
|
||||
AstClass* const classp = cextp->classp();
|
||||
VSymEnt* const srcp = m_statep->getNodeSym(classp);
|
||||
if (classp->isInterfaceClass()) importImplementsClass(nodep, srcp, classp);
|
||||
AstClass* const baseClassp = cextp->classp();
|
||||
VSymEnt* const srcp = m_statep->getNodeSym(baseClassp);
|
||||
if (baseClassp->isInterfaceClass()) importImplementsClass(nodep, srcp, baseClassp);
|
||||
if (!cextp->isImplements()) m_curSymp->importFromClass(m_statep->symsp(), srcp);
|
||||
}
|
||||
void classExtendImport(AstClass* nodep) {
|
||||
// A class reference might be to a class that is later in Ast due to
|
||||
// e.g. parmaeterization or referring to a "class (type T) extends T"
|
||||
// Resolve it so later Class:: references into its base classes work
|
||||
VL_RESTORER(m_ds);
|
||||
VSymEnt* const srcp = m_statep->getNodeSym(nodep);
|
||||
m_ds.init(srcp);
|
||||
iterate(nodep);
|
||||
}
|
||||
bool checkPinRef(AstPin* pinp, VVarType refVarType) {
|
||||
// In instantiations of modules/ifaces, we shouldn't connect port pins to submodule's
|
||||
// parameters or vice versa
|
||||
return pinp->param() == refVarType.isParam();
|
||||
}
|
||||
void updateVarUse(AstVar* nodep) {
|
||||
// Avoid dotted.PARAM false positive when in a parameter block
|
||||
// that is if ()'ed off by same dotted name as another block
|
||||
if (nodep && nodep->isParam()) nodep->usedParam(true);
|
||||
}
|
||||
|
||||
// VISITs
|
||||
void visit(AstNetlist* nodep) override {
|
||||
@@ -2322,7 +2364,32 @@ private:
|
||||
UASSERT_OBJ(m_pinSymp, nodep, "Pin not under instance?");
|
||||
VSymEnt* const foundp = m_pinSymp->findIdFlat(nodep->name());
|
||||
const char* const whatp = nodep->param() ? "parameter pin" : "pin";
|
||||
if (!foundp) {
|
||||
bool pinCheckFail = false;
|
||||
if (foundp) {
|
||||
if (AstVar* const refp = VN_CAST(foundp->nodep(), Var)) {
|
||||
if (!refp->isIO() && !refp->isParam() && !refp->isIfaceRef()) {
|
||||
nodep->v3error(ucfirst(whatp)
|
||||
<< " is not an in/out/inout/param/interface: "
|
||||
<< nodep->prettyNameQ());
|
||||
} else if (!checkPinRef(nodep, refp->varType())) {
|
||||
pinCheckFail = true;
|
||||
} else {
|
||||
nodep->modVarp(refp);
|
||||
markAndCheckPinDup(nodep, refp, whatp);
|
||||
}
|
||||
} else if (AstParamTypeDType* const refp
|
||||
= VN_CAST(foundp->nodep(), ParamTypeDType)) {
|
||||
if (!checkPinRef(nodep, refp->varType())) {
|
||||
pinCheckFail = true;
|
||||
} else {
|
||||
nodep->modPTypep(refp);
|
||||
markAndCheckPinDup(nodep, refp, whatp);
|
||||
}
|
||||
} else {
|
||||
nodep->v3error(ucfirst(whatp) << " not found: " << nodep->prettyNameQ());
|
||||
}
|
||||
}
|
||||
if (!foundp || pinCheckFail) {
|
||||
if (nodep->name() == "__paramNumber1" && m_cellp
|
||||
&& VN_IS(m_cellp->modp(), Primitive)) {
|
||||
// Primitive parameter is really a delay we can just ignore
|
||||
@@ -2338,19 +2405,6 @@ private:
|
||||
ucfirst(whatp)
|
||||
<< " not found: " << nodep->prettyNameQ() << '\n'
|
||||
<< (suggest.empty() ? "" : nodep->warnMore() + suggest));
|
||||
} else if (AstVar* const refp = VN_CAST(foundp->nodep(), Var)) {
|
||||
if (!refp->isIO() && !refp->isParam() && !refp->isIfaceRef()) {
|
||||
nodep->v3error(ucfirst(whatp) << " is not an in/out/inout/param/interface: "
|
||||
<< nodep->prettyNameQ());
|
||||
} else {
|
||||
nodep->modVarp(refp);
|
||||
markAndCheckPinDup(nodep, refp, whatp);
|
||||
}
|
||||
} else if (AstParamTypeDType* const refp = VN_CAST(foundp->nodep(), ParamTypeDType)) {
|
||||
nodep->modPTypep(refp);
|
||||
markAndCheckPinDup(nodep, refp, whatp);
|
||||
} else {
|
||||
nodep->v3error(ucfirst(whatp) << " not found: " << nodep->prettyNameQ());
|
||||
}
|
||||
}
|
||||
// Early return() above when deleted
|
||||
@@ -2400,9 +2454,9 @@ private:
|
||||
} else {
|
||||
const auto cextp = classp->extendsp();
|
||||
UASSERT_OBJ(cextp, nodep, "Bad super extends link");
|
||||
const auto sclassp = cextp->classp();
|
||||
UASSERT_OBJ(sclassp, nodep, "Bad superclass");
|
||||
m_ds.m_dotSymp = m_statep->getNodeSym(sclassp);
|
||||
const auto baseClassp = cextp->classp();
|
||||
UASSERT_OBJ(baseClassp, nodep, "Bad superclass");
|
||||
m_ds.m_dotSymp = m_statep->getNodeSym(baseClassp);
|
||||
UINFO(8, " super. " << m_ds.ascii() << endl);
|
||||
}
|
||||
}
|
||||
@@ -2533,13 +2587,15 @@ private:
|
||||
string expectWhat;
|
||||
bool allowScope = false;
|
||||
bool allowVar = false;
|
||||
bool allowFTask = false;
|
||||
bool staticAccess = false;
|
||||
if (m_ds.m_dotPos == DP_PACKAGE) {
|
||||
// {package}::{a}
|
||||
AstNodeModule* classOrPackagep = nullptr;
|
||||
expectWhat = "scope/variable";
|
||||
expectWhat = "scope/variable/func";
|
||||
allowScope = true;
|
||||
allowVar = true;
|
||||
allowFTask = true;
|
||||
staticAccess = true;
|
||||
UASSERT_OBJ(VN_IS(m_ds.m_dotp->lhsp(), ClassOrPackageRef), m_ds.m_dotp->lhsp(),
|
||||
"Bad package link");
|
||||
@@ -2620,6 +2676,13 @@ private:
|
||||
<< cellp->modp()->prettyNameQ());
|
||||
}
|
||||
}
|
||||
} else if (allowFTask && VN_IS(foundp->nodep(), NodeFTask)) {
|
||||
AstTaskRef* const taskrefp
|
||||
= new AstTaskRef{nodep->fileline(), nodep->name(), nullptr};
|
||||
nodep->replaceWith(taskrefp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
if (start) m_ds = lastStates;
|
||||
return;
|
||||
} else if (AstVar* const varp = foundToVarp(foundp, nodep, VAccess::READ)) {
|
||||
AstIfaceRefDType* const ifacerefp
|
||||
= LinkDotState::ifaceRefFromArray(varp->subDTypep());
|
||||
@@ -2820,11 +2883,11 @@ private:
|
||||
if (checkImplicit) {
|
||||
// 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
|
||||
auto varp = createImplicitVar(m_curSymp, nodep, m_modp, m_modSymp, err);
|
||||
AstVarRef* const newp
|
||||
= new AstVarRef{nodep->fileline(), nodep->name(), VAccess::READ};
|
||||
= new AstVarRef{nodep->fileline(), varp, VAccess::READ};
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
createImplicitVar(m_curSymp, newp, m_modp, m_modSymp, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2842,19 +2905,26 @@ private:
|
||||
"class reference parameter not removed by V3Param");
|
||||
VL_RESTORER(m_ds);
|
||||
VL_RESTORER(m_pinSymp);
|
||||
{
|
||||
// ClassRef's have pins, so track
|
||||
if (nodep->classOrPackagep()) {
|
||||
m_pinSymp = m_statep->getNodeSym(nodep->classOrPackagep());
|
||||
}
|
||||
m_ds.init(m_curSymp);
|
||||
UINFO(4, "(Backto) Link ClassOrPackageRef: " << nodep << endl);
|
||||
iterateChildren(nodep);
|
||||
|
||||
// ClassRef's have pins, so track
|
||||
if (nodep->classOrPackagep()) {
|
||||
m_pinSymp = m_statep->getNodeSym(nodep->classOrPackagep());
|
||||
}
|
||||
if (m_statep->forPrimary() && VN_IS(nodep->classOrPackagep(), Class) && !nodep->paramsp()
|
||||
AstClass* const refClassp = VN_CAST(nodep->classOrPackagep(), Class);
|
||||
// Make sure any extends() are properly imported within referenced class
|
||||
if (refClassp && !m_statep->forPrimary()) classExtendImport(refClassp);
|
||||
|
||||
m_ds.init(m_curSymp);
|
||||
UINFO(4, "(Backto) Link ClassOrPackageRef: " << nodep << endl);
|
||||
iterateChildren(nodep);
|
||||
|
||||
AstClass* const modClassp = VN_CAST(m_modp, Class);
|
||||
if (m_statep->forPrimary() && refClassp && !nodep->paramsp()
|
||||
&& nodep->classOrPackagep()->hasGParam()
|
||||
// Don't warn on typedefs, which are hard to know if there's a param somewhere buried
|
||||
&& VN_IS(nodep->classOrPackageNodep(), Class)) {
|
||||
&& VN_IS(nodep->classOrPackageNodep(), Class)
|
||||
// References to class:: within class itself are OK per IEEE (UVM does this)
|
||||
&& modClassp != refClassp) {
|
||||
nodep->v3error("Reference to parameterized class without #() (IEEE 1800-2017 8.25.1)\n"
|
||||
<< nodep->warnMore() << "... Suggest use '"
|
||||
<< nodep->classOrPackageNodep()->prettyName() << "#()'");
|
||||
@@ -2873,6 +2943,7 @@ private:
|
||||
if (AstVar* const varp
|
||||
= foundp ? foundToVarp(foundp, nodep, nodep->access()) : nullptr) {
|
||||
nodep->varp(varp);
|
||||
updateVarUse(nodep->varp());
|
||||
// Generally set by parse, but might be an import
|
||||
nodep->classOrPackagep(foundp->classOrPackagep());
|
||||
}
|
||||
@@ -2915,6 +2986,7 @@ private:
|
||||
AstVar* const varp
|
||||
= foundp ? foundToVarp(foundp, nodep, nodep->access()) : nullptr;
|
||||
nodep->varp(varp);
|
||||
updateVarUse(nodep->varp());
|
||||
UINFO(7, " Resolved " << nodep << endl); // Also prints varp
|
||||
if (!nodep->varp()) {
|
||||
nodep->v3error("Can't find definition of "
|
||||
@@ -2952,6 +3024,7 @@ private:
|
||||
// later optimizations to deal with VarXRef.
|
||||
nodep->varp(vscp->varp());
|
||||
nodep->varScopep(vscp);
|
||||
updateVarUse(nodep->varp());
|
||||
UINFO(7, " Resolved " << nodep << endl); // Also prints taskp
|
||||
AstVarRef* const newvscp
|
||||
= new AstVarRef{nodep->fileline(), vscp, nodep->access()};
|
||||
@@ -3026,7 +3099,8 @@ private:
|
||||
}
|
||||
UASSERT_OBJ(cpackagerefp->classOrPackagep(), m_ds.m_dotp->lhsp(), "Bad package link");
|
||||
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;
|
||||
} else if (m_ds.m_dotp && m_ds.m_dotPos == DP_FINAL) {
|
||||
nodep->dotted(m_ds.m_dotText); // Maybe ""
|
||||
@@ -3129,7 +3203,15 @@ private:
|
||||
} else if (nodep->name() == "randomize" || nodep->name() == "srandom"
|
||||
|| nodep->name() == "get_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() == "") {
|
||||
if (nodep->pli()) {
|
||||
if (v3Global.opt.bboxSys()) {
|
||||
@@ -3215,7 +3297,7 @@ private:
|
||||
if (nodep->user3SetOnce()) return;
|
||||
if (m_ds.m_dotPos
|
||||
== 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");
|
||||
m_ds.m_dotErr = true;
|
||||
return;
|
||||
@@ -3273,7 +3355,16 @@ private:
|
||||
{
|
||||
m_ftaskp = nodep;
|
||||
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
|
||||
const bool isNew = nodep->name() == "new";
|
||||
if (isNew) m_explicitSuperNew = false;
|
||||
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_ftaskp = nullptr;
|
||||
@@ -3389,6 +3480,7 @@ private:
|
||||
checkNoDot(nodep);
|
||||
VL_RESTORER(m_curSymp);
|
||||
VL_RESTORER(m_modSymp);
|
||||
VL_RESTORER(m_modp);
|
||||
VL_RESTORER(m_ifClassImpNames);
|
||||
VL_RESTORER(m_insideClassExtParam);
|
||||
{
|
||||
@@ -3426,26 +3518,28 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
if (AstClass* const classp = cextp->classOrNullp()) {
|
||||
if (AstClass* const baseClassp = cextp->classOrNullp()) {
|
||||
// Already converted. Update symbol table to link unlinked members.
|
||||
// Base class has to be visited in a case if its extends statement
|
||||
// needs to be handled. Recursive inheritance was already checked.
|
||||
if (classp == nodep) {
|
||||
// Must be here instead of in LinkDotParam to handle
|
||||
// "class (type T) extends T".
|
||||
if (baseClassp == nodep) {
|
||||
cextp->v3error("Attempting to extend class " << nodep->prettyNameQ()
|
||||
<< " from itself");
|
||||
} else if (cextp->isImplements() && !classp->isInterfaceClass()) {
|
||||
} else if (cextp->isImplements() && !baseClassp->isInterfaceClass()) {
|
||||
cextp->v3error("Attempting to implement from non-interface class "
|
||||
<< classp->prettyNameQ() << '\n'
|
||||
<< baseClassp->prettyNameQ() << '\n'
|
||||
<< "... Suggest use 'extends'");
|
||||
} else if (!cextp->isImplements() && !nodep->isInterfaceClass()
|
||||
&& classp->isInterfaceClass()) {
|
||||
&& baseClassp->isInterfaceClass()) {
|
||||
cextp->v3error("Attempting to extend from interface class "
|
||||
<< classp->prettyNameQ() << '\n'
|
||||
<< baseClassp->prettyNameQ() << '\n'
|
||||
<< "... Suggest use 'implements'");
|
||||
}
|
||||
classp->isExtended(true);
|
||||
baseClassp->isExtended(true);
|
||||
nodep->isExtended(true);
|
||||
iterate(classp);
|
||||
iterate(baseClassp);
|
||||
importSymbolsFromExtended(nodep, cextp);
|
||||
continue;
|
||||
}
|
||||
@@ -3457,15 +3551,16 @@ private:
|
||||
// V3Width when determines types needs to find enum values and such
|
||||
// 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) {
|
||||
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)) {
|
||||
AstEnumItemRef* const newp = new AstEnumItemRef{
|
||||
aitemp->fileline(), aitemp, it->second->classOrPackagep()};
|
||||
UINFO(8, "Class import noderef '" << it->first << "' " << newp << endl);
|
||||
nodep->addMembersp(newp);
|
||||
memberMap.insert(nodep, newp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3476,7 +3571,8 @@ private:
|
||||
if (nodep->user3SetOnce()) return;
|
||||
if (AstNode* const cpackagep = nodep->classOrPackageOpp()) {
|
||||
if (AstClassOrPackageRef* const cpackagerefp = VN_CAST(cpackagep, ClassOrPackageRef)) {
|
||||
if (cpackagerefp->paramsp()) {
|
||||
const AstClass* const clsp = VN_CAST(cpackagerefp->classOrPackageNodep(), Class);
|
||||
if (clsp && clsp->isParameterized()) {
|
||||
// Unable to link before the instantiation of parameter classes.
|
||||
// The class reference node has to be visited to properly link parameters.
|
||||
iterate(cpackagep);
|
||||
@@ -3595,6 +3691,19 @@ private:
|
||||
UINFO(5, " AstCellArrayRef: " << nodep << " " << m_ds.ascii() << endl);
|
||||
// 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 {
|
||||
checkNoDot(nodep);
|
||||
@@ -3647,6 +3756,7 @@ void V3LinkDot::linkDotGuts(AstNetlist* rootp, VLinkDotStep step) {
|
||||
state.computeScopeAliases();
|
||||
state.dumpSelf();
|
||||
{ LinkDotResolveVisitor{rootp, &state}; }
|
||||
state.dumpSelf();
|
||||
}
|
||||
|
||||
void V3LinkDot::linkDotPrimary(AstNetlist* nodep) {
|
||||
|
||||
+33
-12
@@ -61,13 +61,30 @@ private:
|
||||
|
||||
// STATE
|
||||
AstNodeFTask* m_ftaskp = nullptr; // Function or task we're inside
|
||||
AstNodeModule* m_modp = nullptr; // Module we're inside
|
||||
int m_modIncrementsNum = 0; // Var name counter
|
||||
InsertMode m_insMode = IM_BEFORE; // How to insert
|
||||
AstNode* m_insStmtp = nullptr; // Where to insert statement
|
||||
bool m_unsupportedHere = false; // Used to detect where it's not supported yet
|
||||
|
||||
// METHODS
|
||||
void insertBeforeStmt(AstNode* nodep, AstNode* newp) {
|
||||
void insertOnTop(AstNode* newp) {
|
||||
// Add the thing directly under the current TFunc/Module
|
||||
AstNode* stmtsp = nullptr;
|
||||
if (m_ftaskp) {
|
||||
stmtsp = m_ftaskp->stmtsp();
|
||||
} else if (m_modp) {
|
||||
stmtsp = m_modp->stmtsp();
|
||||
}
|
||||
UASSERT(stmtsp, "Variable not under FTASK/MODULE");
|
||||
newp->addNext(stmtsp->unlinkFrBackWithNext());
|
||||
if (m_ftaskp) {
|
||||
m_ftaskp->addStmtsp(newp);
|
||||
} else if (m_modp) {
|
||||
m_modp->addStmtsp(newp);
|
||||
}
|
||||
}
|
||||
void insertNextToStmt(AstNode* nodep, AstNode* newp) {
|
||||
// Return node that must be visited, if any
|
||||
if (debug() >= 9) newp->dumpTree("- newstmt: ");
|
||||
UASSERT_OBJ(m_insStmtp, nodep, "Function not underneath a statement");
|
||||
@@ -88,7 +105,9 @@ private:
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
VL_RESTORER(m_modIncrementsNum);
|
||||
m_modp = nodep;
|
||||
m_modIncrementsNum = 0;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
@@ -244,15 +263,14 @@ private:
|
||||
return;
|
||||
}
|
||||
AstNodeExpr* const readp = nodep->rhsp();
|
||||
AstNodeExpr* const writep = nodep->thsp();
|
||||
AstNodeExpr* const writep = nodep->thsp()->unlinkFrBack();
|
||||
|
||||
AstConst* const constp = VN_AS(nodep->lhsp(), Const);
|
||||
UASSERT_OBJ(nodep, constp, "Expecting CONST");
|
||||
const AstNode* const backp = nodep->backp();
|
||||
AstConst* const newconstp = constp->cloneTree(true);
|
||||
|
||||
// Prepare a temporary variable
|
||||
FileLine* const fl = backp->fileline();
|
||||
FileLine* const fl = nodep->fileline();
|
||||
const string name = string{"__Vincrement"} + cvtToStr(++m_modIncrementsNum);
|
||||
AstVar* const varp = new AstVar{
|
||||
fl, VVarType::BLOCKTEMP, name, VFlagChildDType{},
|
||||
@@ -260,7 +278,7 @@ private:
|
||||
if (m_ftaskp) varp->funcLocal(true);
|
||||
|
||||
// Declare the variable
|
||||
insertBeforeStmt(nodep, varp);
|
||||
insertOnTop(varp);
|
||||
|
||||
// Define what operation will we be doing
|
||||
AstNodeExpr* operp;
|
||||
@@ -273,17 +291,20 @@ private:
|
||||
if (VN_IS(nodep, PreAdd) || VN_IS(nodep, PreSub)) {
|
||||
// PreAdd/PreSub operations
|
||||
// Immediately after declaration - increment it by one
|
||||
varp->addNextHere(new AstAssign{fl, writep->cloneTree(true),
|
||||
new AstVarRef{fl, varp, VAccess::READ}});
|
||||
AstAssign* const assignp
|
||||
= new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, operp};
|
||||
insertNextToStmt(nodep, assignp);
|
||||
// Immediately after incrementing - assign it to the original variable
|
||||
varp->addNextHere(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, operp});
|
||||
assignp->addNextHere(
|
||||
new AstAssign{fl, writep, new AstVarRef{fl, varp, VAccess::READ}});
|
||||
} else {
|
||||
// PostAdd/PostSub operations
|
||||
// assign the original variable to the temporary one
|
||||
varp->addNextHere(new AstAssign{fl, writep->cloneTree(true), operp});
|
||||
// Assign the original variable to the temporary one
|
||||
AstAssign* const assignp = new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
|
||||
readp->cloneTree(true)};
|
||||
insertNextToStmt(nodep, assignp);
|
||||
// Increment the original variable by one
|
||||
varp->addNextHere(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
|
||||
readp->cloneTree(true)});
|
||||
assignp->addNextHere(new AstAssign{fl, writep, operp});
|
||||
}
|
||||
|
||||
// Replace the node with the temporary
|
||||
|
||||
@@ -196,6 +196,7 @@ private:
|
||||
// Spec says value is integral, if negative is ignored
|
||||
AstVar* const varp
|
||||
= new AstVar{nodep->fileline(), VVarType::BLOCKTEMP, name, nodep->findSigned32DType()};
|
||||
varp->lifetime(VLifetime::AUTOMATIC);
|
||||
varp->usedLoopIdx(true);
|
||||
m_modp->addStmtsp(varp);
|
||||
AstNode* initsp = new AstAssign{
|
||||
|
||||
+10
-5
@@ -41,7 +41,6 @@ private:
|
||||
bool m_setContinuously = false; // Set that var has some continuous assignment
|
||||
bool m_setStrengthSpecified = false; // Set that var has assignment with strength specified.
|
||||
VAccess m_setRefLvalue; // Set VarRefs to lvalues for pin assignments
|
||||
AstNodeFTask* m_ftaskp = nullptr; // Function or task we're inside
|
||||
|
||||
// VISITs
|
||||
// Result handing
|
||||
@@ -55,7 +54,8 @@ private:
|
||||
// so it is needed to check only if m_setContinuously is 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,
|
||||
"Assigning to input/const variable: " << nodep->prettyNameQ());
|
||||
}
|
||||
@@ -275,9 +275,14 @@ private:
|
||||
iterateAndNextNull(nodep->thsp());
|
||||
}
|
||||
}
|
||||
void visit(AstNodeFTask* nodep) override {
|
||||
VL_RESTORER(m_ftaskp);
|
||||
m_ftaskp = nodep;
|
||||
void visit(AstMemberSel* nodep) override {
|
||||
if (m_setRefLvalue != VAccess::NOCHANGE) {
|
||||
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);
|
||||
}
|
||||
void visit(AstNodeFTaskRef* nodep) override {
|
||||
|
||||
+3
-1
@@ -53,7 +53,7 @@ void V3LinkLevel::modSortByLevel() {
|
||||
ModVec tops; // Top level modules
|
||||
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep;
|
||||
nodep = VN_AS(nodep->nextp(), NodeModule)) {
|
||||
if (nodep->level() <= 2) tops.push_back(nodep);
|
||||
if (nodep->level() <= 2 && !VN_IS(nodep, NotFoundModule)) tops.push_back(nodep);
|
||||
mods.push_back(nodep);
|
||||
}
|
||||
if (tops.size() >= 2) {
|
||||
@@ -289,6 +289,8 @@ void V3LinkLevel::wrapTopCell(AstNetlist* rootp) {
|
||||
varp->trace(false);
|
||||
}
|
||||
|
||||
if (v3Global.opt.noTraceTop() && varp->isIO()) { varp->trace(false); }
|
||||
|
||||
AstPin* const pinp = new AstPin{
|
||||
oldvarp->fileline(), 0, varp->name(),
|
||||
new AstVarRef{varp->fileline(), varp,
|
||||
|
||||
+111
-10
@@ -76,6 +76,8 @@ private:
|
||||
// without suppressing other token's messages as a side effect.
|
||||
// We could have verilog.l create a new one on every token,
|
||||
// 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()) {
|
||||
nodep->fileline(new FileLine{nodep->fileline()});
|
||||
}
|
||||
@@ -125,6 +127,58 @@ private:
|
||||
&& !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
|
||||
void visit(AstNodeFTask* nodep) override {
|
||||
if (!nodep->user1SetOnce()) { // Process only once.
|
||||
@@ -242,8 +296,16 @@ private:
|
||||
nodep->v3warn(STATICVAR, "Static variable with assignment declaration declared in a "
|
||||
"loop converted to automatic");
|
||||
}
|
||||
if (m_ftaskp && m_ftaskp->classMethod() && nodep->lifetime().isNone()) {
|
||||
nodep->lifetime(VLifetime::AUTOMATIC);
|
||||
if (m_ftaskp) {
|
||||
bool classMethod = m_ftaskp->classMethod();
|
||||
if (!classMethod) {
|
||||
AstClassOrPackageRef* const pkgrefp
|
||||
= VN_CAST(m_ftaskp->classOrPackagep(), ClassOrPackageRef);
|
||||
if (pkgrefp && VN_IS(pkgrefp->classOrPackagep(), Class)) classMethod = true;
|
||||
}
|
||||
if (classMethod && nodep->lifetime().isNone()) {
|
||||
nodep->lifetime(VLifetime::AUTOMATIC);
|
||||
}
|
||||
}
|
||||
if (nodep->lifetime().isNone() && nodep->varType() != VVarType::PORT) {
|
||||
nodep->lifetime(m_lifetime);
|
||||
@@ -316,9 +378,11 @@ private:
|
||||
// Making an AstAssign (vs AstAssignW) to a wire is an error, suppress it
|
||||
FileLine* const newfl = new FileLine{fl};
|
||||
newfl->warnOff(V3ErrorCode::PROCASSWIRE, true);
|
||||
auto* const assp
|
||||
= new AstAssign{newfl, new AstVarRef{newfl, nodep->name(), VAccess::WRITE},
|
||||
VN_AS(nodep->valuep()->unlinkFrBack(), NodeExpr)};
|
||||
// Create a ParseRef to the wire. We cannot use the var as it may be deleted if
|
||||
// it's a port (see t_var_set_link.v)
|
||||
auto* const assp = new AstAssign{
|
||||
newfl, new AstParseRef{newfl, VParseRefExp::PX_TEXT, nodep->name()},
|
||||
VN_AS(nodep->valuep()->unlinkFrBack(), NodeExpr)};
|
||||
if (nodep->lifetime().isAutomatic()) {
|
||||
nodep->addNextHere(new AstInitialAutomatic{newfl, assp});
|
||||
} else {
|
||||
@@ -327,7 +391,7 @@ private:
|
||||
} // 4. Under blocks, it's an initial value to be under an assign
|
||||
else {
|
||||
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)});
|
||||
}
|
||||
}
|
||||
@@ -476,6 +540,7 @@ private:
|
||||
void visit(AstForeach* nodep) override {
|
||||
// FOREACH(array, loopvars, body)
|
||||
UINFO(9, "FOREACH " << nodep << endl);
|
||||
cleanFileline(nodep);
|
||||
// Separate iteration vars from base from variable
|
||||
// Input:
|
||||
// v--- arrayp
|
||||
@@ -510,23 +575,39 @@ private:
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstRepeat* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
VL_RESTORER(m_insideLoop);
|
||||
{
|
||||
m_insideLoop = true;
|
||||
checkIndent(nodep, nodep->stmtsp());
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstDoWhile* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
VL_RESTORER(m_insideLoop);
|
||||
{
|
||||
m_insideLoop = true;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstWait* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
iterateChildren(nodep);
|
||||
if (nodep->condp()->isZero()) {
|
||||
// Special case "wait(0)" we won't throw WAITCONST as user wrote
|
||||
// it that way with presumed intent - UVM does this.
|
||||
FileLine* const newfl = nodep->fileline();
|
||||
newfl->warnOff(V3ErrorCode::WAITCONST, true);
|
||||
nodep->fileline(newfl);
|
||||
}
|
||||
}
|
||||
void visit(AstWhile* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
VL_RESTORER(m_insideLoop);
|
||||
{
|
||||
m_insideLoop = true;
|
||||
checkIndent(nodep, nodep->stmtsp());
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
@@ -580,14 +661,25 @@ private:
|
||||
// The genblk name will get attached to the if true/false LOWER begin block(s)
|
||||
const bool nestedIf = nestedIfBegin(nodep);
|
||||
// It's not FOR(BEGIN(...)) but we earlier changed it to BEGIN(FOR(...))
|
||||
int assignGenBlkNum = -1;
|
||||
if (nodep->genforp()) {
|
||||
++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() == ""
|
||||
&& (VN_IS(backp, CaseItem) || VN_IS(backp, GenIf)) && !nestedIf) {
|
||||
nodep->name("genblk" + cvtToStr(m_genblkAbove));
|
||||
if (assignGenBlkNum != -1) {
|
||||
nodep->name("genblk" + cvtToStr(assignGenBlkNum));
|
||||
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() != "") {
|
||||
VL_RESTORER(m_genblkAbove);
|
||||
VL_RESTORER(m_genblkNum);
|
||||
@@ -611,6 +703,7 @@ private:
|
||||
}
|
||||
void visit(AstGenIf* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
checkIndent(nodep, nodep->elsesp() ? nodep->elsesp() : nodep->thensp());
|
||||
const bool nestedIf
|
||||
= (VN_IS(nodep->backp(), Begin) && nestedIfBegin(VN_CAST(nodep->backp(), Begin)));
|
||||
if (nestedIf) {
|
||||
@@ -655,6 +748,11 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
void visit(AstIf* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
checkIndent(nodep, nodep->elsesp() ? nodep->elsesp() : nodep->thensp());
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstPrintTimeScale* nodep) override {
|
||||
// Inlining may change hierarchy, so just save timescale where needed
|
||||
cleanFileline(nodep);
|
||||
@@ -683,6 +781,7 @@ private:
|
||||
nodep->timeunit(m_modp->timeunit());
|
||||
}
|
||||
void visit(AstTimeUnit* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
iterateChildren(nodep);
|
||||
nodep->timeunit(m_modp->timeunit());
|
||||
}
|
||||
@@ -711,6 +810,7 @@ private:
|
||||
}
|
||||
}
|
||||
void visit(AstClocking* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
VL_RESTORER(m_defaultInSkewp);
|
||||
VL_RESTORER(m_defaultOutSkewp);
|
||||
// Find default input and output skews
|
||||
@@ -745,6 +845,7 @@ private:
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstClockingItem* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
if (nodep->direction() == VDirection::OUTPUT) {
|
||||
if (!nodep->skewp()) {
|
||||
if (m_defaultOutSkewp) {
|
||||
|
||||
+54
-2
@@ -32,6 +32,7 @@
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3String.h"
|
||||
#include "V3Task.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
@@ -45,7 +46,8 @@ class LinkResolveVisitor final : public VNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstCaseItem::user2() // bool Moved default caseitems
|
||||
// AstCaseItem::user2() // bool Moved default caseitems
|
||||
// AstNodeFTaskRef::user2() // bool Processing - to check for recursion
|
||||
const VNUser2InUse m_inuser2;
|
||||
|
||||
// STATE
|
||||
@@ -89,7 +91,9 @@ private:
|
||||
}
|
||||
}
|
||||
void visit(AstNodeCoverOrAssert* nodep) override {
|
||||
if (m_assertp) nodep->v3error("Assert not allowed under another assert");
|
||||
if (m_assertp) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assert not allowed under another assert");
|
||||
}
|
||||
m_assertp = nodep;
|
||||
iterateChildren(nodep);
|
||||
m_assertp = nullptr;
|
||||
@@ -111,6 +115,7 @@ private:
|
||||
}
|
||||
|
||||
void visit(AstNodeFTask* nodep) override {
|
||||
// Note AstLet is handled specifically elsewhere
|
||||
// NodeTask: Remember its name for later resolution
|
||||
if (m_underGenerate) nodep->underGenerate(true);
|
||||
// Remember the existing symbol table scope
|
||||
@@ -138,6 +143,47 @@ private:
|
||||
}
|
||||
void visit(AstNodeFTaskRef* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (AstLet* letp = VN_CAST(nodep->taskp(), Let)) {
|
||||
UINFO(7, "letSubstitute() " << nodep << " <- " << letp << endl);
|
||||
if (letp->user2()) {
|
||||
nodep->v3error("Recursive let substitution " << letp->prettyNameQ());
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
letp->user2(true);
|
||||
// letp->dumpTree("-let-let ");
|
||||
// nodep->dumpTree("-let-ref ");
|
||||
AstStmtExpr* const letStmtp = VN_AS(letp->stmtsp(), StmtExpr);
|
||||
AstNodeExpr* const newp = letStmtp->exprp()->cloneTree(false);
|
||||
const V3TaskConnects tconnects = V3Task::taskConnects(nodep, letp->stmtsp());
|
||||
std::map<const AstVar*, AstNodeExpr*> portToExprs;
|
||||
for (const auto& tconnect : tconnects) {
|
||||
const AstVar* const portp = tconnect.first;
|
||||
const AstArg* const argp = tconnect.second;
|
||||
AstNodeExpr* const pinp = argp->exprp();
|
||||
if (!pinp) continue; // Argument error we'll find later
|
||||
portToExprs.emplace(portp, pinp);
|
||||
}
|
||||
// Replace VarRefs of arguments with the argument values
|
||||
newp->foreach([&](AstVarRef* refp) { //
|
||||
const auto it = portToExprs.find(refp->varp());
|
||||
if (it != portToExprs.end()) {
|
||||
AstNodeExpr* const pinp = it->second;
|
||||
UINFO(9, "let pin subst " << refp << " <- " << pinp << endl);
|
||||
// Side effects are copied into pins, to match other simulators
|
||||
refp->replaceWith(pinp->cloneTree(false));
|
||||
VL_DO_DANGLING(pushDeletep(refp), refp);
|
||||
}
|
||||
});
|
||||
// newp->dumpTree("-let-new ");
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
// Iterate to expand further now, so we can look for recursions
|
||||
visit(newp);
|
||||
letp->user2(false);
|
||||
return;
|
||||
}
|
||||
if (nodep->taskp() && (nodep->taskp()->dpiContext() || nodep->taskp()->dpiExport())) {
|
||||
nodep->scopeNamep(new AstScopeName{nodep->fileline(), false});
|
||||
}
|
||||
@@ -168,6 +214,12 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void visit(AstLet* nodep) override {
|
||||
// Lets have been (or about to be) substituted, we can remove
|
||||
nodep->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
|
||||
void visit(AstPragma* nodep) override {
|
||||
if (nodep->pragType() == VPragmaType::HIER_BLOCK) {
|
||||
UASSERT_OBJ(m_modp, nodep, "HIER_BLOCK not under a module");
|
||||
|
||||
@@ -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
|
||||
@@ -620,6 +620,7 @@ private:
|
||||
// LCOV_EXCL_START
|
||||
if (debug()) rhsp->dumpTree("Don't know how to fold expression: ");
|
||||
rhsp->v3fatalSrc("Should not try to fold this during conditional merging");
|
||||
return nullptr;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
|
||||
+11
-2
@@ -109,6 +109,10 @@ public:
|
||||
bool try_lock() VL_TRY_ACQUIRE(true) VL_MT_SAFE {
|
||||
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
|
||||
/// It tries to lock the mutex and if it fails, it check if stop request was send.
|
||||
/// It returns after locking mutex.
|
||||
@@ -135,12 +139,17 @@ private:
|
||||
T& m_mutexr;
|
||||
|
||||
public:
|
||||
/// Construct and hold given mutex lock until destruction or unlock()
|
||||
/// Lock given mutex and hold it for the object lifetime.
|
||||
explicit V3LockGuardImp(T& mutexr) VL_ACQUIRE(mutexr) VL_MT_SAFE
|
||||
: m_mutexr(mutexr) { // Need () or GCC 4.8 false warning
|
||||
mutexr.lock();
|
||||
}
|
||||
/// Destruct and unlock the mutex
|
||||
/// Take already locked mutex, and and hold the lock for the object lifetime.
|
||||
explicit V3LockGuardImp(T& mutexr, std::adopt_lock_t) VL_REQUIRES(mutexr) VL_MT_SAFE
|
||||
: m_mutexr(mutexr) { // Need () or GCC 4.8 false warning
|
||||
}
|
||||
|
||||
/// Unlock the mutex
|
||||
~V3LockGuardImp() VL_RELEASE() { m_mutexr.unlock(); }
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user