mirror of
https://github.com/verilator/verilator.git
synced 2026-10-04 00:53:42 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
522bead374 | ||
|
|
030286142f | ||
|
|
8a4ceb5717 | ||
|
|
63fa6accc4 | ||
|
|
28718f964a | ||
|
|
1d486e3a59 | ||
|
|
9b396f7125 | ||
|
|
e16cbce52e | ||
|
|
840970e8f7 | ||
|
|
e390d6779d | ||
|
|
39ee522f92 | ||
|
|
9526efcf72 | ||
|
|
d397e0d838 | ||
|
|
8301fdc6d3 | ||
|
|
f645382f11 | ||
|
|
1ed5557d2d | ||
|
|
28b9216f8a | ||
|
|
0114cb67ca | ||
|
|
ea8f86dd30 | ||
|
|
0ff77fc352 | ||
|
|
341963e820 | ||
|
|
db60b92613 | ||
|
|
98206a4f04 | ||
|
|
4df9e2e0e5 | ||
|
|
34e4cffb62 | ||
|
|
71d76c993c | ||
|
|
e67bdb4c08 | ||
|
|
23f013f2d7 | ||
|
|
9b729b80e0 | ||
|
|
38ad328956 | ||
|
|
f767f0cfaa | ||
|
|
1c79df8630 | ||
|
|
248573205b | ||
|
|
88831ca21b | ||
|
|
26f15e11c4 | ||
|
|
5c0a7e06dc | ||
|
|
290b313dc0 | ||
|
|
93e5ca3f6d | ||
|
|
6ffff8565f | ||
|
|
494e05b326 | ||
|
|
a24f61403a | ||
|
|
292cc54768 | ||
|
|
e8a9662eb5 | ||
|
|
0262819c20 | ||
|
|
d3b93a1113 | ||
|
|
5f8c06c805 | ||
|
|
a5ad26cd28 | ||
|
|
5802818b9a | ||
|
|
df519ff16e | ||
|
|
bf8a88a0ad | ||
|
|
878204db73 | ||
|
|
08c76b1da6 | ||
|
|
e82c9db6da | ||
|
|
65c3cb4708 | ||
|
|
0cab076b86 | ||
|
|
3a28ed24ea | ||
|
|
71775eae4c | ||
|
|
953249aa43 | ||
|
|
229dbbb100 | ||
|
|
4a439beae5 | ||
|
|
49022c3e73 | ||
|
|
c92ced4ac7 | ||
|
|
ba47da6587 | ||
|
|
2c27f22900 | ||
|
|
c0391990ad | ||
|
|
2247e1e345 | ||
|
|
e4847464d4 | ||
|
|
7694a7bf41 | ||
|
|
a686e547cf | ||
|
|
1481d34959 | ||
|
|
04512e5d04 | ||
|
|
9a3aed57b1 | ||
|
|
5a69321be3 | ||
|
|
5ee938fd1c | ||
|
|
f78783e187 | ||
|
|
11bfe7a7bb | ||
|
|
8ba494c71f | ||
|
|
acb63c929b | ||
|
|
26dd5d4550 | ||
|
|
32f288084a | ||
|
|
6db149c588 | ||
|
|
0fed5f8b3e | ||
|
|
a30930bdcc | ||
|
|
3a1355fb54 | ||
|
|
97db128d66 | ||
|
|
3d57256070 | ||
|
|
5dc8fb5b4f | ||
|
|
3f0d434f82 | ||
|
|
2cdc5ea48b | ||
|
|
745605efe3 | ||
|
|
0fbd4313b2 | ||
|
|
9f8f61ef2f | ||
|
|
4b1f4938b8 | ||
|
|
5f1dc73a1b | ||
|
|
5e1fc6e24d | ||
|
|
0ec32ee404 | ||
|
|
97b21b3849 | ||
|
|
214173c6b8 | ||
|
|
3786f59e03 | ||
|
|
fa7234ff68 | ||
|
|
91dd3c5fac | ||
|
|
a378dbd9b2 | ||
|
|
da9521a351 | ||
|
|
af51107587 | ||
|
|
e60f652a64 | ||
|
|
a4813ec677 | ||
|
|
01dadb0a8d | ||
|
|
9dcb7ea6a9 | ||
|
|
adfe072b89 | ||
|
|
b12f8b3d73 | ||
|
|
f56f318217 | ||
|
|
3af0eb7fa0 | ||
|
|
a69cb9b044 | ||
|
|
b68a696859 | ||
|
|
e320b0b581 | ||
|
|
42041f2403 | ||
|
|
881c6ee655 |
@@ -17,6 +17,8 @@ env:
|
||||
CCACHE_COMPRESS: 1
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_LIMIT_MULTIPLE: 0.95
|
||||
INSTALL_DIR: ${{ github.workspace }}/install
|
||||
RELOC_DIR: ${{ github.workspace }}/relloc
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -94,22 +96,32 @@ jobs:
|
||||
compiler:
|
||||
- { cc: clang, cxx: clang++ }
|
||||
- { cc: gcc, cxx: g++ }
|
||||
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, vltmt-0, vltmt-1]
|
||||
reloc: [0]
|
||||
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1]
|
||||
include:
|
||||
# Test with GCC 10 on ubuntu-20.04
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, suite: dist-vlt-0}
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, suite: dist-vlt-1}
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, suite: dist-vlt-2}
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, suite: dist-vlt-3}
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, suite: vltmt-0}
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, suite: vltmt-1}
|
||||
# Test relocated installation - on most common platform only
|
||||
- {os: ubuntu-22.04, compiler: { cc: gcc, cxx: g++ }, reloc: 1, suite: dist-vlt-0}
|
||||
- {os: ubuntu-22.04, compiler: { cc: gcc, cxx: g++ }, reloc: 1, suite: dist-vlt-1}
|
||||
- {os: ubuntu-22.04, compiler: { cc: gcc, cxx: g++ }, reloc: 1, suite: dist-vlt-2}
|
||||
- {os: ubuntu-22.04, compiler: { cc: gcc, cxx: g++ }, reloc: 1, suite: dist-vlt-3}
|
||||
- {os: ubuntu-22.04, compiler: { cc: gcc, cxx: g++ }, reloc: 1, suite: vltmt-0}
|
||||
- {os: ubuntu-22.04, compiler: { cc: gcc, cxx: g++ }, reloc: 1, suite: vltmt-1}
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: Test | ${{ matrix.os }} | ${{ matrix.compiler.cc }} | ${{ matrix.suite }}
|
||||
name: Test | ${{ matrix.os }} | ${{ matrix.compiler.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
|
||||
env:
|
||||
CI_BUILD_STAGE_NAME: test
|
||||
CI_RUNS_ON: ${{ matrix.os }}
|
||||
CI_RELOC: ${{ matrix.reloc }}
|
||||
CC: ${{ matrix.compiler.cc }}
|
||||
CXX: ${{ matrix.compiler.cxx }}
|
||||
CACHE_BASE_KEY: test-${{ matrix.os }}-${{ matrix.compiler.cc }}-${{ matrix.suite }}
|
||||
CACHE_BASE_KEY: test-${{ matrix.os }}-${{ matrix.compiler.cc }}-${{ matrix.reloc }}-${{ matrix.suite }}
|
||||
CCACHE_MAXSIZE: 100M # Per build per suite (* 5 * 5 = 2500M in total)
|
||||
VERILATOR_ARCHIVE: verilator-${{ github.sha }}-${{ matrix.os }}-${{ matrix.compiler.cc }}.tar.gz
|
||||
steps:
|
||||
@@ -127,7 +139,7 @@ jobs:
|
||||
- name: Cache $CCACHE_DIR
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
|
||||
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache2
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ${{ env.CACHE_KEY }}-${{ github.sha }}
|
||||
|
||||
@@ -44,3 +44,5 @@ verilator-config-version.cmake
|
||||
/.idea/
|
||||
/cmake-build-*/
|
||||
/test_regress/snapshot/
|
||||
xmverilog.*
|
||||
xrun.history
|
||||
|
||||
+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.022
|
||||
VERSION 5.024
|
||||
HOMEPAGE_URL https://verilator.org
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
@@ -8,7 +8,60 @@ The changes in each Verilator version are described below. The
|
||||
contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
|
||||
Verilator 5.022 2024-04-24
|
||||
Verilator 5.024 2024-04-05
|
||||
==========================
|
||||
|
||||
**Major:**
|
||||
|
||||
* Add printing summary reports, use `--quiet` or `+verilator+quiet` to suppress (#4909).
|
||||
* Support 1800-2023 keywords, and parsing with UNDEFINED warnings.
|
||||
* Support 1800-2023 preprocessor ifdef expressions.
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Change 1800-2023 to be default language version.
|
||||
* Add DFG 'regularize' pass, and improve variable removal (#4937). [Geza Lore]
|
||||
* Add error when pass net to function argument (#4132) (#4966). [Fuad Ismail]
|
||||
* Add `UNUSEDLOOP` when unused loop is removed (#4926). [Bartłomiej Chmiel, Antmicro Ltd.]
|
||||
* Add custom version for verilator --version packaging (#4954). [Nolan Poe]
|
||||
* Add error on missing pure virtual functions (#4961).
|
||||
* Add error on calling static function without object (#4962).
|
||||
* Add JSON AST dumps (#5020). [Szymon Gizler]
|
||||
* Support 1800-2023 DPI headers, svGetTime/svgGetTimeUnit/svGetTimePrecision methods.
|
||||
* Support 1800-2023 class and function :initial, :extends, :final virtual overrides (#5025).
|
||||
* Support public packed struct / union (#860) (#4878). [Kefa Chen]
|
||||
* Support stream operation on unpacked array (#4714) (#5006). [Fuad Ismail]
|
||||
* Support implicitly-typed variable definitions in for-loop initializers (#4945) (#4986). [Kevin Nygaard]
|
||||
* Support inside range with implicit type conversion (#5026). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Improve installation to be relocatable (#4927). [Geza Lore]
|
||||
* Improve internal ordering code (#4957) (#4990) (#4994) et al. [Geza Lore]
|
||||
* Fix generate blocks in vpi_iterate (#3609) (#4913). [Andrew Nolte]
|
||||
* Fix __Vlip undefined error in --freloop (#4824). [Justin Yao Du]
|
||||
* Fix missing VPI scopes (#4918). [Andrew Nolte]
|
||||
* Fix invalid cast on string structure creation (#4921).
|
||||
* Fix try-lock spuriously fails (#4931) (#4938). [Kamil Rakoczy]
|
||||
* Fix V3Unknown unpacked struct x-assign (#4934). [Yan Xu]
|
||||
* Fix DFG removing forceable signals (#4942). [Geza Lore]
|
||||
* Fix null characters in shortened identifiers (#4946). [Abdul Hameed]
|
||||
* Fix assignment of null into struct member (#4952).
|
||||
* Fix VPI missing scopes 2 (#4965). [Andrew Nolte]
|
||||
* Fix object assignment from conditionals (#4968).
|
||||
* Fix GCC14 warnings on template specialization syntax (#4974) (#4975). [Nolan Poe]
|
||||
* Fix unpacked structure upper bit cleaning (#4978).
|
||||
* Fix tests on MacOS (#4984) (#4985). [Kevin Nygaard]
|
||||
* Fix `--prof-exec` predicted time values (#4988). [Geza Lore]
|
||||
* Fix class type as an associative array parameter (#4997).
|
||||
* Fix inout ports of unpacked struct type (#5000). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix `unique {}` constraints missing semicolon (#5001).
|
||||
* Fix preprocessor to respect strings in joins (#5007).
|
||||
* Fix tracing class parameters (#5014).
|
||||
* Fix memory leaks (#5016). [Geza Lore]
|
||||
* Fix $readmem with missing newline (#5019). [Josse Van Delm]
|
||||
* Fix internal error on missing pattern key (#5023).
|
||||
* Fix tracing replicated hierarchical models (#5027).
|
||||
|
||||
|
||||
Verilator 5.022 2024-02-24
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
|
||||
+49
-20
@@ -203,11 +203,21 @@ verilator.html:
|
||||
verilator.pdf: Makefile
|
||||
$(MAKE) -C docs verilator.pdf
|
||||
|
||||
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
|
||||
VL_INST_BIN_FILES = verilator verilator_bin$(EXEEXT) verilator_bin_dbg$(EXEEXT) verilator_coverage_bin_dbg$(EXEEXT) \
|
||||
verilator_ccache_report verilator_coverage verilator_difftree verilator_gantt verilator_includer verilator_profcfunc
|
||||
# Some scripts go into both the search path and pkgdatadir,
|
||||
# so they can be found by the user, and under $VERILATOR_ROOT.
|
||||
# Public executables intended to be invoked directly by the user
|
||||
# Don't put wildcards in these variables, it might cause an uninstall of other stuff
|
||||
VL_INST_PUBLIC_SCRIPT_FILES = verilator \
|
||||
verilator_coverage \
|
||||
verilator_gantt \
|
||||
verilator_profcfunc \
|
||||
|
||||
VL_INST_PUBLIC_BIN_FILES = verilator_bin$(EXEEXT) \
|
||||
verilator_bin_dbg$(EXEEXT) \
|
||||
verilator_coverage_bin_dbg$(EXEEXT) \
|
||||
|
||||
# Private executabels intended to be invoked by internals
|
||||
# Don't put wildcards in these variables, it might cause an uninstall of other stuff
|
||||
VL_INST_PRIVATE_SCRIPT_FILES = verilator_ccache_report \
|
||||
verilator_includer \
|
||||
|
||||
VL_INST_INC_BLDDIR_FILES = \
|
||||
include/verilated_config.h \
|
||||
@@ -226,19 +236,34 @@ VL_INST_DATA_SRCDIR_FILES = \
|
||||
examples/*/Makefile* \
|
||||
examples/*/vl_* \
|
||||
|
||||
installbin:
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(bindir)
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator $(DESTDIR)$(bindir)/verilator )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_coverage $(DESTDIR)$(bindir)/verilator_coverage )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_gantt $(DESTDIR)$(bindir)/verilator_gantt )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_profcfunc $(DESTDIR)$(bindir)/verilator_profcfunc )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_bin$(EXEEXT) $(DESTDIR)$(bindir)/verilator_bin$(EXEEXT) )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_bin_dbg$(EXEEXT) $(DESTDIR)$(bindir)/verilator_bin_dbg$(EXEEXT) )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_coverage_bin_dbg$(EXEEXT) $(DESTDIR)$(bindir)/verilator_coverage_bin_dbg$(EXEEXT) )
|
||||
mkbindirs:
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/bin
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_includer $(DESTDIR)$(pkgdatadir)/bin/verilator_includer )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_ccache_report $(DESTDIR)$(pkgdatadir)/bin/verilator_ccache_report )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_difftree $(DESTDIR)$(pkgdatadir)/bin/verilator_difftree )
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(bindir)
|
||||
|
||||
installbin: | mkbindirs
|
||||
cd $(srcdir)/bin; \
|
||||
for p in $(VL_INST_PUBLIC_SCRIPT_FILES) ; do \
|
||||
$(INSTALL_PROGRAM) $$p $(DESTDIR)$(pkgdatadir)/bin/$$p; \
|
||||
done
|
||||
cd bin; \
|
||||
for p in $(VL_INST_PUBLIC_BIN_FILES) ; do \
|
||||
$(INSTALL_PROGRAM) $$p $(DESTDIR)$(pkgdatadir)/bin/$$p; \
|
||||
done
|
||||
cd $(srcdir)/bin; \
|
||||
for p in $(VL_INST_PRIVATE_SCRIPT_FILES) ; do \
|
||||
$(INSTALL_PROGRAM) $$p $(DESTDIR)$(pkgdatadir)/bin/$$p; \
|
||||
done
|
||||
|
||||
installredirect: installbin | mkbindirs
|
||||
cp ${srcdir}/bin/redirect ${srcdir}/bin/redirect.tmp
|
||||
perl -p -i -e 'use File::Spec;' \
|
||||
-e' $$path = File::Spec->abs2rel("$(realpath $(DESTDIR)$(pkgdatadir)/bin)", "$(realpath $(DESTDIR)$(bindir))");' \
|
||||
-e 's/RELPATH.*/"$$path";/g' -- "${srcdir}/bin/redirect.tmp"
|
||||
cd $(srcdir)/bin; \
|
||||
for p in $(VL_INST_PUBLIC_SCRIPT_FILES) $(VL_INST_PUBLIC_BIN_FILES) ; do \
|
||||
$(INSTALL_PROGRAM) redirect.tmp $(DESTDIR)$(bindir)/$$p; \
|
||||
done
|
||||
rm ${srcdir}/bin/redirect.tmp
|
||||
|
||||
# Man files can either be part of the original kit, or built in current directory
|
||||
# So important we use $^ so VPATH is searched
|
||||
@@ -281,8 +306,11 @@ installdata:
|
||||
|
||||
# We don't trust rm -rf, so rmdir instead as it will fail if user put in other files
|
||||
uninstall:
|
||||
-cd $(DESTDIR)$(bindir) && rm -f $(VL_INST_BIN_FILES)
|
||||
-cd $(DESTDIR)$(pkgdatadir)/bin && rm -f $(VL_INST_BIN_FILES)
|
||||
-cd $(DESTDIR)$(bindir) && rm -f $(VL_INST_PUBLIC_SCRIPT_FILES)
|
||||
-cd $(DESTDIR)$(bindir) && rm -f $(VL_INST_PUBLIC_BIN_FILES)
|
||||
-cd $(DESTDIR)$(pkgdatadir)/bin && rm -f $(VL_INST_PUBLIC_SCRIPT_FILES)
|
||||
-cd $(DESTDIR)$(pkgdatadir)/bin && rm -f $(VL_INST_PUBLIC_BIN_FILES)
|
||||
-cd $(DESTDIR)$(pkgdatadir)/bin && rm -f $(VL_INST_PRIVATE_SCRIPT_FILES)
|
||||
-cd $(DESTDIR)$(mandir)/man1 && rm -f $(VL_INST_MAN_FILES)
|
||||
-cd $(DESTDIR)$(pkgdatadir) && rm -f $(VL_INST_INC_BLDDIR_FILES)
|
||||
-cd $(DESTDIR)$(pkgdatadir) && rm -f $(VL_INST_INC_SRCDIR_FILES)
|
||||
@@ -311,7 +339,7 @@ uninstall:
|
||||
-rmdir $(DESTDIR)$(pkgconfigdir)
|
||||
|
||||
install: all_nomsg install-all
|
||||
install-all: installbin installman installdata install-msg
|
||||
install-all: installbin installredirect installman installdata install-msg
|
||||
|
||||
install-here: installman info
|
||||
|
||||
@@ -410,6 +438,7 @@ PY_PROGRAMS = \
|
||||
src/cppcheck_filtered \
|
||||
src/flexfix \
|
||||
src/vlcovgen \
|
||||
src/.gdbinit.py \
|
||||
test_regress/t/*.pf \
|
||||
nodist/clang_check_attributes \
|
||||
nodist/code_coverage \
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env perl
|
||||
######################################################################
|
||||
#
|
||||
# Copyright 2003-2024 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
|
||||
#
|
||||
######################################################################
|
||||
|
||||
require 5.006_001;
|
||||
use warnings;
|
||||
use FindBin qw($RealBin $RealScript);
|
||||
use strict;
|
||||
|
||||
my $relpath = RELPATH; # Substituted during Verilator 'make install'
|
||||
|
||||
unshift @ARGV, $RealScript;
|
||||
|
||||
exec { "$RealBin/$relpath/$RealScript" } @ARGV;
|
||||
die "%Error: Exec failed,";
|
||||
+35
-32
@@ -15,6 +15,7 @@ use Getopt::Long;
|
||||
use FindBin qw($RealBin $RealScript);
|
||||
use IO::File;
|
||||
use Pod::Usage;
|
||||
use Cwd qw(realpath);
|
||||
|
||||
use strict;
|
||||
use vars qw($Debug @Opt_Verilator_Sw);
|
||||
@@ -57,6 +58,7 @@ if (! GetOptions(
|
||||
# Switches
|
||||
"gdb!" => \$opt_gdb,
|
||||
"gdbbt!" => \$opt_gdbbt,
|
||||
"quiet!" => \$opt_quiet_exit, # As -quiet implies -quiet-exit
|
||||
"quiet-exit!" => \$opt_quiet_exit,
|
||||
"rr!" => \$opt_rr,
|
||||
"unlimited-stack!" => \$opt_unlimited_stack,
|
||||
@@ -67,6 +69,18 @@ if (! GetOptions(
|
||||
pod2usage(-exitstatus => 2, -verbose => 0);
|
||||
}
|
||||
|
||||
my $verilator_root = realpath("$RealBin/..");
|
||||
if (defined $ENV{VERILATOR_ROOT}) {
|
||||
if ((!-d $ENV{VERILATOR_ROOT}) || $verilator_root ne realpath($ENV{VERILATOR_ROOT})) {
|
||||
warn "%Error: verilator: VERILATOR_ROOT is set to inconsistent path. Suggest leaving it unset.\n";
|
||||
warn "%Error: VERILATOR_ROOT=$ENV{VERILATOR_ROOT}\n";
|
||||
exit 1;
|
||||
}
|
||||
} else {
|
||||
print "export VERILATOR_ROOT='$verilator_root'\n" if $Debug;
|
||||
$ENV{VERILATOR_ROOT} = $verilator_root;
|
||||
}
|
||||
|
||||
if ($opt_gdbbt && !gdb_works()) {
|
||||
warn "-Info: --gdbbt ignored: gdb doesn't seem to be working\n" if $Debug;
|
||||
$opt_gdbbt = 0;
|
||||
@@ -151,30 +165,13 @@ sub debug {
|
||||
# Builds
|
||||
|
||||
sub verilator_bin {
|
||||
my $bin = "";
|
||||
# Use VERILATOR_ROOT if defined, else assume verilator_bin is in the search path
|
||||
my $basename = ($ENV{VERILATOR_BIN}
|
||||
|| ($Debug ? "verilator_bin_dbg" : "verilator_bin"));
|
||||
if (defined($ENV{VERILATOR_ROOT})) {
|
||||
my $dir = $ENV{VERILATOR_ROOT};
|
||||
if (-x "$dir/bin/$basename"
|
||||
|| -x "$dir/bin/$basename.exe") { # From a "make install" into VERILATOR_ROOT
|
||||
$bin = "$dir/bin/$basename";
|
||||
} else {
|
||||
$bin = "$dir/$basename"; # From pointing to kit directory
|
||||
}
|
||||
if (-x "$RealBin/$basename" || -x "$RealBin/$basename.exe") {
|
||||
return "$RealBin/$basename";
|
||||
} else {
|
||||
if (-x "$RealBin/$basename"
|
||||
|| -x "$RealBin/$basename.exe") {
|
||||
$bin = "$RealBin/$basename"; # From path/to/verilator with verilator_bin installed
|
||||
} else {
|
||||
$bin = $basename; # Find in PATH
|
||||
}
|
||||
# Note we don't look under bin/$basename which would be right if running
|
||||
# in the kit dir. Running that would likely break, since
|
||||
# VERILATOR_ROOT wouldn't be set and Verilator won't find internal files.
|
||||
return $basename; # Find in PATH
|
||||
}
|
||||
return $bin;
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
@@ -312,6 +309,7 @@ detailed descriptions of these arguments.
|
||||
+1800-2009ext+<ext> Use SystemVerilog 2009 with file extension <ext>
|
||||
+1800-2012ext+<ext> Use SystemVerilog 2012 with file extension <ext>
|
||||
+1800-2017ext+<ext> Use SystemVerilog 2017 with file extension <ext>
|
||||
+1800-2023ext+<ext> Use SystemVerilog 2023 with file extension <ext>
|
||||
--assert Enable all assertions
|
||||
--assert-case Enable unique/unique0/priority case related checks
|
||||
--autoflush Flush streams after all $displays
|
||||
@@ -350,10 +348,12 @@ detailed descriptions of these arguments.
|
||||
--dump-tree Enable dumping Ast .tree files
|
||||
--dump-tree-addrids Use short identifiers instead of addresses
|
||||
--dump-tree-dot Enable dumping Ast .tree.dot debug files
|
||||
--dump-tree-json Enable dumping Ast .tree.json files and .tree.meta.json file
|
||||
--dump-<srcfile> Enable dumping everything in source file
|
||||
--dumpi-dfg <level> Enable dumping DfgGraphs to .dot files at level
|
||||
--dumpi-graph <level> Enable dumping V3Graphs to .dot files at level
|
||||
--dumpi-tree <level> Enable dumping Ast .tree files at level
|
||||
--dumpi-tree-json <level> Enable dumping Ast .tree.json files at level
|
||||
--dumpi-<srcfile> <level> Enable dumping everything in source file at level
|
||||
-E Preprocess, but do not compile
|
||||
--error-limit <value> Abort after this number of errors
|
||||
@@ -373,7 +373,7 @@ detailed descriptions of these arguments.
|
||||
--generate-key Create random key for --protect-key
|
||||
--getenv <var> Get environment variable with defaults
|
||||
--get-supported <feature> Get if feature is supported
|
||||
--help Display this help
|
||||
--help Show this help
|
||||
--hierarchical Enable hierarchical Verilation
|
||||
-I<dir> Directory to search for includes
|
||||
--if-depth <value> Tune IFDEPTH warning
|
||||
@@ -426,7 +426,9 @@ detailed descriptions of these arguments.
|
||||
--public-params Mark all parameters as public_flat
|
||||
--public-flat-rw Mark all variables, etc as public_flat_rw
|
||||
-pvalue+<name>=<value> Overwrite toplevel parameter
|
||||
--quiet Minimize additional printing
|
||||
--quiet-exit Don't print the command on failure
|
||||
--quiet-stats Don't print statistics
|
||||
--relative-includes Resolve includes relative to current file
|
||||
--reloop-limit Minimum iterations for forming loops
|
||||
--report-unoptflat Extra diagnostics for UNOPTFLAT
|
||||
@@ -441,7 +443,7 @@ detailed descriptions of these arguments.
|
||||
--no-stop-fail Do not call $stop when assertion fails
|
||||
--structs-packed Convert all unpacked structures to packed structures
|
||||
-sv Enable SystemVerilog parsing
|
||||
+systemverilogext+<ext> Synonym for +1800-2017ext+<ext>
|
||||
+systemverilogext+<ext> Synonym for +1800-2023ext+<ext>
|
||||
--threads <threads> Enable multithreading
|
||||
--threads-dpi <mode> Enable multithreaded DPI
|
||||
--threads-max-mtasks <mtasks> Tune maximum mtask partitioning
|
||||
@@ -474,7 +476,7 @@ detailed descriptions of these arguments.
|
||||
--no-verilate Skip Verilation and just compile previously Verilated code
|
||||
+verilog1995ext+<ext> Synonym for +1364-1995ext+<ext>
|
||||
+verilog2001ext+<ext> Synonym for +1364-2001ext+<ext>
|
||||
--version Displays program version and exits
|
||||
--version Show program version and exits
|
||||
--vpi Enable VPI compiles
|
||||
--waiver-output <filename> Create a waiver file based on the linter warnings
|
||||
-Wall Enable all style warnings
|
||||
@@ -508,20 +510,21 @@ description of these arguments.
|
||||
|
||||
=for VL_SPHINX_EXTRACT "_build/gen/args_verilated.rst"
|
||||
|
||||
+verilator+debug Enable debugging
|
||||
+verilator+debugi+<value> Enable debugging at a level
|
||||
+verilator+debug Enable debugging
|
||||
+verilator+debugi+<value> Enable debugging at a level
|
||||
+verilator+coverage+file+<filename> Set coverage output filename
|
||||
+verilator+error+limit+<value> Set error limit
|
||||
+verilator+help Display help
|
||||
+verilator+noassert Disable assert checking
|
||||
+verilator+error+limit+<value> Set error limit
|
||||
+verilator+help Show help
|
||||
+verilator+noassert Disable assert checking
|
||||
+verilator+prof+exec+file+<filename> Set execution profile filename
|
||||
+verilator+prof+exec+start+<value> Set execution profile starting point
|
||||
+verilator+prof+exec+window+<value> Set execution profile duration
|
||||
+verilator+prof+vlt+file+<filename> Set PGO profile filename
|
||||
+verilator+rand+reset+<value> Set random reset technique
|
||||
+verilator+seed+<value> Set random seed
|
||||
+verilator+V Verbose version and config
|
||||
+verilator+version Show version and exit
|
||||
+verilator+quiet Minimize additional printing
|
||||
+verilator+rand+reset+<value> Set random reset technique
|
||||
+verilator+seed+<value> Set random seed
|
||||
+verilator+V Show verbose version and config
|
||||
+verilator+version Show version and exit
|
||||
|
||||
|
||||
=head1 DISTRIBUTION
|
||||
|
||||
+8
-7
@@ -92,13 +92,12 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
sudo apt-get update ||
|
||||
sudo apt-get update
|
||||
# libfl-dev needed for internal coverage's test runs
|
||||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache ||
|
||||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache
|
||||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq ||
|
||||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq
|
||||
# Required for test_regress/t/t_dist_attributes.pl
|
||||
if [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
|
||||
sudo apt-get install libclang-dev mold ||
|
||||
sudo apt-get install libclang-dev mold
|
||||
pip3 install clang==14.0
|
||||
sudo apt-get install python3-clang mold ||
|
||||
sudo apt-get install python3-clang mold
|
||||
fi
|
||||
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ] || [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
|
||||
sudo apt-get install libsystemc-dev ||
|
||||
@@ -107,10 +106,10 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
elif [ "$CI_OS_NAME" = "osx" ]; then
|
||||
brew update
|
||||
# brew cask install gtkwave # fst2vcd hangs at launch, so don't bother
|
||||
brew install ccache perl
|
||||
brew install ccache perl jq
|
||||
elif [ "$CI_OS_NAME" = "freebsd" ]; then
|
||||
# fst2vcd fails with "Could not open '<input file>', exiting."
|
||||
sudo pkg install -y ccache gmake perl5 python3
|
||||
sudo pkg install -y ccache gmake perl5 python3 jq
|
||||
else
|
||||
fatal "Unknown os: '$CI_OS_NAME'"
|
||||
fi
|
||||
@@ -120,6 +119,8 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
fi
|
||||
yes yes | sudo cpan -M $CI_CPAN_REPO -fi Parallel::Forker
|
||||
install-vcddiff
|
||||
# Workaround -fsanitize=address crash
|
||||
sudo sysctl -w vm.mmap_rnd_bits=28
|
||||
else
|
||||
##############################################################################
|
||||
# Unknown build stage
|
||||
|
||||
+23
-6
@@ -40,7 +40,7 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
|
||||
|
||||
if [ "$COVERAGE" != 1 ]; then
|
||||
autoconf
|
||||
./configure --enable-longtests --enable-ccwarn
|
||||
./configure --enable-longtests --enable-ccwarn --prefix="$INSTALL_DIR"
|
||||
ccache -z
|
||||
"$MAKE" -j "$NPROC" -k
|
||||
# 22.04: ccache -s -v
|
||||
@@ -87,23 +87,40 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
# Run sanitize on Ubuntu 22.04 only
|
||||
[ "$CI_RUNS_ON" = 'ubuntu-22.04' ] && sanitize='--sanitize' || sanitize=''
|
||||
|
||||
TEST_REGRESS=test_regress
|
||||
if [ "$CI_RELOC" == 1 ]; then
|
||||
# Testing that the installation is relocatable.
|
||||
"$MAKE" install
|
||||
mkdir -p "$RELOC_DIR"
|
||||
mv "$INSTALL_DIR" "$RELOC_DIR/relocated-install"
|
||||
export VERILATOR_ROOT="$RELOC_DIR/relocated-install/share/verilator"
|
||||
TEST_REGRESS="$RELOC_DIR/test_regress"
|
||||
mv test_regress "$TEST_REGRESS"
|
||||
# Feeling brave?
|
||||
find . -delete
|
||||
ls -la .
|
||||
fi
|
||||
|
||||
# Run the specified test
|
||||
ccache -z
|
||||
case $TESTS in
|
||||
dist-vlt-0)
|
||||
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=0/3
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=0/4
|
||||
;;
|
||||
dist-vlt-1)
|
||||
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=1/3
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=1/4
|
||||
;;
|
||||
dist-vlt-2)
|
||||
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=2/3
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=2/4
|
||||
;;
|
||||
dist-vlt-3)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=3/4
|
||||
;;
|
||||
vltmt-0)
|
||||
"$MAKE" -C test_regress SCENARIOS=--vltmt DRIVER_HASHSET=--hashset=0/2
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS=--vltmt DRIVER_HASHSET=--hashset=0/2
|
||||
;;
|
||||
vltmt-1)
|
||||
"$MAKE" -C test_regress SCENARIOS=--vltmt DRIVER_HASHSET=--hashset=1/2
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS=--vltmt DRIVER_HASHSET=--hashset=1/2
|
||||
;;
|
||||
coverage-all)
|
||||
nodist/code_coverage --stages 1-
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
# Then 'make maintainer-dist'
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[5.022 2024-02-24],
|
||||
AC_INIT([Verilator],[5.024 2024-04-05],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ february cozzocrea
|
||||
Felix Neumärker
|
||||
Felix Yan
|
||||
Frans Skarman
|
||||
Fuad Ismail
|
||||
G-A. Kamendje
|
||||
Garrett Smith
|
||||
Geza Lore
|
||||
@@ -103,8 +104,10 @@ Justin Thiel
|
||||
Kaleb Barrett
|
||||
Kamil Rakoczy
|
||||
Kanad Kanhere
|
||||
Kefa Chen
|
||||
Keith Colbert
|
||||
Kevin Kiningham
|
||||
Kevin Nygaard
|
||||
Kritik Bhimani
|
||||
Krzysztof Bieganski
|
||||
Krzysztof Boronski
|
||||
@@ -135,6 +138,7 @@ Mostafa Gamal
|
||||
Nandu Raj
|
||||
Nathan Kohagen
|
||||
Nathan Myers
|
||||
Nolan Poe
|
||||
Oleh Maksymenko
|
||||
Patrick Stewart
|
||||
Paul Swirhun
|
||||
@@ -189,6 +193,7 @@ Vito Gamberini
|
||||
William D. Jones
|
||||
Wilson Snyder
|
||||
Xi Zhang
|
||||
Yan Xu
|
||||
Yinan Xu
|
||||
Yoda Lee
|
||||
Yossi Nivin
|
||||
|
||||
@@ -92,6 +92,11 @@ Summary:
|
||||
profile-guided optimization data runtime filename to dump to. Defaults
|
||||
to :file:`profile.vlt`.
|
||||
|
||||
.. option:: +verilator+quiet
|
||||
|
||||
Disable printing the simulation summary report, see :ref:`Simulation
|
||||
Summary Report`.
|
||||
|
||||
.. option:: +verilator+rand+reset+<value>
|
||||
|
||||
When a model was Verilated using
|
||||
|
||||
@@ -51,6 +51,8 @@ Summary:
|
||||
|
||||
.. option:: +1800-2017ext+<ext>
|
||||
|
||||
.. option:: +1800-2023ext+<ext>
|
||||
|
||||
Specifies the language standard to be used with a specific filename
|
||||
extension, <ext>.
|
||||
|
||||
@@ -199,9 +201,10 @@ Summary:
|
||||
|
||||
.. option:: --compiler <compiler-name>
|
||||
|
||||
Enables workarounds for the specified C++ compiler (list below).
|
||||
This does not change any performance tuning options, but it may
|
||||
in the future.
|
||||
Enables workarounds for the specified C++ compiler (list below). This
|
||||
does not change any performance tuning options, but it may in the
|
||||
future. This also does not change default compiler flags; these are
|
||||
determined when Verilator was configured.
|
||||
|
||||
clang
|
||||
Tune for clang. This may reduce execution speed as it enables several
|
||||
@@ -348,7 +351,7 @@ Summary:
|
||||
Select the language used by default when first processing each
|
||||
Verilog file. The language value must be "VAMS", "1364-1995",
|
||||
"1364-2001", "1364-2001-noconfig", "1364-2005", "1800-2005",
|
||||
"1800-2009", "1800-2012", "1800-2017", or "1800+VAMS".
|
||||
"1800-2009", "1800-2012", "1800-2017", "1800-2023", or "1800+VAMS".
|
||||
|
||||
Any language associated with a particular file extension (see the
|
||||
various +<lang>*\ ext+ options) will be used in preference to the
|
||||
@@ -361,7 +364,7 @@ Summary:
|
||||
``+<lang>ext+`` options should be used.
|
||||
|
||||
If no language is specified, either by this option or ``+<lang>ext+``
|
||||
options, then the latest SystemVerilog language (IEEE 1800-2017) is
|
||||
options, then the latest SystemVerilog language (IEEE 1800-2023) is
|
||||
used.
|
||||
|
||||
.. option:: +define+<var>=<value>
|
||||
@@ -410,6 +413,12 @@ Summary:
|
||||
:vlopt:`--debug --no-dump-tree <--dump-tree>` may be useful if the dump
|
||||
files are large and not desired.
|
||||
|
||||
.. option:: --dump-tree-json
|
||||
|
||||
Rarely needed. Enable dumping Ast .json.tree debug files with dumping level 3,
|
||||
which dumps the standard critical stages. For details on the format, see
|
||||
the Verilator Internals manual.
|
||||
|
||||
.. option:: --dump-tree-dot
|
||||
|
||||
Rarely needed. Enable dumping Ast .tree.dot debug files in Graphviz
|
||||
@@ -445,6 +454,11 @@ Summary:
|
||||
Rarely needed - for developer use. Set internal Ast dumping level
|
||||
globally to the specified value.
|
||||
|
||||
.. option:: --dumpi-tree-json <level>
|
||||
|
||||
Rarely needed - for developer use. Set internal Ast JSON dumping level
|
||||
globally to the specified value.
|
||||
|
||||
.. option:: --dumpi-<srcfile> <level>
|
||||
|
||||
Rarely needed - for developer use. Set the dumping level in the
|
||||
@@ -1160,11 +1174,20 @@ Summary:
|
||||
Overwrites the given parameter(s) of the top-level module. See
|
||||
:vlopt:`-G <-G<name>>` for a detailed description.
|
||||
|
||||
.. option:: --quiet
|
||||
|
||||
Alias for :vlopt:`--quiet-exit` :vlopt:`--quiet-stats`.
|
||||
|
||||
.. option:: --quiet-exit
|
||||
|
||||
When exiting due to an error, do not display the "Exiting due to Errors"
|
||||
nor "Command Failed" messages.
|
||||
|
||||
.. option:: --quiet-stats
|
||||
|
||||
Disable printing the Verilation statistics report, see :ref:`Verilation
|
||||
Summary Report`.
|
||||
|
||||
.. option:: --relative-includes
|
||||
|
||||
When a file references an include file, resolve the filename relative to
|
||||
@@ -1298,12 +1321,12 @@ Summary:
|
||||
.. option:: -sv
|
||||
|
||||
Specifies SystemVerilog language features should be enabled; equivalent
|
||||
to :vlopt:`--language 1800-2017 <--language>`. This option is selected
|
||||
to :vlopt:`--language 1800-2023 <--language>`. This option is selected
|
||||
by default; it exists for compatibility with other simulators.
|
||||
|
||||
.. option:: +systemverilogext+<ext>
|
||||
|
||||
A synonym for :vlopt:`+1800-2017ext+\<ext\>`.
|
||||
A synonym for :vlopt:`+1800-2023ext+\<ext\>`.
|
||||
|
||||
.. option:: --threads <threads>
|
||||
|
||||
@@ -1786,7 +1809,8 @@ Summary:
|
||||
format is still evolving; there will be some changes in future versions.
|
||||
|
||||
This option disables some more aggressive transformations and dumps only
|
||||
the final state of the AST.
|
||||
the final state of the AST. For more granular and unaltered dumps, meant
|
||||
mainly for debugging see :vlopt:`--dump-tree-json`.
|
||||
|
||||
.. option:: --json-only-meta-output <filename>
|
||||
|
||||
|
||||
@@ -36,8 +36,7 @@ or "`ifdef`"'s may break other tools.
|
||||
.. option:: """ [string] """
|
||||
|
||||
A triple-quoted block specifies a string that may include newlines and
|
||||
single quotes. This extension is experimental and may be removed
|
||||
without deprecation.
|
||||
single quotes. This extension was standardized in IEEE 1800-2023.
|
||||
|
||||
.. option:: $c([string], ...);
|
||||
|
||||
@@ -577,6 +576,10 @@ or "`ifdef`"'s may break other tools.
|
||||
|
||||
.. option:: /*verilator&32;trace_init_task*/
|
||||
|
||||
Removed.
|
||||
|
||||
In versions before 5.024:
|
||||
|
||||
Attached to a DPI import to indicate that function should be called when
|
||||
initializing tracing. This attribute is indented only to be used
|
||||
internally in code that Verilator generates when :vlopt:`--lib-create`
|
||||
@@ -619,4 +622,4 @@ or "`ifdef`"'s may break other tools.
|
||||
symbols. Also, the data represents the C++ stack; the SystemVerilog
|
||||
functions/tasks involved may be renamed and/or inlined before becoming
|
||||
the C++ functions that may be visible in the stack trace. This
|
||||
extension is experimental and may be removed without deprecation.
|
||||
extension was standardized in IEEE 1800-2023.
|
||||
|
||||
+7
-4
@@ -128,10 +128,13 @@ How do I generate waveforms (traces) in C++?
|
||||
|
||||
See also the next question for tracing in SystemC mode.
|
||||
|
||||
A. Pass the :vlopt:`--trace` option to Verilator, and in your top-level C
|
||||
code, call ``Verilated::traceEverOn(true)``. Then you may use
|
||||
``$dumpfile`` and ``$dumpvars`` to enable traces, the same as with any
|
||||
Verilog simulator. See ``examples/make_tracing_c`` in the distribution.
|
||||
A. Pass the :vlopt:`--trace` option to Verilator. Then you may use ``$dumpfile`` and
|
||||
``$dumpvars`` to enable traces, the same as with any Verilog simulator,
|
||||
although Verilator ignores the arguments to ``$dumpvars``. See
|
||||
``examples/make_tracing_c`` in the distribution.
|
||||
|
||||
If writing the top-level C code, call ``Verilated::traceEverOn(true)``;
|
||||
this is done for you if using :vlopt:`--binary`.
|
||||
|
||||
B. Or, for finer-grained control, or C++ files with multiple Verilated
|
||||
modules, you may also create the trace purely from C++. Create a
|
||||
|
||||
@@ -126,8 +126,9 @@ 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 bear
|
||||
sudo pip3 install clang sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe ruff
|
||||
sudo apt-get install python3-clang yapf3 bear jq
|
||||
sudo pip3 install sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe ruff
|
||||
sudo pip3 install git+https://github.com/antmicro/astsee.git
|
||||
cpan install Pod::Perldoc
|
||||
cpan install Parallel::Forker
|
||||
|
||||
|
||||
@@ -62,12 +62,23 @@ Verilator supports the 2017 "for" loop constructs and several cleanups IEEE
|
||||
made in 1800-2017.
|
||||
|
||||
|
||||
SystemVerilog 2023 (IEEE 1800-2023) Support
|
||||
-------------------------------------------
|
||||
|
||||
Verilator supports some of the 2023 improvements, including triple-quoted
|
||||
string blocks that may include newlines and single quotes.
|
||||
|
||||
Verilator implements a full IEEE 1800-2023 compliant preprocessor,
|
||||
including triple-quoted strings, and \`ifdef expressions.
|
||||
|
||||
|
||||
Verilog AMS Support
|
||||
-------------------
|
||||
|
||||
Verilator implements a very small subset of Verilog AMS (Verilog Analog and
|
||||
Mixed-Signal Extensions) with the subset corresponding to those VMS
|
||||
keywords with near-equivalents in Verilog 2005 or SystemVerilog 2017.
|
||||
keywords with near-equivalents in Verilog IEEE 1364 or SystemVerilog
|
||||
IEEE 1800.
|
||||
|
||||
AMS parsing is enabled with :vlopt:`--language VAMS <--language>` or
|
||||
:vlopt:`--language 1800+VAMS <--language>`.
|
||||
|
||||
@@ -50,6 +50,6 @@ The best place to get started is to try the :ref:`Examples`.
|
||||
.. [#] SystemVerilog is defined by the `Institute of Electrical and
|
||||
Electronics Engineers (IEEE) Standard for SystemVerilog - Unified
|
||||
Hardware Design, Specification, and Verification Language`, Standard
|
||||
1800, released in 2005, 2009, 2012, and 2017. The Verilator
|
||||
documentation uses the shorthand e.g., "IEEE 1800-2017", to refer to
|
||||
the, e.g., 2017 version of this standard.
|
||||
1800, released in 2005, 2009, 2012, 2017, and 2023. The Verilator
|
||||
documentation uses the shorthand e.g., "IEEE 1800-2023", to refer to
|
||||
the, e.g., 2023 version of this standard.
|
||||
|
||||
@@ -12,6 +12,60 @@ Verilated model's executable. For the runtime arguments to a simulated
|
||||
model, see :ref:`Simulation Runtime Arguments`.
|
||||
|
||||
|
||||
.. _Simulation Summary Report:
|
||||
|
||||
Simulation Summary Report
|
||||
=========================
|
||||
|
||||
When simulation finishes, it will print a report to stdout summarizing the
|
||||
simulation. This requires the model being Verilated with :vlopt:`--main`.
|
||||
The report may be disabled with :vlopt:`+verilator+quiet`.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block::
|
||||
|
||||
- S i m u l a t i o n R e p o r t: Verilator ...
|
||||
- Verilator: End at simtime 123 ns; walltime 1234.001 s; speed 123 ns/s
|
||||
- Verilator: cpu 22.001 s on 4 threads; alloced 123 MB
|
||||
|
||||
The information in this report is:
|
||||
|
||||
.. describe:: "Verilator ..."
|
||||
|
||||
Program version.
|
||||
|
||||
.. describe:: "End at simtime 123 ns"
|
||||
|
||||
Verilog $time at which the model finished or stopped.
|
||||
|
||||
.. describe:: "walltime 1234.001 s"
|
||||
|
||||
Real elapsed wall time in seconds.
|
||||
|
||||
.. describe:: "speed 123.1 ns/s"
|
||||
|
||||
Simulated time (if non-zero) divided by wall time. e.g. `123 ns/s` means
|
||||
123 simulated nanoseconds took 1 second of wall time; for a model with
|
||||
only a 1 GHz clock that would be equivalent to 123.1 cycles per
|
||||
second. The units are automatically selected to give a number between 1
|
||||
and 1000. The wall time includes initialization, initial and final
|
||||
process blocks, so indicates a slower speed than if the model had a
|
||||
longer runtime.
|
||||
|
||||
.. describe:: "cpu 22 s"
|
||||
|
||||
CPU time used total across all CPU threads in seconds.
|
||||
|
||||
.. describe:: "4 threads"
|
||||
|
||||
Number of simultaneous threads used.
|
||||
|
||||
.. describe:: "alloced 123 MB"
|
||||
|
||||
Total memory used during simulation in megabytes.
|
||||
|
||||
|
||||
.. _Benchmarking & Optimization:
|
||||
|
||||
Benchmarking & Optimization
|
||||
|
||||
@@ -498,3 +498,78 @@ The search paths can be configured by setting some variables:
|
||||
|
||||
Sets the installation prefix of an installed SystemC library. (Same as
|
||||
SYSTEMC_ROOT).
|
||||
|
||||
|
||||
.. _Verilation Summary Report:
|
||||
|
||||
Verilation Summary Report
|
||||
=========================
|
||||
|
||||
When Verilator generates code, unless :vlopt:`--quiet-stats` is used, it
|
||||
will print a report to stdout summarizing the build. For example:
|
||||
|
||||
.. code-block::
|
||||
|
||||
- V e r i l a t i o n R e p o r t: Verilator ....
|
||||
- Verilator: Built from 354 MB sources in 247 modules,
|
||||
into 74 MB in 89 C++ files needing 0.192 MB
|
||||
- Verilator: Walltime 26.580 s (elab=2.096, cvt=18.268,
|
||||
bld=2.100); cpu 26.548 s on 1 threads; alloced 2894.672 MB
|
||||
|
||||
The information in this report is:
|
||||
|
||||
.. describe:: "Verilator ..."
|
||||
|
||||
Program version.
|
||||
|
||||
.. describe:: "234 MB sources"
|
||||
|
||||
Characters of post-preprocessed text in all input
|
||||
Verilog and Verilator Control files in megabytes.
|
||||
|
||||
.. describe:: "247 modules"
|
||||
|
||||
Number of interfaces/modules/classes/packages in design before
|
||||
elaboration.
|
||||
|
||||
.. describe:: "into 74 MB"
|
||||
|
||||
Characters of output C++ code, including comments in megabytes.
|
||||
|
||||
.. describe:: "89 C++ files"
|
||||
|
||||
Number of .cpp files created.
|
||||
|
||||
.. describe:: "needing 192MB"
|
||||
|
||||
Verilation-time minimum-bound estimate of memory needed to run model in
|
||||
megabytes. (Expect to need significantly more.)
|
||||
|
||||
.. describe:: "Walltime 26.580 s"
|
||||
|
||||
Real elapsed wall time for Verilation and build.
|
||||
|
||||
.. describe:: "elab=2.096"
|
||||
|
||||
Wall time to read in files and complete elaboration.
|
||||
|
||||
.. describe:: "cvt=18.268"
|
||||
|
||||
Wall time for Verilator to process and write output.
|
||||
|
||||
.. describe:: "bld=2.1"
|
||||
|
||||
Wall time to compile gcc/clang (if using :vlopt:`--build`).
|
||||
|
||||
.. describe:: "cpu 22.548 s"
|
||||
|
||||
CPU time used, total across all CPU threads.
|
||||
|
||||
.. describe:: "4 threads"
|
||||
|
||||
Number of simultaneous threads used.
|
||||
|
||||
.. describe:: "alloced 123 MB"
|
||||
|
||||
Total memory used during build by Verilator executable (excludes
|
||||
:vlopt:`--build` compiler's usage) in megabytes.
|
||||
|
||||
+39
-14
@@ -161,8 +161,8 @@ List Of Warnings
|
||||
.. option:: BADSTDPRAGMA
|
||||
|
||||
An error that a pragma is badly formed, for pragmas defined by IEEE
|
||||
1800-2017. For example, an empty pragma line, or an incorrectly used
|
||||
'pragma protect'. Third-party pragmas not defined by IEEE 1800-2017 are
|
||||
1800-2023. For example, an empty pragma line, or an incorrectly used
|
||||
'pragma protect'. Third-party pragmas not defined by IEEE 1800-2023 are
|
||||
ignored.
|
||||
|
||||
|
||||
@@ -283,7 +283,7 @@ List Of Warnings
|
||||
Unique case statements that select on an enumerated variable, where all
|
||||
of the enumerated values are covered by case items, are considered
|
||||
complete even if the case statement does not cover illegal
|
||||
non-enumerated values (IEEE 1800-2017 12.5.3). To check that illegal
|
||||
non-enumerated values (IEEE 1800-2023 12.5.3). To check that illegal
|
||||
values are not hit, use :vlopt:`--assert`.
|
||||
|
||||
Ignoring this warning will only suppress the lint check; it will
|
||||
@@ -479,7 +479,7 @@ List Of Warnings
|
||||
|
||||
.. code-block::
|
||||
|
||||
%Warning-DEFPARAM: example.v:5:15: defparam is deprecated (IEEE 1800-2017 C.4.1)
|
||||
%Warning-DEFPARAM: example.v:5:15: defparam is deprecated (IEEE 1800-2023 C.4.1)
|
||||
: ... Suggest use instantiation with #(.MY_PARAM(...etc...))
|
||||
|
||||
To repair use :code:`#(.PARAMETER(...))` syntax. Repaired Example:
|
||||
@@ -707,7 +707,7 @@ List Of Warnings
|
||||
|
||||
.. code-block::
|
||||
|
||||
%Warning-GENUNNAMED: example.v:2:9: Unnamed generate block (IEEE 1800-2017 27.6)
|
||||
%Warning-GENUNNAMED: example.v:2:9: Unnamed generate block (IEEE 1800-2023 27.6)
|
||||
|
||||
To fix this assign a label (often with the naming convention prefix of
|
||||
:code:`gen_` or :code:`g_`), for example:
|
||||
@@ -763,7 +763,7 @@ List Of Warnings
|
||||
|
||||
.. code-block::
|
||||
|
||||
%Warning-IGNOREDRETURN: example.v:5:9: Ignoring return value of non-void function (IEEE 1800-2017 13.4.1)
|
||||
%Warning-IGNOREDRETURN: example.v:5:9: Ignoring return value of non-void function (IEEE 1800-2023 13.4.1)
|
||||
|
||||
The portable way to suppress this warning (in SystemVerilog) is to use a
|
||||
void cast, for example:
|
||||
@@ -1328,10 +1328,35 @@ List Of Warnings
|
||||
.. TODO better example
|
||||
|
||||
An error that a package/class appears to have been referenced that has not
|
||||
yet been declared. According to IEEE 1800-2017 26.3, all packages must
|
||||
yet been declared. According to IEEE 1800-2023 26.3, all packages must
|
||||
be declared before being used.
|
||||
|
||||
|
||||
.. option:: PREPROCZERO
|
||||
|
||||
Warns that a preprocessor \`ifdef/\`ifndef expression (added in IEEE
|
||||
1800-2023) evaluates a define value which has a value of :code:`0`.
|
||||
This will evaluate in the expression as :code:`1` because the define has
|
||||
a definition, unlike in the C preprocessor, which evaluates using the
|
||||
define's value (of :code:`1`).
|
||||
|
||||
Referring to a define with an empty value does not give this warning, as
|
||||
in C, the preprocessor will give an error on a preprocessor expression
|
||||
of a define that is empty.
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 2
|
||||
|
||||
`define ZERO 0
|
||||
`ifdef (ZERO || ZERO) //<--- warning PREPROCZERO
|
||||
`error This_will_error_which_might_be_not_the_intent
|
||||
`endif
|
||||
|
||||
The portable way to suppress this warning is to use a define value other
|
||||
than zero to when used in a preprocessor expression.
|
||||
|
||||
|
||||
.. option:: PROCASSWIRE
|
||||
|
||||
.. TODO better example
|
||||
@@ -1707,7 +1732,7 @@ List Of Warnings
|
||||
|
||||
.. code-block::
|
||||
|
||||
%Warning-TIMESCALEMOD: example.v:1:8: Timescale missing on this module as other modules have it (IEEE 1800-2017 3.14.2.3)
|
||||
%Warning-TIMESCALEMOD: example.v:1:8: Timescale missing on this module as other modules have it (IEEE 1800-2023 3.14.2.3)
|
||||
|
||||
Recommend using :vlopt:`--timescale` argument, or in front of all
|
||||
modules use:
|
||||
@@ -1949,7 +1974,7 @@ List Of Warnings
|
||||
.. option:: USERERROR
|
||||
|
||||
A SystemVerilog elaboration-time assertion error was executed.
|
||||
IEEE 1800-2017 20.11 requires this error.
|
||||
IEEE 1800-2023 20.11 requires this error.
|
||||
|
||||
Faulty example:
|
||||
|
||||
@@ -1965,7 +1990,7 @@ List Of Warnings
|
||||
.. option:: USERFATAL
|
||||
|
||||
A SystemVerilog elaboration-time assertion fatal was executed.
|
||||
IEEE 1800-2017 20.11 requires this error.
|
||||
IEEE 1800-2023 20.11 requires this error.
|
||||
|
||||
Faulty example:
|
||||
|
||||
@@ -1981,7 +2006,7 @@ List Of Warnings
|
||||
.. option:: USERINFO
|
||||
|
||||
A SystemVerilog elaboration-time assertion print was executed. This is
|
||||
not an error or warning, and IEEE 1800-2017 20.11 requires this
|
||||
not an error or warning, and IEEE 1800-2023 20.11 requires this
|
||||
behavior.
|
||||
|
||||
Example:
|
||||
@@ -1996,7 +2021,7 @@ List Of Warnings
|
||||
.. option:: USERWARN
|
||||
|
||||
A SystemVerilog elaboration-time assertion warning was executed.
|
||||
IEEE 1800-2017 20.11 requires this warning.
|
||||
IEEE 1800-2023 20.11 requires this warning.
|
||||
|
||||
Faulty example:
|
||||
|
||||
@@ -2135,7 +2160,7 @@ List Of Warnings
|
||||
.. option:: ZEROREPL
|
||||
|
||||
Warns that zero is used as the replication value in the replication
|
||||
operator. This is specified as an error by IEEE 1800-2017 11.4.12.1.
|
||||
operator. This is specified as an error by IEEE 1800-2023 11.4.12.1.
|
||||
|
||||
Faulty example:
|
||||
|
||||
@@ -2155,7 +2180,7 @@ List Of Warnings
|
||||
|
||||
.. code-block::
|
||||
|
||||
%Error-ZEROREPL: test.v:5:22: Replication value of 0 is only legal under a concatenation (IEEE 1800-2017 11.4.12.1)
|
||||
%Error-ZEROREPL: test.v:5:22: Replication value of 0 is only legal under a concatenation (IEEE 1800-2023 11.4.12.1)
|
||||
|
||||
Note that in some cases, this warning may be false, when a condition
|
||||
upstream or downstream of the access means the zero replication will
|
||||
|
||||
+37
-19
@@ -224,7 +224,7 @@ Scheduling
|
||||
----------
|
||||
|
||||
Verilator implements the Active and NBA regions of the SystemVerilog scheduling
|
||||
model as described in IEEE 1800-2017 chapter 4, and in particular sections
|
||||
model as described in IEEE 1800-2023 chapter 4, and in particular sections
|
||||
4.5 and Figure 4.1. The static (Verilation time) scheduling of SystemVerilog
|
||||
processes is performed by code in the ``V3Sched`` namespace. The single
|
||||
entry point to the scheduling algorithm is ``V3Sched::schedule``. Some
|
||||
@@ -312,7 +312,7 @@ Scheduling of clocked and combinational logic
|
||||
For performance, clocked and combinational logic needs to be ordered.
|
||||
Conceptually this minimizes the iterations through the evaluation loop
|
||||
presented in the reference algorithm in the SystemVerilog standard (IEEE
|
||||
1800-2017 section 4.5), by evaluating logic constructs in data-flow order.
|
||||
1800-2023 section 4.5), by evaluating logic constructs in data-flow order.
|
||||
Without going into a lot of detail here, accept that well thought out ordering
|
||||
is crucial to good simulation performance, and also enables further
|
||||
optimizations later on.
|
||||
@@ -407,7 +407,7 @@ triggered due to a combinational signal change from the previous evaluation
|
||||
pass, and a combinational loop settling due to hybrid logic, if the clocked
|
||||
logic reads the not yet settled combinationally driven signal. Such a race
|
||||
is indeed possible, but our evaluation is consistent with the SystemVerilog
|
||||
scheduling semantics (IEEE 1800-2017 chapter 4), and therefore any program
|
||||
scheduling semantics (IEEE 1800-2023 chapter 4), and therefore any program
|
||||
that exhibits such a race has non-deterministic behavior according to the
|
||||
SystemVerilog semantics, so we accept this.
|
||||
|
||||
@@ -1701,7 +1701,7 @@ Similarly, the ``NETLIST`` has a list of modules referred to by its
|
||||
|
||||
``.tree.json``` is an alternative dump format to ``.tree`` that is meant for
|
||||
programmatic processing (e.g. with `astsee <https://github.com/antmicro/astsee>`_).
|
||||
To enable this dump format, use :vlopt:`--json-only`.
|
||||
To enable this dump format, use :vlopt:`--dump-tree-json` or :vlopt:`--json-only`.
|
||||
|
||||
Structure:
|
||||
::
|
||||
@@ -1743,8 +1743,8 @@ Structure:
|
||||
|
||||
.tree.meta.json Output
|
||||
----------------
|
||||
|
||||
.tree.meta.json contains metadata that is common across the whole AST tree.
|
||||
.tree.meta.json contains metadata that is common across the whole AST tree
|
||||
(in case of --dump-tree-json, multiple trees share one meta file).
|
||||
|
||||
Besides de-duplication of data shared between multiple stages, .meta.json enables offloading
|
||||
unstable data (that can vary from machine-to-machine or run-to-run) from main .tree.json.
|
||||
@@ -1754,11 +1754,11 @@ This offloading allows, for example, to use byte-to-byte comparisons of AST dump
|
||||
|
||||
{"files": {
|
||||
/* Map id to filename, and other metadata */
|
||||
"d": {"filename":"/home/ant/tmp/verilator/include/verilated_std.sv", "realpath":"/home/ant/tmp/verilator/include/verilated_std.sv", "language":"1800-2017"},
|
||||
"a": {"filename":"<built-in>", "realpath":"<built-in>", "language":"1800-2017"},
|
||||
"b": {"filename":"<command-line>", "realpath":"<command-line>", "language":"1800-2017"},
|
||||
"c": {"filename":"input.vc", "realpath":"/home/ant/tmp/verilator/test_regress/input.vc", "language":"1800-2017"},
|
||||
"e": {"filename":"t/t_EXAMPLE.v", "realpath":"/home/ant/tmp/verilator/test_regress/t/t_EXAMPLE.v", "language":"1800-2017"}
|
||||
"d": {"filename":"/home/ant/tmp/verilator/include/verilated_std.sv", "realpath":"/home/ant/tmp/verilator/include/verilated_std.sv", "language":"1800-2023"},
|
||||
"a": {"filename":"<built-in>", "realpath":"<built-in>", "language":"1800-2023"},
|
||||
"b": {"filename":"<command-line>", "realpath":"<command-line>", "language":"1800-2023"},
|
||||
"c": {"filename":"input.vc", "realpath":"/home/ant/tmp/verilator/test_regress/input.vc", "language":"1800-2023"},
|
||||
"e": {"filename":"t/t_EXAMPLE.v", "realpath":"/home/ant/tmp/verilator/test_regress/t/t_EXAMPLE.v", "language":"1800-2023"}
|
||||
},"pointers": {
|
||||
/* Map id to real address */
|
||||
"(AG)": "0x562997289180",
|
||||
@@ -1846,6 +1846,20 @@ To print a node:
|
||||
pnt nodep
|
||||
# or: call dumpTreeGdb(nodep) # aliased to "pnt" in src/.gdbinit
|
||||
|
||||
``src/.gdbinit`` and ``src/.gdbinit.py`` define handy utilities for working with
|
||||
JSON AST dumps. For example:
|
||||
|
||||
* ``jstash nodep`` - Perform a JSON AST dump and save it into GDB value history (e.g. ``$1``)
|
||||
* ``jtree nodep`` - Perform a JSON AST dump and pretty print it using ``astsee_verilator``.
|
||||
* ``jtree $1`` - Pretty print a dump that was previously saved by ``jstash``.
|
||||
* ``jtree nodep -d '.file, .timeunit'`` - Perform a JSON AST dump, filter out some fields and pretty print it.
|
||||
* ``jtree 0x55555613dca0`` - Pretty print using address literal (rather than actual pointer).
|
||||
* ``jtree $1 nodep`` - Diff ``nodep`` against an older dump.
|
||||
|
||||
A detailed description of ``jstash`` and ``jtree`` can be displayed using ``gdb``'s ``help`` command.
|
||||
|
||||
These commands require `astsee <https://github.com/antmicro/astsee>`_ to be installed.
|
||||
|
||||
When GDB halts, it is useful to understand that the backtrace will commonly
|
||||
show the iterator functions between each invocation of ``visit`` in the
|
||||
backtrace. You will typically see a frame sequence something like:
|
||||
@@ -1907,21 +1921,21 @@ Verilator ideally would support all of IEEE, and has the goal to get close
|
||||
to full support. However the following IEEE sections and features are not
|
||||
anticipated to be ever implemented for the reasons indicated.
|
||||
|
||||
IEEE 1800-2017 3.3 modules within modules
|
||||
IEEE 1800-2023 3.3 modules within modules
|
||||
Little/no tool support, and arguably not a good practice.
|
||||
IEEE 1800-2017 6.12 "shortreal"
|
||||
IEEE 1800-2023 6.12 "shortreal"
|
||||
Little/no tool support, and easily promoted to real.
|
||||
IEEE 1800-2017 11.11 Min, typ, max
|
||||
IEEE 1800-2023 11.11 Min, typ, max
|
||||
No SDF support, so will always use typical.
|
||||
IEEE 1800-2017 20.16 Stochastic analysis
|
||||
IEEE 1800-2023 20.16 Stochastic analysis
|
||||
Little industry use.
|
||||
IEEE 1800-2017 20.17 PLA modeling
|
||||
IEEE 1800-2023 20.17 PLA modeling
|
||||
Little industry use and outdated technology.
|
||||
IEEE 1800-2017 31 Timing checks
|
||||
IEEE 1800-2023 31 Timing checks
|
||||
No longer relevant with static timing analysis tools.
|
||||
IEEE 1800-2017 32 SDF annotation
|
||||
IEEE 1800-2023 32 SDF annotation
|
||||
No longer relevant with static timing analysis tools.
|
||||
IEEE 1800-2017 33 Config
|
||||
IEEE 1800-2023 33 Config
|
||||
Little industry use.
|
||||
|
||||
|
||||
@@ -1962,6 +1976,10 @@ driver.pl Non-Scenario Arguments
|
||||
files with dumping level 3, which dumps the standard critical stages.
|
||||
For details on the format see `.tree Output`.
|
||||
|
||||
--fail-max <numtests>
|
||||
Set the number of failing tests, after which the driver will stop running
|
||||
additional tests. Defaults to 20, 0 disables.
|
||||
|
||||
--gdb
|
||||
Same as ``verilator --gdb``: Run Verilator under the debugger.
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ Deadman
|
||||
Debacker
|
||||
Deepa
|
||||
Defparams
|
||||
Delm
|
||||
Denio
|
||||
Deprecations
|
||||
Deroo
|
||||
@@ -146,6 +147,7 @@ Grulfen
|
||||
Gu
|
||||
Gunter
|
||||
Guo
|
||||
Hameed
|
||||
Hao
|
||||
Haojin
|
||||
Harboe
|
||||
@@ -184,6 +186,7 @@ Jiaxun
|
||||
Jiuyang
|
||||
Joannou
|
||||
Joly
|
||||
Josse
|
||||
Jullien
|
||||
Junji
|
||||
Junyi
|
||||
@@ -438,6 +441,7 @@ Verilog
|
||||
Vighnesh
|
||||
Viktor
|
||||
Vilp
|
||||
Vlip
|
||||
Vm
|
||||
Vukobratovic
|
||||
Wai
|
||||
@@ -461,6 +465,7 @@ Xiaoliang
|
||||
Xiaoyi
|
||||
Xuan
|
||||
Xuanqi
|
||||
Yao
|
||||
Yazdanbakhsh
|
||||
Yernagula
|
||||
Yi
|
||||
@@ -639,6 +644,7 @@ envvar
|
||||
eof
|
||||
errae
|
||||
erroring
|
||||
esynr
|
||||
et
|
||||
eval
|
||||
evals
|
||||
@@ -670,6 +676,7 @@ foreach
|
||||
fprintf
|
||||
fprofile
|
||||
fread
|
||||
freloop
|
||||
frewind
|
||||
fs
|
||||
fscanf
|
||||
@@ -965,8 +972,11 @@ sv
|
||||
svBitVal
|
||||
svBitVecVal
|
||||
svGet
|
||||
svGetTime
|
||||
svGetTimePrecision
|
||||
svLogicVal
|
||||
svdpi
|
||||
svgGetTimeUnit
|
||||
swrite
|
||||
sys
|
||||
systemc
|
||||
|
||||
@@ -30,6 +30,7 @@ endif()
|
||||
|
||||
# Create a new executable target that will contain all your sources
|
||||
add_executable(example ../make_hello_c/sim_main.cpp)
|
||||
target_compile_features(example PUBLIC cxx_std_14)
|
||||
|
||||
# Add the Verilated circuit to the target
|
||||
verilate(example
|
||||
|
||||
@@ -31,7 +31,7 @@ endif
|
||||
ifeq ($(shell which cmake),)
|
||||
TARGET := nocmake
|
||||
else
|
||||
CMAKE_VERSION := $(shell cmake --version | grep -Po '(\d[\.\d]+)')
|
||||
CMAKE_VERSION := $(shell cmake --version | grep -o '[0-9][.0-9]\+')
|
||||
CMAKE_MAJOR := $(shell echo $(CMAKE_VERSION) | cut -f1 -d.)
|
||||
CMAKE_MINOR := $(shell echo $(CMAKE_VERSION) | cut -f2 -d.)
|
||||
CMAKE_GT_3_8 := $(shell [ $(CMAKE_MAJOR) -gt 3 -o \( $(CMAKE_MAJOR) -eq 3 -a $(CMAKE_MINOR) -ge 8 \) ] && echo true)
|
||||
|
||||
@@ -37,6 +37,7 @@ find_package(SystemCLanguage QUIET)
|
||||
|
||||
# Create a new executable target that will contain all your sources
|
||||
add_executable(example ../make_hello_sc/sc_main.cpp)
|
||||
target_compile_features(example PUBLIC cxx_std_14)
|
||||
|
||||
set_property(
|
||||
TARGET example
|
||||
|
||||
@@ -31,7 +31,7 @@ endif
|
||||
ifeq ($(shell which cmake),)
|
||||
TARGET := nocmake
|
||||
else
|
||||
CMAKE_VERSION := $(shell cmake --version | grep -Po '(\d[\.\d]+)')
|
||||
CMAKE_VERSION := $(shell cmake --version | grep -o '[0-9][.0-9]\+')
|
||||
CMAKE_MAJOR := $(shell echo $(CMAKE_VERSION) | cut -f1 -d.)
|
||||
CMAKE_MINOR := $(shell echo $(CMAKE_VERSION) | cut -f2 -d.)
|
||||
CMAKE_GT_3_8 := $(shell [ $(CMAKE_MAJOR) -gt 3 -o \( $(CMAKE_MAJOR) -eq 3 -a $(CMAKE_MINOR) -ge 8 \) ] && echo true)
|
||||
|
||||
@@ -30,9 +30,12 @@ endif()
|
||||
|
||||
# Create the main executable target
|
||||
add_executable(example ../make_protect_lib/sim_main.cpp)
|
||||
target_compile_features(example PUBLIC cxx_std_14)
|
||||
|
||||
# Create a secret library
|
||||
add_library(verilated_secret STATIC) # or SHARED for a shared library
|
||||
target_compile_features(verilated_secret PUBLIC cxx_std_14)
|
||||
|
||||
target_link_libraries(example PRIVATE verilated_secret)
|
||||
# To create both libraries on CMake >= 3.12 replace the above 2 lines with the following:
|
||||
# add_library(verilated_secret OBJECT)
|
||||
|
||||
@@ -31,7 +31,7 @@ endif
|
||||
ifeq ($(shell which cmake),)
|
||||
TARGET := nocmake
|
||||
else
|
||||
CMAKE_VERSION := $(shell cmake --version | grep -Po '(\d[\.\d]+)')
|
||||
CMAKE_VERSION := $(shell cmake --version | grep -o '[0-9][.0-9]\+')
|
||||
CMAKE_MAJOR := $(shell echo $(CMAKE_VERSION) | cut -f1 -d.)
|
||||
CMAKE_MINOR := $(shell echo $(CMAKE_VERSION) | cut -f2 -d.)
|
||||
CMAKE_GT_3_8 := $(shell [ $(CMAKE_MAJOR) -gt 3 -o \( $(CMAKE_MAJOR) -eq 3 -a $(CMAKE_MINOR) -ge 8 \) ] && echo true)
|
||||
|
||||
@@ -30,6 +30,7 @@ endif()
|
||||
|
||||
# Create a new executable target that will contain all your sources
|
||||
add_executable(example ../make_tracing_c/sim_main.cpp)
|
||||
target_compile_features(example PUBLIC cxx_std_14)
|
||||
|
||||
# Add the Verilated circuit to the target
|
||||
verilate(example COVERAGE TRACE
|
||||
|
||||
@@ -31,7 +31,7 @@ endif
|
||||
ifeq ($(shell which cmake),)
|
||||
TARGET := nocmake
|
||||
else
|
||||
CMAKE_VERSION := $(shell cmake --version | grep -Po '(\d[\.\d]+)')
|
||||
CMAKE_VERSION := $(shell cmake --version | grep -o '[0-9][.0-9]\+')
|
||||
CMAKE_MAJOR := $(shell echo $(CMAKE_VERSION) | cut -f1 -d.)
|
||||
CMAKE_MINOR := $(shell echo $(CMAKE_VERSION) | cut -f2 -d.)
|
||||
CMAKE_GT_3_8 := $(shell [ $(CMAKE_MAJOR) -gt 3 -o \( $(CMAKE_MAJOR) -eq 3 -a $(CMAKE_MINOR) -ge 8 \) ] && echo true)
|
||||
|
||||
@@ -38,6 +38,7 @@ find_package(SystemCLanguage QUIET)
|
||||
|
||||
# Create a new executable target that will contain all your sources
|
||||
add_executable(example ../make_tracing_sc/sc_main.cpp)
|
||||
target_compile_features(example PUBLIC cxx_std_14)
|
||||
|
||||
set_property(
|
||||
TARGET example
|
||||
|
||||
@@ -31,7 +31,7 @@ endif
|
||||
ifeq ($(shell which cmake),)
|
||||
TARGET := nocmake
|
||||
else
|
||||
CMAKE_VERSION := $(shell cmake --version | grep -Po '(\d[\.\d]+)')
|
||||
CMAKE_VERSION := $(shell cmake --version | grep -o '[0-9][.0-9]\+')
|
||||
CMAKE_MAJOR := $(shell echo $(CMAKE_VERSION) | cut -f1 -d.)
|
||||
CMAKE_MINOR := $(shell echo $(CMAKE_VERSION) | cut -f2 -d.)
|
||||
CMAKE_GT_3_8 := $(shell [ $(CMAKE_MAJOR) -gt 3 -o \( $(CMAKE_MAJOR) -eq 3 -a $(CMAKE_MINOR) -ge 8 \) ] && echo true)
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
#endif
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (false && argc && argv) {}
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
// Construct context to hold simulation time, etc
|
||||
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
|
||||
|
||||
@@ -117,6 +117,9 @@ int main(int argc, char** argv) {
|
||||
contextp->coveragep()->write("logs/coverage.dat");
|
||||
#endif
|
||||
|
||||
// Final simulation summary
|
||||
contextp->statsPrintSummary();
|
||||
|
||||
// Return good completion status
|
||||
// Don't use exit() or destructor won't get called
|
||||
return 0;
|
||||
|
||||
@@ -1646,7 +1646,11 @@ if(1)
|
||||
{
|
||||
if(vm4ip[2] != prev_alias)
|
||||
{
|
||||
fpos += fstWriterSVarint(f, (((int64_t)((int32_t)(prev_alias = vm4ip[2]))) << 1) | 1);
|
||||
int32_t t_i32 = ((int32_t)(prev_alias = vm4ip[2])); /* vm4ip is generic unsigned data */
|
||||
int64_t t_i64 = (int64_t)t_i32; /* convert to signed */
|
||||
uint64_t t_u64 = (uint64_t)t_i64; /* sign extend through 64b */
|
||||
|
||||
fpos += fstWriterSVarint(f, (int64_t)((t_u64 << 1) | 1)); /* all in this block was: fpos += fstWriterSVarint(f, (((int64_t)((int32_t)(prev_alias = vm4ip[2]))) << 1) | 1); */
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+123
-20
@@ -169,9 +169,12 @@ void vl_stop_maybe(const char* filename, int linenum, const char* hier, bool may
|
||||
Verilated::threadContextp()->errorCountInc();
|
||||
if (maybe
|
||||
&& Verilated::threadContextp()->errorCount() < Verilated::threadContextp()->errorLimit()) {
|
||||
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
|
||||
"-Info: %s:%d: %s\n", filename, linenum,
|
||||
"Verilog $stop, ignored due to +verilator+error+limit");
|
||||
// Do just once when cross error limit
|
||||
if (Verilated::threadContextp()->errorCount() == 1) {
|
||||
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
|
||||
"-Info: %s:%d: %s\n", filename, linenum,
|
||||
"Verilog $stop, ignored due to +verilator+error+limit");
|
||||
}
|
||||
} else {
|
||||
vl_stop(filename, linenum, hier);
|
||||
}
|
||||
@@ -602,7 +605,7 @@ WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, const WDataInP
|
||||
const int words = VL_WORDS_I(obits);
|
||||
VL_ZERO_W(obits, owp);
|
||||
EData lor = 0; // 0=all zeros, ~0=all ones, else mix
|
||||
for (int i = 1; i < (words - 1); ++i) { lor |= lwp[i]; }
|
||||
for (int i = 1; i < (words - 1); ++i) lor |= lwp[i];
|
||||
lor |= ((lwp[words - 1] == VL_MASK_E(rbits)) ? ~VL_EUL(0) : 0);
|
||||
if (lor == 0 && lwp[0] == 0) { // "X" so return 0
|
||||
return owp;
|
||||
@@ -814,7 +817,7 @@ void _vl_vsformat(std::string& output, const std::string& format, va_list ap) VL
|
||||
bool widthSet = false;
|
||||
bool left = false;
|
||||
size_t width = 0;
|
||||
for (std::string::const_iterator pos = format.begin(); pos != format.end(); ++pos) {
|
||||
for (std::string::const_iterator pos = format.cbegin(); pos != format.cend(); ++pos) {
|
||||
if (!inPct && pos[0] == '%') {
|
||||
pctit = pos;
|
||||
inPct = true;
|
||||
@@ -883,7 +886,7 @@ void _vl_vsformat(std::string& output, const std::string& format, va_list ap) VL
|
||||
case '^': { // Realtime
|
||||
const int lbits = va_arg(ap, int);
|
||||
const double d = va_arg(ap, double);
|
||||
if (lbits) {} // UNUSED - always 64
|
||||
(void)lbits; // UNUSED - always 64
|
||||
if (fmt == '^') { // Realtime
|
||||
if (!widthSet) width = Verilated::threadContextp()->impp()->timeFormatWidth();
|
||||
const int timeunit = va_arg(ap, int);
|
||||
@@ -1212,15 +1215,15 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
|
||||
IData got = 0;
|
||||
bool inPct = false;
|
||||
bool inIgnore = false;
|
||||
std::string::const_iterator pos = format.begin();
|
||||
for (; pos != format.end() && !_vl_vsss_eof(fp, floc); ++pos) {
|
||||
std::string::const_iterator pos = format.cbegin();
|
||||
for (; pos != format.cend() && !_vl_vsss_eof(fp, floc); ++pos) {
|
||||
// VL_DBG_MSGF("_vlscan fmt='"<<pos[0]<<"' floc="<<floc<<" file='"<<_vl_vsss_peek(fp, floc,
|
||||
// fromp, fstr)<<"'\n");
|
||||
if (!inPct && pos[0] == '%') {
|
||||
inPct = true;
|
||||
inIgnore = false;
|
||||
} else if (!inPct && std::isspace(pos[0])) { // Format spaces
|
||||
while (std::isspace(pos[1])) pos++;
|
||||
while (std::isspace(pos[1])) ++pos;
|
||||
_vl_vsss_skipspace(fp, floc, fromp, fstr);
|
||||
} else if (!inPct) { // Expected Format
|
||||
_vl_vsss_skipspace(fp, floc, fromp, fstr);
|
||||
@@ -1436,8 +1439,8 @@ void _vl_string_to_vint(int obits, void* destp, size_t srclen, const char* srcp)
|
||||
char* op = reinterpret_cast<char*>(destp);
|
||||
if (srclen > bytes) srclen = bytes; // Don't overflow destination
|
||||
size_t i = 0;
|
||||
for (i = 0; i < srclen; ++i) { *op++ = srcp[srclen - 1 - i]; }
|
||||
for (; i < bytes; ++i) { *op++ = 0; }
|
||||
for (i = 0; i < srclen; ++i) *op++ = srcp[srclen - 1 - i];
|
||||
for (; i < bytes; ++i) *op++ = 0;
|
||||
}
|
||||
|
||||
static IData getLine(std::string& str, IData fpi, size_t maxLen) VL_MT_SAFE {
|
||||
@@ -1652,7 +1655,8 @@ IData VL_SSCANF_INNX(int, const std::string& ld, const std::string& format, int
|
||||
...) VL_MT_SAFE {
|
||||
va_list ap;
|
||||
va_start(ap, argc);
|
||||
const IData got = _vl_vsscanf(nullptr, ld.length() * 8, nullptr, ld, format, ap);
|
||||
const IData got
|
||||
= _vl_vsscanf(nullptr, static_cast<int>(ld.length() * 8), nullptr, ld, format, ap);
|
||||
va_end(ap);
|
||||
return got;
|
||||
}
|
||||
@@ -1941,7 +1945,7 @@ std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_PURE {
|
||||
|
||||
IData VL_ATOI_N(const std::string& str, int base) VL_PURE {
|
||||
std::string str_mod = str;
|
||||
// IEEE 1800-2017 6.16.9 says '_' may exist.
|
||||
// IEEE 1800-2023 6.16.9 says '_' may exist.
|
||||
str_mod.erase(std::remove(str_mod.begin(), str_mod.end(), '_'), str_mod.end());
|
||||
|
||||
errno = 0;
|
||||
@@ -2106,10 +2110,11 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) {
|
||||
|
||||
if (VL_UNLIKELY(m_end != ~0ULL && m_addr <= m_end && !m_anyAddr)) {
|
||||
VL_WARN_MT(m_filename.c_str(), m_linenum, "",
|
||||
"$readmem file ended before specified final address (IEEE 1800-2017 21.4)");
|
||||
"$readmem file ended before specified final address (IEEE 1800-2023 21.4)");
|
||||
}
|
||||
|
||||
return false; // EOF
|
||||
addrr = m_addr;
|
||||
return indata; // EOF
|
||||
}
|
||||
void VlReadMem::setData(void* valuep, const std::string& rhs) {
|
||||
const QData shift = m_hex ? 4ULL : 1ULL;
|
||||
@@ -2258,6 +2263,7 @@ void VL_READMEM_N(bool hex, // Hex format, else binary
|
||||
QData addr = 0;
|
||||
std::string value;
|
||||
if (rmem.get(addr /*ref*/, value /*ref*/)) {
|
||||
// printf("readmem.get [%" PRIu64 "]=%s\n", addr, value.c_str());
|
||||
if (VL_UNLIKELY(addr < static_cast<QData>(array_lsb)
|
||||
|| addr >= static_cast<QData>(array_lsb + depth))) {
|
||||
VL_FATAL_MT(filename.c_str(), rmem.linenum(), "",
|
||||
@@ -2412,6 +2418,22 @@ uint64_t vl_time_pow10(int n) {
|
||||
return pow10[n];
|
||||
}
|
||||
|
||||
std::string vl_timescaled_double(double value, const char* format) VL_PURE {
|
||||
const char* suffixp = "s";
|
||||
// clang-format off
|
||||
if (value >= 1e0) { suffixp = "s"; value *= 1e0; }
|
||||
else if (value >= 1e-3) { suffixp = "ms"; value *= 1e3; }
|
||||
else if (value >= 1e-6) { suffixp = "us"; value *= 1e6; }
|
||||
else if (value >= 1e-9) { suffixp = "ns"; value *= 1e9; }
|
||||
else if (value >= 1e-12) { suffixp = "ps"; value *= 1e12; }
|
||||
else if (value >= 1e-15) { suffixp = "fs"; value *= 1e15; }
|
||||
else if (value >= 1e-18) { suffixp = "as"; value *= 1e18; }
|
||||
// clang-format on
|
||||
char valuestr[100];
|
||||
VL_SNPRINTF(valuestr, 100, format, value, suffixp);
|
||||
return std::string{valuestr}; // Gets converted to string, so no ref to stack
|
||||
}
|
||||
|
||||
void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp,
|
||||
const VerilatedContext* contextp) VL_MT_SAFE {
|
||||
VL_PRINTF_MT("Time scale of %s is %s / %s\n", namep, timeunitp,
|
||||
@@ -2438,7 +2460,8 @@ VerilatedContext::VerilatedContext()
|
||||
m_fdps.resize(31);
|
||||
std::fill(m_fdps.begin(), m_fdps.end(), static_cast<FILE*>(nullptr));
|
||||
m_fdFreeMct.resize(30);
|
||||
for (std::size_t i = 0, id = 1; i < m_fdFreeMct.size(); ++i, ++id) m_fdFreeMct[i] = id;
|
||||
IData id = 1;
|
||||
for (std::size_t i = 0; i < m_fdFreeMct.size(); ++i, ++id) m_fdFreeMct[i] = id;
|
||||
}
|
||||
|
||||
// Must declare here not in interface, as otherwise forward declarations not known
|
||||
@@ -2544,6 +2567,10 @@ std::string VerilatedContext::profVltFilename() const VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
return m_ns.m_profVltFilename;
|
||||
}
|
||||
void VerilatedContext::quiet(bool flag) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_quiet = flag;
|
||||
}
|
||||
void VerilatedContext::randReset(int val) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_randReset = val;
|
||||
@@ -2611,8 +2638,16 @@ void VerilatedContext::internalsDump() const VL_MT_SAFE {
|
||||
}
|
||||
|
||||
void VerilatedContext::addModel(VerilatedModel* modelp) {
|
||||
if (!quiet()) {
|
||||
// CPU time isn't read as starting point until model creation, so that quiet() is set
|
||||
// Thus if quiet(), avoids slow OS read affecting some usages that make many models
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_ns.m_cpuTimeStart.start();
|
||||
m_ns.m_wallTimeStart.start();
|
||||
}
|
||||
threadPoolp(); // Ensure thread pool is created, so m_threads cannot change any more
|
||||
|
||||
m_threadsInModels += modelp->threads();
|
||||
if (VL_UNLIKELY(modelp->threads() > m_threads)) {
|
||||
std::ostringstream msg;
|
||||
msg << "VerilatedContext has " << m_threads << " threads but model '"
|
||||
@@ -2645,6 +2680,7 @@ VerilatedContext::enableExecutionProfiler(VerilatedVirtualBase* (*construct)(Ver
|
||||
|
||||
//======================================================================
|
||||
// VerilatedContextImp:: Methods - command line
|
||||
|
||||
void VerilatedContextImp::commandArgsGuts(int argc, const char** argv)
|
||||
VL_MT_SAFE_EXCLUDES(m_argMutex) {
|
||||
const VerilatedLockGuard lock{m_argMutex};
|
||||
@@ -2741,6 +2777,8 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
|
||||
profExecFilename(str);
|
||||
} else if (commandArgVlString(arg, "+verilator+prof+vlt+file+", str)) {
|
||||
profVltFilename(str);
|
||||
} else if (arg == "+verilator+quiet") {
|
||||
quiet(true);
|
||||
} else if (commandArgVlUint64(arg, "+verilator+rand+reset+", u64, 0, 2)) {
|
||||
randReset(static_cast<int>(u64));
|
||||
} else if (commandArgVlUint64(arg, "+verilator+seed+", u64, 1,
|
||||
@@ -2786,7 +2824,7 @@ bool VerilatedContextImp::commandArgVlUint64(const std::string& arg, const std::
|
||||
VL_FATAL_MT("COMMAND_LINE", 0, "", msg.c_str());
|
||||
};
|
||||
|
||||
if (std::any_of(str.begin(), str.end(), [](int c) { return !std::isdigit(c); })) fail();
|
||||
if (std::any_of(str.cbegin(), str.cend(), [](int c) { return !std::isdigit(c); })) fail();
|
||||
char* end;
|
||||
valuer = std::strtoull(str.c_str(), &end, 10);
|
||||
if (errno == ERANGE) fail("Value out of range of uint64_t");
|
||||
@@ -2818,6 +2856,36 @@ uint64_t VerilatedContextImp::randSeedDefault64() const VL_MT_SAFE {
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// VerilatedContext:: Statistics
|
||||
|
||||
double VerilatedContext::statCpuTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
return m_ns.m_cpuTimeStart.deltaTime();
|
||||
}
|
||||
double VerilatedContext::statWallTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
return m_ns.m_wallTimeStart.deltaTime();
|
||||
}
|
||||
void VerilatedContext::statsPrintSummary() VL_MT_UNSAFE {
|
||||
if (quiet()) return;
|
||||
VL_PRINTF("- S i m u l a t i o n R e p o r t: %s %s\n", Verilated::productName(),
|
||||
Verilated::productVersion());
|
||||
const std::string endwhy = gotError() ? "$stop" : gotFinish() ? "$finish" : "end";
|
||||
const double simtimeInUnits = VL_TIME_Q() * vl_time_multiplier(timeunit())
|
||||
* vl_time_multiplier(timeprecision() - timeunit());
|
||||
const std::string simtime = vl_timescaled_double(simtimeInUnits);
|
||||
const double walltime = statWallTimeSinceStart();
|
||||
const double cputime = statCpuTimeSinceStart();
|
||||
const std::string simtimePerf
|
||||
= vl_timescaled_double((cputime != 0.0) ? (simtimeInUnits / cputime) : 0, "%0.3f %s");
|
||||
VL_PRINTF("- Verilator: %s at %s; walltime %0.3f s; speed %s/s\n", endwhy.c_str(),
|
||||
simtime.c_str(), walltime, simtimePerf.c_str());
|
||||
const double modelMB = VlOs::memUsageBytes() / 1024.0 / 1024.0;
|
||||
VL_PRINTF("- Verilator: cpu %0.3f s on %d threads; alloced %0.0f MB\n", cputime,
|
||||
threadsInModels(), modelMB);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// VerilatedContext:: Methods - scopes
|
||||
|
||||
@@ -2856,6 +2924,37 @@ const VerilatedScopeNameMap* VerilatedContext::scopeNameMap() VL_MT_SAFE {
|
||||
return &(impp()->m_impdatap->m_nameMap);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// VerilatedContext:: Methods - trace
|
||||
|
||||
void VerilatedContext::trace(VerilatedTraceBaseC* tfp, int levels, int options) VL_MT_SAFE {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("+ VerilatedContext::trace\n"););
|
||||
if (tfp->isOpen()) {
|
||||
VL_FATAL_MT("", 0, "",
|
||||
"Testbench C call to 'VerilatedContext::trace()' must not be called"
|
||||
" after 'VerilatedTrace*::open()'\n");
|
||||
return;
|
||||
}
|
||||
{
|
||||
// Legacy usage may call {modela}->trace(...) then {modelb}->trace(...)
|
||||
// So check for and suppress second and later calls
|
||||
if (tfp->modelConnected()) return;
|
||||
tfp->modelConnected(true);
|
||||
}
|
||||
// We rely on m_ns.m_traceBaseModelCbs being stable when trace() is called
|
||||
// nope: const VerilatedLockGuard lock{m_mutex};
|
||||
if (m_ns.m_traceBaseModelCbs.empty())
|
||||
VL_FATAL_MT("", 0, "",
|
||||
"Testbench C call to 'VerilatedContext::trace()' requires model(s) Verilated"
|
||||
" with --trace or --trace-vcd option");
|
||||
for (auto& cbr : m_ns.m_traceBaseModelCbs) cbr(tfp, levels, options);
|
||||
}
|
||||
void VerilatedContext::traceBaseModelCbAdd(traceBaseModelCb_t cb) VL_MT_SAFE {
|
||||
// Model creation registering a callback for when Verilated::trace() called
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_ns.m_traceBaseModelCbs.push_back(cb);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// VerilatedSyms:: Methods
|
||||
|
||||
@@ -3031,11 +3130,11 @@ void Verilated::stackCheck(QData needSize) VL_MT_UNSAFE {
|
||||
// if the user follows the suggestions
|
||||
if (VL_UNLIKELY(haveSize && needSize && haveSize < (needSize + needSize / 2))) {
|
||||
VL_PRINTF_MT("%%Warning: System has stack size %" PRIu64 " kb"
|
||||
" which may be too small; suggest 'ulimit -c %" PRIu64 "' or larger\n",
|
||||
" which may be too small; suggest 'ulimit -s %" PRIu64 "' or larger\n",
|
||||
haveSize / 1024, (needSize * 2) / 1024);
|
||||
}
|
||||
#else
|
||||
if (false && needSize) {} // Unused argument
|
||||
(void)needSize; // Unused argument
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -3234,7 +3333,7 @@ void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE {
|
||||
// Slowpath - Called only when find has failed
|
||||
const std::string msg = ("Testbench C called '"s + VerilatedImp::exportName(funcnum)
|
||||
+ "' but scope wasn't set, perhaps due to dpi import call without "
|
||||
+ "'context', or missing svSetScope. See IEEE 1800-2017 35.5.3.");
|
||||
+ "'context', or missing svSetScope. See IEEE 1800-2023 35.5.3.");
|
||||
VL_FATAL_MT("unknown", 0, "", msg.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
@@ -3300,3 +3399,7 @@ void VlDeleter::deleteAll() VL_EXCLUDES(m_mutex) VL_EXCLUDES(m_deleteMutex) VL_M
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// OS functions (last, so we have minimal OS dependencies above)
|
||||
|
||||
#define VL_ALLOW_VERILATEDOS_C
|
||||
#include "verilatedos_c.h"
|
||||
|
||||
+31
-2
@@ -101,6 +101,7 @@ class VerilatedScope;
|
||||
class VerilatedScopeNameMap;
|
||||
template <class, class>
|
||||
class VerilatedTrace;
|
||||
class VerilatedTraceBaseC;
|
||||
class VerilatedTraceConfig;
|
||||
class VerilatedVar;
|
||||
class VerilatedVarNameMap;
|
||||
@@ -347,6 +348,10 @@ class VerilatedContext VL_NOT_FINAL {
|
||||
friend class VerilatedContextImp;
|
||||
|
||||
protected:
|
||||
// TYPES
|
||||
using traceBaseModelCb_t
|
||||
= std::function<void(VerilatedTraceBaseC*, int, int)>; // Type of traceBaseModel callbacks
|
||||
|
||||
// MEMBERS
|
||||
// Slow path variables
|
||||
mutable VerilatedMutex m_mutex; // Mutex for most s_s/s_ns members
|
||||
@@ -354,13 +359,14 @@ protected:
|
||||
struct Serialized final { // All these members serialized/deserialized
|
||||
// No std::strings or pointers or will serialize badly!
|
||||
// Fast path
|
||||
uint64_t m_time = 0; // Current $time (unscaled), 0=at zero, or legacy
|
||||
bool m_assertOn = true; // Assertions are enabled
|
||||
bool m_calcUnusedSigs = false; // Waves file on, need all signals calculated
|
||||
bool m_fatalOnError = true; // Fatal on $stop/non-fatal error
|
||||
bool m_fatalOnVpiError = true; // Fatal on vpi error/unsupported
|
||||
bool m_gotError = false; // A $finish statement executed
|
||||
bool m_gotFinish = false; // A $finish or $stop statement executed
|
||||
uint64_t m_time = 0; // Current $time (unscaled), 0=at zero, or legacy
|
||||
bool m_quiet = false; // Quiet, no summary report
|
||||
// Slow path
|
||||
int8_t m_timeunit; // Time unit as 0..15
|
||||
int8_t m_timeprecision; // Time precision as 0..15
|
||||
@@ -390,6 +396,9 @@ protected:
|
||||
std::string m_coverageFilename; // +coverage+file filename
|
||||
std::string m_profExecFilename; // +prof+exec+file filename
|
||||
std::string m_profVltFilename; // +prof+vlt filename
|
||||
VlOs::DeltaCpuTime m_cpuTimeStart{false}; // CPU time, starts when create first model
|
||||
VlOs::DeltaWallTime m_wallTimeStart{false}; // Wall time, starts when create first model
|
||||
std::vector<traceBaseModelCb_t> m_traceBaseModelCbs; // Callbacks to traceRegisterModel
|
||||
} m_ns;
|
||||
|
||||
mutable VerilatedMutex m_argMutex; // Protect m_argVec, m_argVecLoaded
|
||||
@@ -405,6 +414,8 @@ protected:
|
||||
const std::unique_ptr<VerilatedContextImpData> m_impdatap;
|
||||
// Number of threads to use for simulation (size of m_threadPool + 1 for main thread)
|
||||
unsigned m_threads = std::thread::hardware_concurrency();
|
||||
// Number of threads in added models
|
||||
unsigned m_threadsInModels = 0;
|
||||
// The thread pool shared by all models added to this context
|
||||
std::unique_ptr<VerilatedVirtualBase> m_threadPool;
|
||||
// The execution profiler shared by all models added to this context
|
||||
@@ -489,6 +500,10 @@ public:
|
||||
void gotFinish(bool flag) VL_MT_SAFE;
|
||||
/// Return if got a $finish or $stop/error
|
||||
bool gotFinish() const VL_MT_SAFE { return m_s.m_gotFinish; }
|
||||
/// Enable quiet (also prevents need for OS calls to get CPU time)
|
||||
void quiet(bool flag) VL_MT_SAFE;
|
||||
/// Return if quiet enabled
|
||||
bool quiet() const VL_MT_SAFE { return m_s.m_quiet; }
|
||||
/// Select initial value of otherwise uninitialized signals.
|
||||
/// 0 = Set to zeros
|
||||
/// 1 = Set all bits to one
|
||||
@@ -501,6 +516,13 @@ public:
|
||||
/// Set default random seed, 0 = seed it automatically
|
||||
int randSeed() const VL_MT_SAFE { return m_s.m_randSeed; }
|
||||
|
||||
/// Return statistic: CPU time delta from model created until now
|
||||
double statCpuTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
/// Return statistic: Wall time delta from model created until now
|
||||
double statWallTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
/// Print statistics summary (if not quiet)
|
||||
void statsPrintSummary() VL_MT_UNSAFE;
|
||||
|
||||
// Time handling
|
||||
/// Returns current simulation time in units of timeprecision().
|
||||
///
|
||||
@@ -544,10 +566,14 @@ public:
|
||||
|
||||
/// Get number of threads used for simulation (including the main thread)
|
||||
unsigned threads() const { return m_threads; }
|
||||
/// Get number of threads in added models (for statistical use only)
|
||||
unsigned threadsInModels() const { return m_threadsInModels; }
|
||||
/// Set number of threads used for simulation (including the main thread)
|
||||
/// Can only be called before the thread pool is created (before first model is added).
|
||||
void threads(unsigned n);
|
||||
|
||||
/// Trace signals in models within the context; called by application code
|
||||
void trace(VerilatedTraceBaseC* tfp, int levels, int options = 0);
|
||||
/// Allow traces to at some point be enabled (disables some optimizations)
|
||||
void traceEverOn(bool flag) VL_MT_SAFE {
|
||||
if (flag) calcUnusedSigs(true);
|
||||
@@ -571,8 +597,8 @@ public:
|
||||
return reinterpret_cast<const VerilatedContextImp*>(this);
|
||||
}
|
||||
|
||||
// Internal: Model and thread setup
|
||||
void addModel(VerilatedModel*);
|
||||
|
||||
VerilatedVirtualBase* threadPoolp();
|
||||
void prepareClone();
|
||||
VerilatedVirtualBase* threadPoolpOnClone();
|
||||
@@ -606,6 +632,9 @@ public:
|
||||
static constexpr size_t serialized1Size() VL_PURE { return sizeof(m_s); }
|
||||
void* serialized1Ptr() VL_MT_UNSAFE { return &m_s; }
|
||||
|
||||
// Internal: trace registration
|
||||
void traceBaseModelCbAdd(traceBaseModelCb_t cb) VL_MT_SAFE;
|
||||
|
||||
// Internal: Check magic number
|
||||
static void checkMagic(const VerilatedContext* contextp);
|
||||
void selfTestClearMagic() { m_magic = 0x2; }
|
||||
|
||||
@@ -121,7 +121,7 @@ private:
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VerilatedCovImp(VerilatedContext* contextp)
|
||||
explicit VerilatedCovImp(VerilatedContext* contextp)
|
||||
: m_contextp{contextp} {}
|
||||
VL_UNCOPYABLE(VerilatedCovImp);
|
||||
|
||||
|
||||
@@ -165,7 +165,7 @@ public:
|
||||
/// Return default filename for the current thread
|
||||
static std::string defaultFilename() VL_MT_SAFE { return threadCovp()->defaultFilename(); }
|
||||
/// Write all coverage data to a file for the current thread
|
||||
static void write(const std::string filename) VL_MT_SAFE { threadCovp()->write(filename); }
|
||||
static void write(const std::string& filename) VL_MT_SAFE { threadCovp()->write(filename); }
|
||||
static void write() VL_MT_SAFE { write(defaultFilename()); }
|
||||
/// Clear coverage points (and call delete on all items) for the current thread
|
||||
static void clear() VL_MT_SAFE { threadCovp()->clear(); }
|
||||
|
||||
@@ -770,6 +770,36 @@ int svGetCallerInfo(const char** fileNamepp, int* lineNumberp) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Time
|
||||
|
||||
int svGetTime(const svScope scope, svTimeVal* time) {
|
||||
if (VL_UNLIKELY(!time)) return -1;
|
||||
const QData qtime = VL_TIME_Q();
|
||||
VlWide<2> itime;
|
||||
VL_SET_WQ(itime, qtime);
|
||||
time->low = itime[0];
|
||||
time->high = itime[1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int svGetTimeUnit(const svScope scope, int32_t* time_unit) {
|
||||
if (VL_UNLIKELY(!time_unit)) return -1;
|
||||
const VerilatedScope* const vscopep = reinterpret_cast<const VerilatedScope*>(scope);
|
||||
if (!vscopep) { // Null asks for global, not unlikely
|
||||
*time_unit = Verilated::threadContextp()->timeunit();
|
||||
} else {
|
||||
*time_unit = vscopep->timeunit();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int svGetTimePrecision(const svScope scope, int32_t* time_precision) {
|
||||
if (VL_UNLIKELY(!time_precision)) return -1;
|
||||
*time_precision = Verilated::threadContextp()->timeprecision();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Disables
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ class VerilatedFstBuffer VL_NOT_FINAL {
|
||||
/// Create a FST dump file in C standalone (no SystemC) simulations.
|
||||
/// Also derived for use in SystemC simulations.
|
||||
|
||||
class VerilatedFstC VL_NOT_FINAL {
|
||||
class VerilatedFstC VL_NOT_FINAL : public VerilatedTraceBaseC {
|
||||
VerilatedFst m_sptrace; // Trace file being created
|
||||
|
||||
// CONSTRUCTORS
|
||||
@@ -208,11 +208,14 @@ public:
|
||||
// METHODS - User called
|
||||
|
||||
/// Return if file is open
|
||||
bool isOpen() const VL_MT_SAFE { return m_sptrace.isOpen(); }
|
||||
bool isOpen() const override VL_MT_SAFE { return m_sptrace.isOpen(); }
|
||||
/// Open a new FST file
|
||||
virtual void open(const char* filename) VL_MT_SAFE { m_sptrace.open(filename); }
|
||||
/// Close dump
|
||||
void close() VL_MT_SAFE { m_sptrace.close(); }
|
||||
void close() VL_MT_SAFE {
|
||||
m_sptrace.close();
|
||||
modelConnected(false);
|
||||
}
|
||||
/// Flush dump
|
||||
void flush() VL_MT_SAFE { m_sptrace.flush(); }
|
||||
/// Write one cycle of dump data
|
||||
|
||||
+135
-1
@@ -263,7 +263,7 @@ static inline QData VL_EXTENDSIGN_Q(int lbits, QData lhs) VL_PURE {
|
||||
extern void _vl_debug_print_w(int lbits, WDataInP const iwp) VL_MT_SAFE;
|
||||
|
||||
//=========================================================================
|
||||
// Pli macros
|
||||
// Time handling
|
||||
|
||||
// clang-format off
|
||||
|
||||
@@ -321,6 +321,8 @@ uint64_t VerilatedContext::time() const VL_MT_SAFE {
|
||||
double vl_time_multiplier(int scale) VL_PURE;
|
||||
// Return power of 10. e.g. returns 100 if n==2
|
||||
uint64_t vl_time_pow10(int n) VL_PURE;
|
||||
// Return time as string with timescale suffix
|
||||
std::string vl_timescaled_double(double value, const char* format = "%0.0f%s") VL_PURE;
|
||||
|
||||
//=========================================================================
|
||||
// Functional macros/routines
|
||||
@@ -1309,6 +1311,26 @@ static inline void _vl_insert_WI(WDataOutP iowp, IData ld, int hbit, int lbit,
|
||||
}
|
||||
}
|
||||
|
||||
// Copy bits from lwp[hbit:lbit] to low bits of lhsr. rbits is real width of lshr
|
||||
static inline void _vl_insert_IW(IData& lhsr, WDataInP const lwp, int hbit, int lbit,
|
||||
int rbits = 0) VL_MT_SAFE {
|
||||
const int hoffset = VL_BITBIT_E(hbit);
|
||||
const int loffset = VL_BITBIT_E(lbit);
|
||||
const int hword = VL_BITWORD_E(hbit);
|
||||
const int lword = VL_BITWORD_E(lbit);
|
||||
const IData cleanmask = VL_MASK_I(rbits);
|
||||
if (hword == lword) {
|
||||
const IData insmask = (VL_MASK_I(hoffset - loffset + 1));
|
||||
lhsr = (lhsr & ~insmask) | ((lwp[lword] >> loffset) & (insmask & cleanmask));
|
||||
} else {
|
||||
const int nbitsonright = VL_IDATASIZE - loffset; // bits that filled by lword
|
||||
const IData hinsmask = (VL_MASK_E(hoffset - 0 + 1)) << nbitsonright;
|
||||
const IData linsmask = VL_MASK_E(VL_EDATASIZE - loffset);
|
||||
lhsr = (lhsr & ~linsmask) | ((lwp[lword] >> loffset) & (linsmask & cleanmask));
|
||||
lhsr = (lhsr & ~hinsmask) | ((lwp[hword] << nbitsonright) & (hinsmask & cleanmask));
|
||||
}
|
||||
}
|
||||
|
||||
// INTERNAL: Stuff large LHS bit 0++ into OUTPUT at specified offset
|
||||
// lwp may be "dirty"
|
||||
static inline void _vl_insert_WW(WDataOutP iowp, WDataInP const lwp, int hbit, int lbit,
|
||||
@@ -1556,6 +1578,13 @@ static inline void VL_ASSIGN_DYN_Q(VlQueue<T>& q, int elem_size, int lbits, QDat
|
||||
for (int i = 0; i < size; ++i) q.at(i) = (T)((from >> (i * elem_size)) & mask);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t T_Depth>
|
||||
static inline void VL_ASSIGN_UNPACK_Q(VlUnpacked<T, T_Depth>& q, size_t elem_size, QData from) {
|
||||
const QData mask = VL_MASK_Q(elem_size);
|
||||
for (size_t i = 0; i < T_Depth; ++i)
|
||||
q[i] = (T)((from >> ((T_Depth - 1 - i) * elem_size)) & mask);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline IData VL_DYN_TO_I(const VlQueue<T>& q, int elem_size) {
|
||||
IData ret = 0;
|
||||
@@ -1570,6 +1599,20 @@ static inline QData VL_DYN_TO_Q(const VlQueue<T>& q, int elem_size) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t T_Depth>
|
||||
static inline IData VL_UNPACK_TO_I(const VlUnpacked<T, T_Depth>& q, size_t elem_size) {
|
||||
IData ret = 0;
|
||||
for (size_t i = 0; i < T_Depth; ++i) ret |= q[T_Depth - 1 - i] << (i * elem_size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t T_Depth>
|
||||
static inline QData VL_UNPACK_TO_Q(const VlUnpacked<T, T_Depth>& q, size_t elem_size) {
|
||||
QData ret = 0;
|
||||
for (size_t i = 0; i < T_Depth; ++i) ret |= q[T_Depth - 1 - 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.
|
||||
@@ -2083,6 +2126,97 @@ static inline void VL_ASSIGNSEL_WW(int rbits, int obits, int lsb, WDataOutP iowp
|
||||
_vl_insert_WW(iowp, rwp, lsb + obits - 1, lsb, rbits);
|
||||
}
|
||||
|
||||
//====================================================
|
||||
// Range assignments
|
||||
|
||||
// These additional functions copy bits range [obis+roffset-1:roffset] from rhs to lower bits
|
||||
// of lhs(select before assigning). Rhs should always be wider than lhs.
|
||||
static inline void VL_SELASSIGN_II(int rbits, int obits, CData& lhsr, IData rhs,
|
||||
int roffset) VL_PURE {
|
||||
_vl_insert_II(lhsr, rhs >> roffset, obits - 1, 0, rbits);
|
||||
}
|
||||
static inline void VL_SELASSIGN_II(int rbits, int obits, SData& lhsr, IData rhs,
|
||||
int roffset) VL_PURE {
|
||||
_vl_insert_II(lhsr, rhs >> roffset, obits - 1, 0, rbits);
|
||||
}
|
||||
static inline void VL_SELASSIGN_II(int rbits, int obits, IData& lhsr, IData rhs,
|
||||
int roffset) VL_PURE {
|
||||
_vl_insert_II(lhsr, rhs >> roffset, obits - 1, 0, rbits);
|
||||
}
|
||||
static inline void VL_SELASSIGN_IQ(int rbits, int obits, CData& lhsr, QData rhs,
|
||||
int roffset) VL_PURE {
|
||||
// it will be truncated to right CData mask
|
||||
const CData cleanmask = VL_MASK_I(rbits);
|
||||
const CData insmask = VL_MASK_I(obits);
|
||||
lhsr = (lhsr & ~insmask) | (static_cast<CData>(rhs >> roffset) & (insmask & cleanmask));
|
||||
}
|
||||
static inline void VL_SELASSIGN_IQ(int rbits, int obits, SData& lhsr, QData rhs,
|
||||
int roffset) VL_PURE {
|
||||
// it will be truncated to right CData mask
|
||||
const SData cleanmask = VL_MASK_I(rbits);
|
||||
const SData insmask = VL_MASK_I(obits);
|
||||
lhsr = (lhsr & ~insmask) | (static_cast<SData>(rhs >> roffset) & (insmask & cleanmask));
|
||||
}
|
||||
static inline void VL_SELASSIGN_IQ(int rbits, int obits, IData& lhsr, QData rhs,
|
||||
int roffset) VL_PURE {
|
||||
const IData cleanmask = VL_MASK_I(rbits);
|
||||
const IData insmask = VL_MASK_I(obits);
|
||||
lhsr = (lhsr & ~insmask) | (static_cast<IData>(rhs >> roffset) & (insmask & cleanmask));
|
||||
}
|
||||
|
||||
static inline void VL_SELASSIGN_QQ(int rbits, int obits, QData& lhsr, QData rhs,
|
||||
int roffset) VL_PURE {
|
||||
_vl_insert_QQ(lhsr, rhs >> roffset, obits - 1, 0, rbits);
|
||||
}
|
||||
|
||||
static inline void VL_SELASSIGN_IW(int rbits, int obits, CData& lhsr, WDataInP const rhs,
|
||||
int roffset) VL_MT_SAFE {
|
||||
IData l = static_cast<IData>(lhsr);
|
||||
_vl_insert_IW(l, rhs, roffset + obits - 1, roffset, rbits);
|
||||
lhsr = static_cast<CData>(l);
|
||||
}
|
||||
static inline void VL_SELASSIGN_IW(int rbits, int obits, SData& lhsr, WDataInP const rhs,
|
||||
int roffset) VL_MT_SAFE {
|
||||
IData l = static_cast<IData>(lhsr);
|
||||
_vl_insert_IW(l, rhs, roffset + obits - 1, roffset, rbits);
|
||||
lhsr = static_cast<SData>(l);
|
||||
}
|
||||
static inline void VL_SELASSIGN_IW(int rbits, int obits, IData& lhsr, WDataInP const rhs,
|
||||
int roffset) VL_MT_SAFE {
|
||||
_vl_insert_IW(lhsr, rhs, roffset + obits - 1, roffset, rbits);
|
||||
}
|
||||
static inline void VL_SELASSIGN_QW(int rbits, int obits, QData& lhsr, WDataInP const rhs,
|
||||
int roffset) VL_MT_SAFE {
|
||||
// assert VL_QDATASIZE >= rbits > VL_IDATASIZE;
|
||||
IData low = static_cast<IData>(lhsr);
|
||||
IData high = static_cast<IData>(lhsr >> VL_IDATASIZE);
|
||||
if (obits <= VL_IDATASIZE) {
|
||||
_vl_insert_IW(low, rhs, obits + roffset - 1, roffset, VL_IDATASIZE);
|
||||
} else {
|
||||
_vl_insert_IW(low, rhs, roffset + VL_IDATASIZE - 1, roffset, VL_IDATASIZE);
|
||||
_vl_insert_IW(high, rhs, roffset + obits - 1, roffset + VL_IDATASIZE,
|
||||
rbits - VL_IDATASIZE);
|
||||
}
|
||||
lhsr = (static_cast<QData>(high) << VL_IDATASIZE) | low;
|
||||
}
|
||||
|
||||
static inline void VL_SELASSIGN_WW(int rbits, int obits, WDataOutP iowp, WDataInP const rwp,
|
||||
int roffset) VL_MT_SAFE {
|
||||
// assert rbits > VL_QDATASIZE
|
||||
const int wordoff = roffset / VL_EDATASIZE;
|
||||
const int lsb = roffset & VL_SIZEBITS_E;
|
||||
const int upperbits = lsb == 0 ? 0 : VL_EDATASIZE - lsb;
|
||||
// If roffset is not aligned, we copy some bits to align it.
|
||||
if (lsb != 0) {
|
||||
const int w = obits < upperbits ? obits : upperbits;
|
||||
const int insmask = VL_MASK_E(w);
|
||||
iowp[0] = (iowp[0] & ~insmask) | ((rwp[wordoff] >> lsb) & insmask);
|
||||
if (w == obits) return;
|
||||
obits -= w;
|
||||
}
|
||||
_vl_insert_WW(iowp, rwp + wordoff + (lsb != 0), upperbits + obits - 1, upperbits, rbits);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Triops
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ IData VL_DIST_ERLANG(IData& seedr, IData uk, IData umean) VL_MT_SAFE {
|
||||
return 0;
|
||||
}
|
||||
double x = 1.0;
|
||||
for (int32_t i = 1; i <= k; i++) { x = x * _vl_dbase_uniform(seedr, 0, 1); }
|
||||
for (int32_t i = 1; i <= k; i++) x = x * _vl_dbase_uniform(seedr, 0, 1);
|
||||
const double a = static_cast<double>(mean);
|
||||
const double b = static_cast<double>(k);
|
||||
double r = -a * log(x) / b;
|
||||
|
||||
@@ -59,7 +59,7 @@ void VlDelayScheduler::resume() {
|
||||
#endif
|
||||
bool resumed = false;
|
||||
|
||||
while (!m_queue.empty() && (m_queue.begin()->first == m_context.time())) {
|
||||
while (!m_queue.empty() && (m_queue.cbegin()->first == m_context.time())) {
|
||||
VlCoroutineHandle handle = std::move(m_queue.begin()->second);
|
||||
m_queue.erase(m_queue.begin());
|
||||
handle.resume();
|
||||
@@ -80,7 +80,7 @@ void VlDelayScheduler::resume() {
|
||||
}
|
||||
|
||||
uint64_t VlDelayScheduler::nextTimeSlot() const {
|
||||
if (!m_queue.empty()) return m_queue.begin()->first;
|
||||
if (!m_queue.empty()) return m_queue.cbegin()->first;
|
||||
if (m_zeroDelayed.empty())
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "%Error: There is no next time slot scheduled");
|
||||
return m_context.time();
|
||||
|
||||
@@ -180,7 +180,7 @@ public:
|
||||
bool empty() const { return m_queue.empty() && m_zeroDelayed.empty(); }
|
||||
// Are there coroutines to resume at the current simulation time?
|
||||
bool awaitingCurrentTime() const {
|
||||
return (!m_queue.empty() && (m_queue.begin()->first <= m_context.time()))
|
||||
return (!m_queue.empty() && (m_queue.cbegin()->first <= m_context.time()))
|
||||
|| !m_zeroDelayed.empty();
|
||||
}
|
||||
#ifdef VL_DEBUG
|
||||
|
||||
@@ -179,6 +179,21 @@ public:
|
||||
, m_useFstWriterThread{useFstWriterThread} {}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedTraceBaseC - base class of all Verilated*C trace classes
|
||||
// Internal use only
|
||||
|
||||
class VerilatedTraceBaseC VL_NOT_FINAL {
|
||||
bool m_modelConnected = false; // Model connected by calling Verilated::trace()
|
||||
public:
|
||||
/// True if file currently open
|
||||
virtual bool isOpen() const VL_MT_SAFE = 0;
|
||||
|
||||
// internal use only
|
||||
bool modelConnected() const VL_MT_SAFE { return m_modelConnected; }
|
||||
void modelConnected(bool flag) VL_MT_SAFE { m_modelConnected = flag; }
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedTrace
|
||||
|
||||
@@ -205,10 +220,6 @@ private:
|
||||
friend OffloadBuffer;
|
||||
|
||||
struct CallbackRecord final {
|
||||
// Note: would make these fields const, but some old STL implementations
|
||||
// (the one in Ubuntu 14.04 with GCC 4.8.4 in particular) use the
|
||||
// assignment operator on inserting into collections, so they don't work
|
||||
// with const fields...
|
||||
union { // The callback
|
||||
const initCb_t m_initCb;
|
||||
const dumpCb_t m_dumpCb;
|
||||
@@ -582,7 +593,7 @@ public:
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_WDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep += 2;
|
||||
for (int i = 0; i < (bits + 31) / 32; ++i) { *m_offloadBufferWritep++ = newvalp[i]; }
|
||||
for (int i = 0; i < (bits + 31) / 32; ++i) *m_offloadBufferWritep++ = newvalp[i];
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgDouble(uint32_t code, double newval) {
|
||||
|
||||
@@ -60,22 +60,6 @@ static double timescaleToDouble(const char* unitp) VL_PURE {
|
||||
return value;
|
||||
}
|
||||
|
||||
static std::string doubleToTimescale(double value) VL_PURE {
|
||||
const char* suffixp = "s";
|
||||
// clang-format off
|
||||
if (value >= 1e0) { suffixp = "s"; value *= 1e0; }
|
||||
else if (value >= 1e-3) { suffixp = "ms"; value *= 1e3; }
|
||||
else if (value >= 1e-6) { suffixp = "us"; value *= 1e6; }
|
||||
else if (value >= 1e-9) { suffixp = "ns"; value *= 1e9; }
|
||||
else if (value >= 1e-12) { suffixp = "ps"; value *= 1e12; }
|
||||
else if (value >= 1e-15) { suffixp = "fs"; value *= 1e15; }
|
||||
else if (value >= 1e-18) { suffixp = "as"; value *= 1e18; }
|
||||
// clang-format on
|
||||
char valuestr[100];
|
||||
VL_SNPRINTF(valuestr, 100, "%0.0f%s", value, suffixp);
|
||||
return valuestr; // Gets converted to string, so no ref to stack
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Buffer management
|
||||
|
||||
@@ -190,13 +174,16 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
|
||||
|
||||
//===
|
||||
// Rare commands
|
||||
case VerilatedTraceOffloadCommand::TIME_CHANGE:
|
||||
case VerilatedTraceOffloadCommand::TIME_CHANGE: {
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command TIME_CHANGE " << top);
|
||||
readp -= 1; // No code in this command, undo increment
|
||||
emitTimeChange(*reinterpret_cast<const uint64_t*>(readp));
|
||||
const uint64_t timeui
|
||||
= static_cast<uint64_t>(*reinterpret_cast<const uint32_t*>(readp)) << 32ULL
|
||||
| static_cast<uint64_t>(*reinterpret_cast<const uint32_t*>(readp + 1));
|
||||
emitTimeChange(timeui);
|
||||
readp += 2;
|
||||
continue;
|
||||
|
||||
}
|
||||
case VerilatedTraceOffloadCommand::TRACE_BUFFER:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command TRACE_BUFFER " << top);
|
||||
readp -= 1; // No code in this command, undo increment
|
||||
@@ -418,7 +405,7 @@ bool VerilatedTrace<VL_SUB_T, VL_BUF_T>::declCode(uint32_t code, const std::stri
|
||||
|
||||
template <>
|
||||
std::string VerilatedTrace<VL_SUB_T, VL_BUF_T>::timeResStr() const {
|
||||
return doubleToTimescale(m_timeRes);
|
||||
return vl_timescaled_double(m_timeRes);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
@@ -574,7 +561,10 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
|
||||
|
||||
// Tell worker to update time point
|
||||
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::TIME_CHANGE;
|
||||
*reinterpret_cast<uint64_t*>(m_offloadBufferWritep + 1) = timeui;
|
||||
*reinterpret_cast<uint32_t*>(m_offloadBufferWritep + 1)
|
||||
= static_cast<uint32_t>(timeui >> 32ULL);
|
||||
*reinterpret_cast<uint32_t*>(m_offloadBufferWritep + 2)
|
||||
= static_cast<uint32_t>(timeui);
|
||||
m_offloadBufferWritep += 3;
|
||||
} else {
|
||||
// Update time point
|
||||
|
||||
+35
-37
@@ -615,7 +615,7 @@ public:
|
||||
|
||||
// inside (set membership operator)
|
||||
bool inside(const T_Value& value) const {
|
||||
return std::find(m_deque.begin(), m_deque.end(), value) != m_deque.end();
|
||||
return std::find(m_deque.cbegin(), m_deque.cend(), value) != m_deque.cend();
|
||||
}
|
||||
|
||||
// Return slice q[lsb:msb]
|
||||
@@ -776,13 +776,13 @@ public:
|
||||
// Reduction operators
|
||||
VlQueue min() const {
|
||||
if (m_deque.empty()) return VlQueue{};
|
||||
const auto it = std::min_element(m_deque.begin(), m_deque.end());
|
||||
const auto it = std::min_element(m_deque.cbegin(), m_deque.cend());
|
||||
return VlQueue::cons(*it);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue min(Func with_func) const {
|
||||
if (m_deque.empty()) return VlQueue{};
|
||||
const auto it = std::min_element(m_deque.begin(), m_deque.end(),
|
||||
const auto it = std::min_element(m_deque.cbegin(), m_deque.cend(),
|
||||
[&with_func](const IData& a, const IData& b) {
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
@@ -790,13 +790,13 @@ public:
|
||||
}
|
||||
VlQueue max() const {
|
||||
if (m_deque.empty()) return VlQueue{};
|
||||
const auto it = std::max_element(m_deque.begin(), m_deque.end());
|
||||
const auto it = std::max_element(m_deque.cbegin(), m_deque.cend());
|
||||
return VlQueue::cons(*it);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue max(Func with_func) const {
|
||||
if (m_deque.empty()) return VlQueue{};
|
||||
const auto it = std::max_element(m_deque.begin(), m_deque.end(),
|
||||
const auto it = std::max_element(m_deque.cbegin(), m_deque.cend(),
|
||||
[&with_func](const IData& a, const IData& b) {
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
@@ -817,40 +817,40 @@ public:
|
||||
}
|
||||
T_Value r_product() const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
auto it = m_deque.cbegin();
|
||||
T_Value out{*it};
|
||||
++it;
|
||||
for (; it != m_deque.end(); ++it) out *= *it;
|
||||
for (; it != m_deque.cend(); ++it) out *= *it;
|
||||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
T_Value r_product(Func with_func) const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
auto it = m_deque.cbegin();
|
||||
IData index = 0;
|
||||
T_Value out{with_func(index, *it)};
|
||||
++it;
|
||||
++index;
|
||||
for (; it != m_deque.end(); ++it) out *= with_func(index++, *it);
|
||||
for (; it != m_deque.cend(); ++it) out *= with_func(index++, *it);
|
||||
return out;
|
||||
}
|
||||
T_Value r_and() const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
auto it = m_deque.cbegin();
|
||||
T_Value out{*it};
|
||||
++it;
|
||||
for (; it != m_deque.end(); ++it) out &= *it;
|
||||
for (; it != m_deque.cend(); ++it) out &= *it;
|
||||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
T_Value r_and(Func with_func) const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
auto it = m_deque.cbegin();
|
||||
IData index = 0;
|
||||
T_Value out{with_func(index, *it)};
|
||||
++it;
|
||||
++index;
|
||||
for (; it != m_deque.end(); ++it) out &= with_func(index, *it);
|
||||
for (; it != m_deque.cend(); ++it) out &= with_func(index, *it);
|
||||
return out;
|
||||
}
|
||||
T_Value r_or() const {
|
||||
@@ -948,7 +948,7 @@ public:
|
||||
// Return last element. Verilog: function int last(ref index)
|
||||
int last(T_Key& indexr) const {
|
||||
const auto it = m_map.crbegin();
|
||||
if (it == m_map.rend()) return 0;
|
||||
if (it == m_map.crend()) return 0;
|
||||
indexr = it->first;
|
||||
return 1;
|
||||
}
|
||||
@@ -1077,7 +1077,7 @@ public:
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> find_first(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
= std::find_if(m_map.cbegin(), m_map.cend(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.end()) return VlQueue<T_Value>{};
|
||||
@@ -1086,7 +1086,7 @@ public:
|
||||
template <typename Func>
|
||||
VlQueue<T_Key> find_first_index(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
= std::find_if(m_map.cbegin(), m_map.cend(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.end()) return VlQueue<T_Value>{};
|
||||
@@ -1094,19 +1094,17 @@ public:
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> find_last(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
const auto it = std::find_if(
|
||||
m_map.crbegin(), m_map.crend(),
|
||||
[=](const std::pair<T_Key, T_Value>& i) { return with_func(i.first, i.second); });
|
||||
if (it == m_map.rend()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Key> find_last_index(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
const auto it = std::find_if(
|
||||
m_map.crbegin(), m_map.crend(),
|
||||
[=](const std::pair<T_Key, T_Value>& i) { return with_func(i.first, i.second); });
|
||||
if (it == m_map.rend()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Key>::cons(it->first);
|
||||
}
|
||||
@@ -1115,7 +1113,7 @@ public:
|
||||
VlQueue<T_Value> min() const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::min_element(
|
||||
m_map.begin(), m_map.end(),
|
||||
m_map.cbegin(), m_map.cend(),
|
||||
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return a.second < b.second;
|
||||
});
|
||||
@@ -1125,7 +1123,7 @@ public:
|
||||
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(),
|
||||
m_map.cbegin(), m_map.cend(),
|
||||
[&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);
|
||||
});
|
||||
@@ -1134,7 +1132,7 @@ public:
|
||||
VlQueue<T_Value> max() const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::max_element(
|
||||
m_map.begin(), m_map.end(),
|
||||
m_map.cbegin(), m_map.cend(),
|
||||
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return a.second < b.second;
|
||||
});
|
||||
@@ -1144,7 +1142,7 @@ public:
|
||||
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(),
|
||||
m_map.cbegin(), m_map.cend(),
|
||||
[&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);
|
||||
});
|
||||
@@ -1164,36 +1162,36 @@ public:
|
||||
}
|
||||
T_Value r_product() const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
auto it = m_map.cbegin();
|
||||
T_Value out{it->second};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out *= it->second;
|
||||
for (; it != m_map.cend(); ++it) out *= it->second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
T_Value r_product(Func with_func) const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
auto it = m_map.cbegin();
|
||||
T_Value out{with_func(it->first, it->second)};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out *= with_func(it->first, it->second);
|
||||
for (; it != m_map.cend(); ++it) out *= with_func(it->first, it->second);
|
||||
return out;
|
||||
}
|
||||
T_Value r_and() const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
auto it = m_map.cbegin();
|
||||
T_Value out{it->second};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out &= it->second;
|
||||
for (; it != m_map.cend(); ++it) out &= it->second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
T_Value r_and(Func with_func) const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
auto it = m_map.cbegin();
|
||||
T_Value out{with_func(it->first, it->second)};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out &= with_func(it->first, it->second);
|
||||
for (; it != m_map.cend(); ++it) out &= with_func(it->first, it->second);
|
||||
return out;
|
||||
}
|
||||
T_Value r_or() const {
|
||||
@@ -1707,7 +1705,7 @@ public:
|
||||
T_Class* operator->() const { return m_objp; }
|
||||
// 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)
|
||||
// In SV A == B iff both are handles to the same object (IEEE 1800-2023 8.4)
|
||||
template <typename T_OtherClass>
|
||||
bool operator==(const VlClassRef<T_OtherClass>& rhs) const {
|
||||
return m_objp == rhs.m_objp;
|
||||
|
||||
@@ -268,7 +268,7 @@ void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
|
||||
// We add output data to m_writep.
|
||||
// When it gets nearly full we dump it using this routine which calls write()
|
||||
// This is much faster than using buffered I/O
|
||||
if (VL_UNLIKELY(!isOpen())) return;
|
||||
if (VL_UNLIKELY(!m_isOpen)) return;
|
||||
const char* wp = m_wrBufp;
|
||||
while (true) {
|
||||
const ssize_t remaining = (m_writep - wp);
|
||||
|
||||
@@ -251,7 +251,7 @@ public:
|
||||
/// Class representing a VCD dump file in C standalone (no SystemC)
|
||||
/// simulations. Also derived for use in SystemC simulations.
|
||||
|
||||
class VerilatedVcdC VL_NOT_FINAL {
|
||||
class VerilatedVcdC VL_NOT_FINAL : public VerilatedTraceBaseC {
|
||||
VerilatedVcd m_sptrace; // Trace file being created
|
||||
|
||||
// CONSTRUCTORS
|
||||
@@ -267,7 +267,7 @@ public:
|
||||
// METHODS - User called
|
||||
|
||||
/// Return if file is open
|
||||
bool isOpen() const VL_MT_SAFE { return m_sptrace.isOpen(); }
|
||||
bool isOpen() const override VL_MT_SAFE { return m_sptrace.isOpen(); }
|
||||
/// Open a new VCD file
|
||||
/// This includes a complete header dump each time it is called,
|
||||
/// just as if this object was deleted and reconstructed.
|
||||
@@ -283,7 +283,10 @@ public:
|
||||
/// first may be removed. Cat files together to create viewable vcd.
|
||||
void rolloverSize(size_t size) VL_MT_SAFE { m_sptrace.rolloverSize(size); }
|
||||
/// Close dump
|
||||
void close() VL_MT_SAFE { m_sptrace.close(); }
|
||||
void close() VL_MT_SAFE {
|
||||
m_sptrace.close();
|
||||
modelConnected(false);
|
||||
}
|
||||
/// Flush dump
|
||||
void flush() VL_MT_SAFE { m_sptrace.flush(); }
|
||||
/// Write one cycle of dump data
|
||||
|
||||
+70
-36
@@ -263,33 +263,24 @@ class VerilatedVpioScope VL_NOT_FINAL : public VerilatedVpio {
|
||||
protected:
|
||||
const VerilatedScope* const m_scopep;
|
||||
bool m_toplevel = false;
|
||||
const char* m_name;
|
||||
const char* m_fullname;
|
||||
|
||||
public:
|
||||
explicit VerilatedVpioScope(const VerilatedScope* scopep)
|
||||
: m_scopep{scopep} {
|
||||
std::string scopename = m_scopep->name();
|
||||
std::string::size_type pos = std::string::npos;
|
||||
// Look for '.' not inside escaped identifier
|
||||
size_t i = 0;
|
||||
while (i < scopename.length()) {
|
||||
if (scopename[i] == '\\') {
|
||||
while (i < scopename.length() && scopename[i] != ' ') ++i;
|
||||
++i; // Proc ' ', it should always be there. Then grab '.' on next cycle
|
||||
} else {
|
||||
while (i < scopename.length() && scopename[i] != '.') ++i;
|
||||
if (i < scopename.length()) pos = i++;
|
||||
}
|
||||
}
|
||||
if (VL_UNLIKELY(pos == std::string::npos)) m_toplevel = true;
|
||||
m_fullname = m_scopep->name();
|
||||
if (std::strncmp(m_fullname, "TOP.", 4) == 0) m_fullname += 4;
|
||||
m_name = m_scopep->identifier();
|
||||
}
|
||||
~VerilatedVpioScope() override = default;
|
||||
static VerilatedVpioScope* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioScope*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
uint32_t type() const override { return vpiScope; }
|
||||
uint32_t type() const override { return vpiGenScope; }
|
||||
const VerilatedScope* scopep() const { return m_scopep; }
|
||||
const char* name() const override { return m_scopep->name(); }
|
||||
const char* fullname() const override { return m_scopep->name(); }
|
||||
const char* name() const override { return m_name; }
|
||||
const char* fullname() const override { return m_fullname; }
|
||||
bool toplevel() const { return m_toplevel; }
|
||||
};
|
||||
|
||||
@@ -469,22 +460,29 @@ public:
|
||||
};
|
||||
|
||||
class VerilatedVpioModule final : public VerilatedVpioScope {
|
||||
const char* m_name;
|
||||
const char* m_fullname;
|
||||
|
||||
public:
|
||||
explicit VerilatedVpioModule(const VerilatedScope* modulep)
|
||||
: VerilatedVpioScope{modulep} {
|
||||
m_fullname = m_scopep->name();
|
||||
if (std::strncmp(m_fullname, "TOP.", 4) == 0) m_fullname += 4;
|
||||
m_name = m_scopep->identifier();
|
||||
// Look for '.' not inside escaped identifier
|
||||
const std::string scopename = m_fullname;
|
||||
std::string::size_type pos = std::string::npos;
|
||||
size_t i = 0;
|
||||
while (i < scopename.length()) {
|
||||
if (scopename[i] == '\\') {
|
||||
while (i < scopename.length() && scopename[i] != ' ') ++i;
|
||||
++i; // Proc ' ', it should always be there. Then grab '.' on next cycle
|
||||
} else {
|
||||
while (i < scopename.length() && scopename[i] != '.') ++i;
|
||||
if (i < scopename.length()) pos = i++;
|
||||
}
|
||||
}
|
||||
if (VL_UNLIKELY(pos == std::string::npos)) m_toplevel = true;
|
||||
}
|
||||
static VerilatedVpioModule* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioModule*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
uint32_t type() const override { return vpiModule; }
|
||||
const char* name() const override { return m_name; }
|
||||
const char* fullname() const override { return m_fullname; }
|
||||
};
|
||||
|
||||
class VerilatedVpioModuleIter final : public VerilatedVpio {
|
||||
@@ -507,8 +505,8 @@ public:
|
||||
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
||||
return nullptr;
|
||||
}
|
||||
const VerilatedScope::Type itype = (*m_it)->type();
|
||||
const VerilatedScope* const modp = *m_it++;
|
||||
const VerilatedScope::Type itype = modp->type();
|
||||
if (itype == VerilatedScope::SCOPE_MODULE) {
|
||||
return (new VerilatedVpioModule{modp})->castVpiHandle();
|
||||
}
|
||||
@@ -516,26 +514,54 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class VerilatedVpioScopeIter final : public VerilatedVpio {
|
||||
const std::vector<const VerilatedScope*>* m_vec;
|
||||
std::vector<const VerilatedScope*>::const_iterator m_it;
|
||||
|
||||
public:
|
||||
explicit VerilatedVpioScopeIter(const std::vector<const VerilatedScope*>& vec)
|
||||
: m_vec{&vec} {
|
||||
m_it = m_vec->begin();
|
||||
}
|
||||
~VerilatedVpioScopeIter() override = default;
|
||||
static VerilatedVpioScopeIter* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioScopeIter*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
uint32_t type() const override { return vpiIterator; }
|
||||
vpiHandle dovpi_scan() override {
|
||||
while (true) {
|
||||
if (m_it == m_vec->end()) {
|
||||
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
||||
return nullptr;
|
||||
}
|
||||
const VerilatedScope* const modp = *m_it++;
|
||||
const VerilatedScope::Type itype = modp->type();
|
||||
if (itype == VerilatedScope::SCOPE_OTHER) {
|
||||
return (new VerilatedVpioScope{modp})->castVpiHandle();
|
||||
} else if (itype == VerilatedScope::SCOPE_MODULE) {
|
||||
return (new VerilatedVpioModule{modp})->castVpiHandle();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static const char* d_unit = "$unit";
|
||||
class VerilatedVpioPackage final : public VerilatedVpioScope {
|
||||
std::string m_name;
|
||||
std::string m_fullname;
|
||||
std::string m_fullname_string;
|
||||
|
||||
public:
|
||||
explicit VerilatedVpioPackage(const VerilatedScope* modulep)
|
||||
: VerilatedVpioScope{modulep} {
|
||||
const char* sfullname = m_scopep->name();
|
||||
if (std::strncmp(sfullname, "TOP.", 4) == 0) sfullname += 4;
|
||||
m_fullname = std::string{sfullname} + "::";
|
||||
if (m_fullname == "\\$unit ::") m_fullname = "$unit::";
|
||||
m_name = std::string(m_scopep->identifier());
|
||||
if (m_name == "\\$unit ") m_name = "$unit";
|
||||
m_fullname_string = std::string{m_fullname} + "::";
|
||||
if (m_fullname_string == "\\$unit ::") m_fullname_string = "$unit::";
|
||||
|
||||
if (strcmp(m_name, "\\$unit ") == 0) m_name = d_unit;
|
||||
}
|
||||
static VerilatedVpioPackage* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioPackage*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
const char* fullname() const override { return m_fullname_string.c_str(); }
|
||||
uint32_t type() const override { return vpiPackage; }
|
||||
const char* name() const override { return m_name.c_str(); }
|
||||
const char* fullname() const override { return m_fullname.c_str(); }
|
||||
};
|
||||
|
||||
class VerilatedVpioInstanceIter final : public VerilatedVpio {
|
||||
@@ -1978,13 +2004,21 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
|
||||
return ((new VerilatedVpioVarIter{vop, true})->castVpiHandle());
|
||||
}
|
||||
case vpiModule: {
|
||||
const VerilatedVpioModule* const vop = VerilatedVpioModule::castp(object);
|
||||
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
|
||||
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
|
||||
const VerilatedScope* const modp = vop ? vop->scopep() : nullptr;
|
||||
const auto it = vlstd::as_const(map)->find(const_cast<VerilatedScope*>(modp));
|
||||
if (it == map->end()) return nullptr;
|
||||
return ((new VerilatedVpioModuleIter{it->second})->castVpiHandle());
|
||||
}
|
||||
case vpiInternalScope: {
|
||||
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
|
||||
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
|
||||
const VerilatedScope* const modp = vop ? vop->scopep() : nullptr;
|
||||
const auto it = vlstd::as_const(map)->find(const_cast<VerilatedScope*>(modp));
|
||||
if (it == map->end()) return nullptr;
|
||||
return ((new VerilatedVpioScopeIter{it->second})->castVpiHandle());
|
||||
}
|
||||
case vpiInstance: {
|
||||
if (object) return nullptr;
|
||||
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include "verilated_syms.h"
|
||||
|
||||
//======================================================================
|
||||
// From IEEE 1800-2017 annex M
|
||||
// From IEEE 1800-2023 annex M
|
||||
|
||||
#include "vltstd/sv_vpi_user.h"
|
||||
|
||||
|
||||
+46
-8
@@ -58,6 +58,9 @@
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) __attribute__((format(printf, (fmtArgNum), (fmtArgNum) + 1)))
|
||||
# define VL_ATTR_PURE __attribute__((pure))
|
||||
# define VL_ATTR_UNUSED __attribute__((unused))
|
||||
#ifndef VL_ATTR_WARN_UNUSED_RESULT
|
||||
# define VL_ATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
|
||||
#endif
|
||||
# if !defined(_WIN32) && !defined(__MINGW32__)
|
||||
// All VL_ATTR_WEAK symbols must be marked with the macOS -U linker flag in verilated.mk.in
|
||||
# define VL_ATTR_WEAK __attribute__((weak))
|
||||
@@ -164,6 +167,9 @@
|
||||
#ifndef VL_ATTR_UNUSED
|
||||
# define VL_ATTR_UNUSED ///< Attribute that function that may be never used
|
||||
#endif
|
||||
#ifndef VL_ATTR_WARN_UNUSED_RESULT
|
||||
# define VL_ATTR_WARN_UNUSED_RESULT ///< Attribute that return value of function must be used
|
||||
#endif
|
||||
#ifndef VL_ATTR_WEAK
|
||||
# define VL_ATTR_WEAK ///< Attribute that function external that is optionally defined
|
||||
#endif
|
||||
@@ -337,6 +343,7 @@ extern "C" void __gcov_dump();
|
||||
#include <cstdint>
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
|
||||
#ifndef VL_NO_LEGACY
|
||||
using vluint8_t = uint8_t; ///< 8-bit unsigned type (backward compatibility)
|
||||
@@ -604,6 +611,45 @@ static inline double VL_ROUND(double n) {
|
||||
#define VL_OFFSETOF(type, field) \
|
||||
(reinterpret_cast<size_t>(&(reinterpret_cast<type*>(0x10000000)->field)) - 0x10000000)
|
||||
|
||||
//=========================================================================
|
||||
// Time and performance
|
||||
|
||||
namespace VlOs {
|
||||
|
||||
extern uint64_t memUsageBytes() VL_MT_SAFE; ///< Return memory usage in bytes, or 0 if unknown
|
||||
|
||||
// Internal: Record CPU time, starting point on construction, and current delta from that
|
||||
class DeltaCpuTime final {
|
||||
double m_start{}; // Time constructed at
|
||||
static double gettime() VL_MT_SAFE;
|
||||
|
||||
public:
|
||||
// Construct, and if startit is true, start() timer
|
||||
explicit DeltaCpuTime(bool startit) {
|
||||
if (startit) start();
|
||||
}
|
||||
void start() VL_MT_SAFE { m_start = gettime(); } // Start timer; record current time
|
||||
double deltaTime() const VL_MT_SAFE { // Return time between now and start()
|
||||
return (m_start == 0.0) ? 0.0 : gettime() - m_start;
|
||||
}
|
||||
};
|
||||
// Internal: Record wall time, starting point on construction, and current delta from that
|
||||
class DeltaWallTime final {
|
||||
double m_start{}; // Time constructed at
|
||||
static double gettime() VL_MT_SAFE;
|
||||
|
||||
public:
|
||||
// Construct, and if startit is true, start() timer
|
||||
explicit DeltaWallTime(bool startit) {
|
||||
if (startit) start();
|
||||
}
|
||||
void start() VL_MT_SAFE { m_start = gettime(); } // Start timer; record current time
|
||||
double deltaTime() const VL_MT_SAFE { // Return time between now and start()
|
||||
return (m_start == 0.0) ? 0.0 : gettime() - m_start;
|
||||
}
|
||||
};
|
||||
} //namespace VlOs
|
||||
|
||||
//=========================================================================
|
||||
// Conversions
|
||||
|
||||
@@ -637,14 +683,6 @@ T const& as_const(T& v) VL_MT_SAFE {
|
||||
return v;
|
||||
}
|
||||
|
||||
// C++14's std::exchange
|
||||
template <class T, class U = T>
|
||||
T exchange(T& obj, U&& new_value) {
|
||||
T old_value = std::move(obj);
|
||||
obj = std::forward<U>(new_value);
|
||||
return old_value;
|
||||
}
|
||||
|
||||
}; // namespace vlstd
|
||||
|
||||
//=========================================================================
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
// Copyright 2003-2024 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/Verilator common implementation for OS portability
|
||||
///
|
||||
/// This is compiled as part of other .cpp files to reduce compile time
|
||||
/// and as such is a .h file rather than .cpp file.
|
||||
///
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VL_ALLOW_VERILATEDOS_C
|
||||
#error "This file should be included only from V3Os.cpp/Verilated.cpp"
|
||||
#endif
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
// clang-format off
|
||||
#if defined(_WIN32) || defined(__MINGW32__)
|
||||
# include <windows.h> // LONG for bcrypt.h on MINGW
|
||||
# include <processthreadsapi.h> // GetProcessTimes
|
||||
# include <psapi.h> // GetProcessMemoryInfo
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
namespace VlOs {
|
||||
|
||||
//=========================================================================
|
||||
// VlOs::VlGetCpuTime/VlGetWallTime implementation
|
||||
|
||||
double DeltaCpuTime::gettime() VL_MT_SAFE {
|
||||
#if defined(_WIN32) || defined(__MINGW32__)
|
||||
FILETIME lpCreationTime, lpExitTime, lpKernelTime, lpUserTime;
|
||||
if (0
|
||||
!= GetProcessTimes(GetCurrentProcess(), &lpCreationTime, &lpExitTime, &lpKernelTime,
|
||||
&lpUserTime)) {
|
||||
return static_cast<double>(static_cast<uint64_t>(lpUserTime.dwLowDateTime)
|
||||
| static_cast<uint64_t>(lpUserTime.dwHighDateTime) << 32ULL)
|
||||
* 1e-7;
|
||||
}
|
||||
#else
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
|
||||
timespec ts;
|
||||
if (0 != clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts)) // MT-Safe
|
||||
return ts.tv_sec + ts.tv_nsec * 1e-9;
|
||||
#endif
|
||||
return 0.0;
|
||||
}
|
||||
double DeltaWallTime::gettime() VL_MT_SAFE {
|
||||
#if defined(_WIN32) || defined(__MINGW32__)
|
||||
FILETIME ft; // contains number of 0.1us intervals since the beginning of 1601 UTC.
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
const uint64_t tenthus
|
||||
= ((static_cast<uint64_t>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime + 5ULL);
|
||||
return static_cast<double>(tenthus) * 1e-7;
|
||||
#else
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
|
||||
timespec ts;
|
||||
if (0 == clock_gettime(CLOCK_MONOTONIC, &ts)) // MT-Safe
|
||||
return ts.tv_sec + ts.tv_nsec * 1e-9;
|
||||
return 0.0;
|
||||
#endif
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// VlOs::memUsageBytes implementation
|
||||
|
||||
uint64_t memUsageBytes() VL_MT_SAFE {
|
||||
#if defined(_WIN32) || defined(__MINGW32__)
|
||||
const HANDLE process = GetCurrentProcess();
|
||||
PROCESS_MEMORY_COUNTERS pmc;
|
||||
if (GetProcessMemoryInfo(process, &pmc, sizeof(pmc))) {
|
||||
// The best we can do using simple Windows APIs is to get the size of the working set.
|
||||
return pmc.WorkingSetSize;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
// Highly unportable. Sorry
|
||||
const char* const statmFilename = "/proc/self/statm";
|
||||
FILE* const fp = fopen(statmFilename, "r");
|
||||
if (!fp) return 0;
|
||||
uint64_t size, resident, share, text, lib, data, dt; // All in pages
|
||||
const int items = fscanf(
|
||||
fp, "%" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64,
|
||||
&size, &resident, &share, &text, &lib, &data, &dt);
|
||||
fclose(fp);
|
||||
if (VL_UNCOVERABLE(7 != items)) return 0;
|
||||
return (text + data) * getpagesize();
|
||||
#endif
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
} //namespace VlOs
|
||||
@@ -150,10 +150,21 @@ extern "C" {
|
||||
#define vpiIntegerNet 681
|
||||
#define vpiLogicNet vpiNet
|
||||
#define vpiTimeNet 682
|
||||
#define vpiUnionNet 525
|
||||
#define vpiShortRealNet 526
|
||||
#define vpiRealNet 527
|
||||
#define vpiByteNet 528
|
||||
#define vpiShortIntNet 529
|
||||
#define vpiIntNet 530
|
||||
#define vpiLongIntNet 531
|
||||
#define vpiBitNet 532
|
||||
#define vpiInterconnectNet 533
|
||||
#define vpiInterconnectArray 534
|
||||
#define vpiStructNet 683
|
||||
#define vpiBreak 684
|
||||
#define vpiContinue 685
|
||||
#define vpiPackedArrayNet 693
|
||||
#define vpiNettypeDecl 523
|
||||
#define vpiConstraintExpr 747
|
||||
#define vpiElseConst 748
|
||||
#define vpiImplication 749
|
||||
@@ -174,6 +185,8 @@ extern "C" {
|
||||
#define vpiBaseTypespec 703
|
||||
#define vpiElemTypespec 704
|
||||
|
||||
#define vpiNetTypedefAlias 705
|
||||
|
||||
#define vpiInputSkew 706
|
||||
#define vpiOutputSkew 707
|
||||
#define vpiGlobalClocking 708
|
||||
@@ -206,7 +219,8 @@ extern "C" {
|
||||
#define vpiMessages 735
|
||||
#define vpiLoopVars 737
|
||||
|
||||
#define vpiConcurrentAssertions 740
|
||||
#define vpiConcurrentAssertion 740
|
||||
#define vpiConcurrentAssertions vpiConcurrentAssertion
|
||||
#define vpiMatchItem 741
|
||||
#define vpiMember 742
|
||||
#define vpiElement 743
|
||||
@@ -221,6 +235,7 @@ extern "C" {
|
||||
/**************************************************************************/
|
||||
/************************ generic object properties ***********************/
|
||||
/**************************************************************************/
|
||||
|
||||
#define vpiTop 600
|
||||
|
||||
#define vpiUnit 602
|
||||
@@ -255,7 +270,6 @@ extern "C" {
|
||||
#define vpiModportPort 2
|
||||
/* vpiPort is also a port type. It is defined in vpi_user.h */
|
||||
|
||||
|
||||
#define vpiConstantVariable 612
|
||||
#define vpiStructUnionMember 615
|
||||
|
||||
@@ -407,6 +421,7 @@ extern "C" {
|
||||
/***************************** structure *****************************/
|
||||
|
||||
/**************************** CALLBACK REASONS ****************************/
|
||||
#define vpiMethodFuncCall 648
|
||||
#define cbStartOfThread 600 /* callback on thread creation */
|
||||
#define cbEndOfThread 601 /* callback on thread termination */
|
||||
#define cbEnterThread 602 /* callback on reentering thread */
|
||||
|
||||
+51
-6
@@ -7,7 +7,7 @@
|
||||
* This file contains the constant definitions, structure definitions,
|
||||
* and routine declarations used by SystemVerilog DPI.
|
||||
*
|
||||
* This file is from the SystemVerilog IEEE 1800-2017 Annex I.
|
||||
* This file is from the SystemVerilog IEEE 1800-2023 Annex I.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_SVDPI
|
||||
@@ -111,6 +111,26 @@ typedef uint32_t svBitVecVal;
|
||||
((N) == 32 ? (VALUE) : \
|
||||
(((VALUE) & (1 << (N))) ? ((VALUE) | ~SV_MASK(N)) : ((VALUE) & SV_MASK(N))))
|
||||
|
||||
#ifndef VPI_TIME
|
||||
#define VPI_TIME
|
||||
typedef struct t_vpi_time {
|
||||
int32_t type;
|
||||
uint32_t high, low;
|
||||
double real;
|
||||
} s_vpi_time, *p_vpi_time;
|
||||
|
||||
#define vpiScaledRealTime 1
|
||||
#define vpiSimTime 2
|
||||
#define vpiSuppressTime 3
|
||||
#endif
|
||||
|
||||
/* time value */
|
||||
typedef s_vpi_time svTimeVal;
|
||||
|
||||
/* time value types */
|
||||
#define sv_scaled_real_time vpiScaledRealTime
|
||||
#define sv_sim_time vpiSimTime
|
||||
|
||||
/*
|
||||
* Implementation-dependent representation.
|
||||
*/
|
||||
@@ -277,7 +297,7 @@ XXTERN svScope svGetScope( void );
|
||||
|
||||
/*
|
||||
* Set context for subsequent export function execution.
|
||||
* This function must be called before calling an export function, unless
|
||||
* This function shall be called before calling an export function, unless
|
||||
* the export function is called while executing an import function. In that
|
||||
* case the export function shall inherit the scope of the surrounding import
|
||||
* function. This is known as the "default scope".
|
||||
@@ -297,7 +317,7 @@ XXTERN svScope svGetScopeFromName(const char* scopeName);
|
||||
|
||||
/*
|
||||
* Store an arbitrary user data pointer for later retrieval by svGetUserData()
|
||||
* The userKey is generated by the user. It must be guaranteed by the user to
|
||||
* The userKey is generated by the user. It needs to be guaranteed by the user to
|
||||
* be unique from all other userKey's for all unique data storage requirements
|
||||
* It is recommended that the address of static functions or variables in the
|
||||
* user's C code be used as the userKey.
|
||||
@@ -329,24 +349,49 @@ XXTERN void* svGetUserData(const svScope scope, void* userKey);
|
||||
* modified. Whether this information is available or not is implementation-
|
||||
* specific. Note that the string provided (if any) is owned by the SV
|
||||
* implementation and is valid only until the next call to any SV function.
|
||||
* Applications must not modify this string or free it
|
||||
* Applications shall not modify this string or free it.
|
||||
*/
|
||||
XXTERN int svGetCallerInfo(const char** fileName, int *lineNumber);
|
||||
|
||||
/*
|
||||
* Returns 1 if the current execution thread is in the disabled state.
|
||||
* Disable protocol must be adhered to if in the disabled state.
|
||||
* Disable protocol shall be adhered to if in the disabled state.
|
||||
*/
|
||||
XXTERN int svIsDisabledState( void );
|
||||
|
||||
/*
|
||||
* Imported functions call this API function during disable processing to
|
||||
* acknowledge that they are correctly participating in the DPI disable protocol.
|
||||
* This function must be called before returning from an imported function that is
|
||||
* This function shall be called before returning from an imported function that is
|
||||
* in the disabled state.
|
||||
*/
|
||||
XXTERN void svAckDisabledState( void );
|
||||
|
||||
/* Mantis 5713/D9
|
||||
* Retrieve the current simulation time, scaled to the time unit of the scope.
|
||||
* If scope is NULL, then time is scaled to the simulation time unit.
|
||||
* It is an error to call svGetTime() with an invalid svScope.
|
||||
* This function returns -1 for all error cases, 0 upon success.
|
||||
*/
|
||||
XXTERN int svGetTime(const svScope scope, svTimeVal* time);
|
||||
|
||||
/*
|
||||
* Retrieve the time unit for scope.
|
||||
* If scope is NULL, then simulation time unit is retrieved.
|
||||
* It is an error to call svGetTimeUnit() with an invalid svScope.
|
||||
* This function returns -1 for all error cases, 0 upon success.
|
||||
*/
|
||||
XXTERN int svGetTimeUnit(const svScope scope, int32_t* time_unit);
|
||||
|
||||
/*
|
||||
* Retrieve the time precision for scope.
|
||||
* If scope is NULL, then simulation time unit is retrieved.
|
||||
* It is an error to call svGetTimePrecision() with an invalid svScope.
|
||||
* This function returns -1 for all error cases, 0 upon success.
|
||||
*/
|
||||
XXTERN int svGetTimePrecision(const svScope scope, int32_t* time_precision);
|
||||
|
||||
|
||||
/*
|
||||
**********************************************************
|
||||
* DEPRECATED PORTION OF FILE STARTS FROM HERE.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*******************************************************************************
|
||||
* vpi_user.h
|
||||
*
|
||||
* IEEE Std 1800-2017 Programming Language Interface (PLI)
|
||||
* IEEE Std 1800-2023 Programming Language Interface (PLI)
|
||||
*
|
||||
* This file contains the constant definitions, structure definitions, and
|
||||
* routine declarations used by the SystemVerilog Verification Procedural
|
||||
@@ -344,6 +344,9 @@ typedef PLI_UINT32 *vpiHandle;
|
||||
#define vpiSupply0 11 /* supply-0 net */
|
||||
#define vpiNone 12 /* no default net type (1364-2001) */
|
||||
#define vpiUwire 13 /* unresolved wire net (1364-2005) */
|
||||
#define vpiNettypeNet 14 /* user-defined nettype net */
|
||||
#define vpiNettypeNetSelect 15 /* user-defined nettype net subelement */
|
||||
#define vpiInterconnect 16 /* interconnect net */
|
||||
|
||||
#define vpiExplicitScalared 23 /* explicitly scalared (Boolean) */
|
||||
#define vpiExplicitVectored 24 /* explicitly vectored (Boolean) */
|
||||
@@ -573,6 +576,9 @@ typedef PLI_UINT32 *vpiHandle;
|
||||
|
||||
/******************************* time structure *******************************/
|
||||
|
||||
#ifndef VPI_TIME /* added in 1800-2023 */
|
||||
#define VPI_TIME
|
||||
|
||||
typedef struct t_vpi_time
|
||||
{
|
||||
PLI_INT32 type; /* [vpiScaledRealTime, vpiSimTime,
|
||||
@@ -587,6 +593,8 @@ typedef struct t_vpi_time
|
||||
#define vpiSimTime 2
|
||||
#define vpiSuppressTime 3
|
||||
|
||||
#endif
|
||||
|
||||
/****************************** delay structures ******************************/
|
||||
|
||||
typedef struct t_vpi_delay
|
||||
@@ -741,9 +749,8 @@ typedef struct t_vpi_arrayvalue
|
||||
typedef struct t_vpi_systf_data
|
||||
{
|
||||
PLI_INT32 type; /* vpiSysTask, vpiSysFunc */
|
||||
PLI_INT32 sysfunctype; /* vpiSysTask, vpi[Int,Real,Time,Sized,
|
||||
SizedSigned]Func */
|
||||
PLI_BYTE8 *tfname; /* first character must be '$' */
|
||||
PLI_INT32 sysfunctype; /* vpi[Int,Real,Time,Sized,SizedSigned]Func */
|
||||
PLI_BYTE8 *tfname; /* first character has to be '$' */
|
||||
PLI_INT32 (*calltf)(PLI_BYTE8 *);
|
||||
PLI_INT32 (*compiletf)(PLI_BYTE8 *);
|
||||
PLI_INT32 (*sizetf)(PLI_BYTE8 *); /* for sized function callbacks only */
|
||||
|
||||
@@ -434,7 +434,7 @@ class CallAnnotationsValidator:
|
||||
self.process_translation_unit(translation_unit)
|
||||
self._main_source_file = ""
|
||||
self._defines = {}
|
||||
else:
|
||||
elif len(errors) != 0:
|
||||
print(f"%Error: parsing failed: {filename}", file=sys.stderr)
|
||||
for error in errors:
|
||||
print(f" {error}", file=sys.stderr)
|
||||
|
||||
@@ -20,6 +20,26 @@ document pnt
|
||||
Verilator: Print AstNode NODEP's tree
|
||||
end
|
||||
|
||||
# Source python-based gdb config with jshow/jdiff definitions
|
||||
# (Stored in separate file, so it can be highlighted/linted/formatted as Python)
|
||||
python
|
||||
import os
|
||||
if "VERILATOR_ROOT" in os.environ:
|
||||
gdbinit_py = os.environ["VERILATOR_ROOT"] + "/src/.gdbinit.py"
|
||||
gdb.execute("source" + gdbinit_py)
|
||||
end
|
||||
|
||||
define jstash
|
||||
call (char*) &(AstNode::dumpTreeJsonGdb($arg0)[0])
|
||||
end
|
||||
document jstash
|
||||
Verilator: Perform a JSON dump of the given AST node and save it in value history (e.g. $1) for later
|
||||
inspection using jtree. The node can be a pointer identifier or an address literal.
|
||||
end
|
||||
|
||||
alias -a js=jstash
|
||||
alias -a jt=jtree
|
||||
|
||||
define dtf
|
||||
call AstNode::dumpTreeFileGdb($arg0, 0)
|
||||
end
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
# pylint: disable=line-too-long,invalid-name,multiple-statements,missing-function-docstring,missing-class-docstring,missing-module-docstring,no-else-return,too-few-public-methods,unused-argument
|
||||
# DESCRIPTION: Verilator: GDB startup file with useful define
|
||||
#
|
||||
# Copyright 2023 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify the Verilator internals 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
|
||||
######################################################################
|
||||
|
||||
import tempfile
|
||||
import gdb # pylint: disable=import-error
|
||||
|
||||
|
||||
def _vltgdb_get_dump(node):
|
||||
gdb.execute(f'set $_gdb_dump_json_str = AstNode::dumpTreeJsonGdb({node})')
|
||||
dump = gdb.execute('printf "%s", $_gdb_dump_json_str', to_string=True)
|
||||
gdb.execute('call free($_gdb_dump_json_str)')
|
||||
return dump
|
||||
|
||||
|
||||
def _vltgdb_tmpfile():
|
||||
return tempfile.NamedTemporaryFile(mode="wt") # write, text mode
|
||||
|
||||
|
||||
def _vltgdb_fwrite(file, s):
|
||||
"""Write to file and flush buffer before passing the file to astsee"""
|
||||
file.write(s)
|
||||
file.flush()
|
||||
|
||||
|
||||
class AstseeCmd(gdb.Command):
|
||||
"""Verilator: Pretty print or diff nodes using `astsee`. A node can be:
|
||||
* an pointer identifier,
|
||||
* an address literal,
|
||||
* a GDB value (like `$1`) that stores a dump previously done by the `jstash` command.
|
||||
Apart from not taking input from a file, it works exactly like `verilator_astsee`:
|
||||
* passing one node gives you a pretty print,
|
||||
* passing two nodes gives you a diff,
|
||||
* for more options see `astsee` readme/help.
|
||||
For examples see internals.rst
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("jtree", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION)
|
||||
|
||||
def _null_check(self, old, new):
|
||||
err = ""
|
||||
if old == "<nullptr>\n": err += "old == <nullptr>\n"
|
||||
if new == "<nullptr>\n": err += "new == <nullptr>"
|
||||
if err: raise gdb.GdbError(err.strip("\n"))
|
||||
|
||||
def invoke(self, arg_str, from_tty):
|
||||
from astsee import verilator_cli as astsee # pylint: disable=import-error,import-outside-toplevel
|
||||
# we import it here so "no module X" error would occur on typing `jtree` rather than on every gdb start
|
||||
|
||||
# We hack `astsee_verilator`'s arg parser to find arguments with nodes
|
||||
# After finding them, we replace them with proper files
|
||||
astsee_args = astsee.parser.parse_args(gdb.string_to_argv(arg_str))
|
||||
with _vltgdb_tmpfile() as oldfile, _vltgdb_tmpfile(
|
||||
) as newfile, _vltgdb_tmpfile() as metafile:
|
||||
if astsee_args.file:
|
||||
_vltgdb_fwrite(oldfile, _vltgdb_get_dump(astsee_args.file))
|
||||
astsee_args.file = oldfile.name
|
||||
if astsee_args.newfile:
|
||||
_vltgdb_fwrite(newfile, _vltgdb_get_dump(astsee_args.newfile))
|
||||
astsee_args.newfile = newfile.name
|
||||
if astsee_args.meta is None:
|
||||
# pass
|
||||
gdb.execute(
|
||||
f'call AstNode::dumpJsonMetaFileGdb("{metafile.name}")')
|
||||
astsee_args.meta = metafile.name
|
||||
try:
|
||||
astsee.main(astsee_args)
|
||||
except SystemExit as e: # astsee prints nice errmsgs on exit(), so rethrow it as GdbError to suppress cryptic python trace
|
||||
raise gdb.GdbError("astsee exited with non-zero code") from e
|
||||
|
||||
|
||||
AstseeCmd()
|
||||
+13
-3
@@ -62,8 +62,10 @@ set(HEADERS
|
||||
V3DepthBlock.h
|
||||
V3Descope.h
|
||||
V3Dfg.h
|
||||
V3DfgCache.h
|
||||
V3DfgOptimizer.h
|
||||
V3DfgPasses.h
|
||||
V3DfgPatternStats.h
|
||||
V3DfgPeephole.h
|
||||
V3DfgVertices.h
|
||||
V3DupFinder.h
|
||||
@@ -77,6 +79,7 @@ set(HEADERS
|
||||
V3EmitV.h
|
||||
V3EmitXml.h
|
||||
V3Error.h
|
||||
V3ExecGraph.h
|
||||
V3Expand.h
|
||||
V3File.h
|
||||
V3FileLine.h
|
||||
@@ -119,6 +122,7 @@ set(HEADERS
|
||||
V3OptionParser.h
|
||||
V3Options.h
|
||||
V3Order.h
|
||||
V3OrderInternal.h
|
||||
V3OrderGraph.h
|
||||
V3OrderMoveGraph.h
|
||||
V3Os.h
|
||||
@@ -127,10 +131,9 @@ set(HEADERS
|
||||
V3Parse.h
|
||||
V3ParseImp.h
|
||||
V3ParseSym.h
|
||||
V3Partition.h
|
||||
V3PartitionGraph.h
|
||||
V3PchAstMT.h
|
||||
V3PchAstNoMT.h
|
||||
V3PreExpr.h
|
||||
V3PreLex.h
|
||||
V3PreProc.h
|
||||
V3PreShell.h
|
||||
@@ -212,11 +215,13 @@ set(COMMON_SOURCES
|
||||
V3Descope.cpp
|
||||
V3Dfg.cpp
|
||||
V3DfgAstToDfg.cpp
|
||||
V3DfgCache.cpp
|
||||
V3DfgDecomposition.cpp
|
||||
V3DfgDfgToAst.cpp
|
||||
V3DfgOptimizer.cpp
|
||||
V3DfgPasses.cpp
|
||||
V3DfgPeephole.cpp
|
||||
V3DfgRegularize.cpp
|
||||
V3DupFinder.cpp
|
||||
V3Timing.cpp
|
||||
V3EmitCBase.cpp
|
||||
@@ -234,6 +239,7 @@ set(COMMON_SOURCES
|
||||
V3EmitV.cpp
|
||||
V3EmitXml.cpp
|
||||
V3Error.cpp
|
||||
V3ExecGraph.cpp
|
||||
V3Expand.cpp
|
||||
V3File.cpp
|
||||
V3FileLine.cpp
|
||||
@@ -270,9 +276,13 @@ set(COMMON_SOURCES
|
||||
V3OptionParser.cpp
|
||||
V3Options.cpp
|
||||
V3Order.cpp
|
||||
V3OrderGraphBuilder.cpp
|
||||
V3OrderMoveGraph.cpp
|
||||
V3OrderParallel.cpp
|
||||
V3OrderProcessDomains.cpp
|
||||
V3OrderSerial.cpp
|
||||
V3Os.cpp
|
||||
V3Param.cpp
|
||||
V3Partition.cpp
|
||||
V3PreShell.cpp
|
||||
V3Premit.cpp
|
||||
V3ProtectLib.cpp
|
||||
|
||||
+8
-1
@@ -228,11 +228,13 @@ RAW_OBJS_PCH_ASTNOMT = \
|
||||
V3Descope.o \
|
||||
V3Dfg.o \
|
||||
V3DfgAstToDfg.o \
|
||||
V3DfgCache.o \
|
||||
V3DfgDecomposition.o \
|
||||
V3DfgDfgToAst.o \
|
||||
V3DfgOptimizer.o \
|
||||
V3DfgPasses.o \
|
||||
V3DfgPeephole.o \
|
||||
V3DfgRegularize.o \
|
||||
V3DupFinder.o \
|
||||
V3EmitCMain.o \
|
||||
V3EmitCMake.o \
|
||||
@@ -240,6 +242,7 @@ RAW_OBJS_PCH_ASTNOMT = \
|
||||
V3EmitCSyms.o \
|
||||
V3EmitMk.o \
|
||||
V3EmitXml.o \
|
||||
V3ExecGraph.o \
|
||||
V3Expand.o \
|
||||
V3Force.o \
|
||||
V3Fork.o \
|
||||
@@ -263,8 +266,12 @@ RAW_OBJS_PCH_ASTNOMT = \
|
||||
V3MergeCond.o \
|
||||
V3Name.o \
|
||||
V3Order.o \
|
||||
V3OrderGraphBuilder.o \
|
||||
V3OrderMoveGraph.o \
|
||||
V3OrderParallel.o \
|
||||
V3OrderProcessDomains.o \
|
||||
V3OrderSerial.o \
|
||||
V3Param.o \
|
||||
V3Partition.o \
|
||||
V3Premit.o \
|
||||
V3ProtectLib.o \
|
||||
V3Randomize.o \
|
||||
|
||||
+5
-6
@@ -97,8 +97,8 @@ protected:
|
||||
result = vertexp->user();
|
||||
break;
|
||||
case LatchDetectGraphVertex::VT_BLOCK: // (OR of potentially many siblings)
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (latchCheckInternal(castVertexp(edgep->top()))) {
|
||||
for (V3GraphEdge& edge : vertexp->outEdges()) {
|
||||
if (latchCheckInternal(castVertexp(edge.top()))) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
@@ -106,9 +106,8 @@ protected:
|
||||
break;
|
||||
case LatchDetectGraphVertex::VT_BRANCH: // (AND of both sibling)
|
||||
// A BRANCH vertex always has exactly 2 siblings
|
||||
LatchDetectGraphVertex* const ifp = castVertexp(vertexp->outBeginp()->top());
|
||||
LatchDetectGraphVertex* const elsp
|
||||
= castVertexp(vertexp->outBeginp()->outNextp()->top());
|
||||
LatchDetectGraphVertex* const ifp = castVertexp(vertexp->outEdges().frontp()->top());
|
||||
LatchDetectGraphVertex* const elsp = castVertexp(vertexp->outEdges().backp()->top());
|
||||
result = latchCheckInternal(ifp) && latchCheckInternal(elsp);
|
||||
break;
|
||||
}
|
||||
@@ -170,7 +169,7 @@ public:
|
||||
for (const auto& vrp : m_outputs) {
|
||||
LatchDetectGraphVertex* const vertp = castVertexp(vrp->varp()->user1p());
|
||||
vertp->user(true); // Identify the output vertex we are checking paths _to_
|
||||
if (!latchCheckInternal(castVertexp(verticesBeginp()))) latch_detected = true;
|
||||
if (!latchCheckInternal(castVertexp(vertices().frontp()))) latch_detected = true;
|
||||
if (latch_detected && !latch_expected) {
|
||||
nodep->v3warn(
|
||||
LATCH,
|
||||
|
||||
@@ -77,6 +77,21 @@ class ActiveTopVisitor final : public VNVisitor {
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete empty procedures
|
||||
for (AstNode *stmtp = nodep->stmtsp(), *nextp; stmtp; stmtp = nextp) {
|
||||
nextp = stmtp->nextp();
|
||||
if (AstNodeProcedure* const procp = VN_CAST(stmtp, NodeProcedure)) {
|
||||
if (!procp->stmtsp()) VL_DO_DANGLING(pushDeletep(procp->unlinkFrBack()), stmtp);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete empty actives
|
||||
if (!nodep->stmtsp()) {
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
// Move the SENTREE for each active up to the global level.
|
||||
// This way we'll easily see what clock domains are identical
|
||||
AstSenTree* const wantp = m_finder.getSenTree(sensesp);
|
||||
|
||||
+1
-1
@@ -170,7 +170,7 @@ class AssertVisitor final : public VNVisitor {
|
||||
if (m_procedurep) {
|
||||
// To support this need queue of asserts to activate
|
||||
nodep->v3error("Unsupported: Procedural concurrent assertion with"
|
||||
" clocking event inside always (IEEE 1800-2017 16.14.6)");
|
||||
" clocking event inside always (IEEE 1800-2023 16.14.6)");
|
||||
}
|
||||
}
|
||||
//
|
||||
|
||||
+16
-13
@@ -115,8 +115,9 @@ private:
|
||||
AstArg* const argp = tconnect.second;
|
||||
AstNode* const pinp = argp->exprp()->unlinkFrBack();
|
||||
replaceVarRefsWithExprRecurse(propExprp, portp, pinp);
|
||||
VL_DO_DANGLING(pushDeletep(pinp), pinp);
|
||||
}
|
||||
// Handle case with 2 disable iff statement (IEEE 1800-2017 16.12.1)
|
||||
// Handle case with 2 disable iff statement (IEEE 1800-2023 16.12.1)
|
||||
if (nodep->disablep() && propExprp->disablep()) {
|
||||
nodep->v3error("disable iff expression before property call and in its "
|
||||
"body is not legal");
|
||||
@@ -160,7 +161,7 @@ private:
|
||||
FileLine* const flp = nodep->fileline();
|
||||
V3Const::constifyEdit(nodep->skewp());
|
||||
if (!VN_IS(nodep->skewp(), Const)) {
|
||||
nodep->skewp()->v3error("Skew must be constant (IEEE 1800-2017 14.4)");
|
||||
nodep->skewp()->v3error("Skew must be constant (IEEE 1800-2023 14.4)");
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
@@ -176,7 +177,7 @@ private:
|
||||
skewedRefp->user1(true);
|
||||
AstAssign* const assignp = new AstAssign{flp, exprp->cloneTreePure(false), skewedRefp};
|
||||
if (skewp->isZero()) {
|
||||
// Drive the var in Re-NBA (IEEE 1800-2017 14.16)
|
||||
// Drive the var in Re-NBA (IEEE 1800-2023 14.16)
|
||||
m_clockingp->addNextHere(new AstAlwaysReactive{
|
||||
flp, new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)}, assignp});
|
||||
} else if (skewp->fileline()->timingOn()) {
|
||||
@@ -206,12 +207,12 @@ private:
|
||||
AstVarRef* const refp = new AstVarRef{flp, varp, VAccess::WRITE};
|
||||
refp->user1(true);
|
||||
if (skewp->num().is1Step()) {
|
||||
// Assign the sampled expression to the clockvar (IEEE 1800-2017 14.13)
|
||||
// Assign the sampled expression to the clockvar (IEEE 1800-2023 14.13)
|
||||
AstSampled* const sampledp = new AstSampled{flp, exprp->cloneTreePure(false)};
|
||||
sampledp->dtypeFrom(exprp);
|
||||
m_clockingp->addNextHere(new AstAssignW{flp, refp, sampledp});
|
||||
} else if (skewp->isZero()) {
|
||||
// #0 means the var has to be sampled in Observed (IEEE 1800-2017 14.13)
|
||||
// #0 means the var has to be sampled in Observed (IEEE 1800-2023 14.13)
|
||||
AstAssign* const assignp = new AstAssign{flp, refp, exprp->cloneTreePure(false)};
|
||||
m_clockingp->addNextHere(new AstAlwaysObserved{
|
||||
flp, new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)}, assignp});
|
||||
@@ -256,13 +257,13 @@ private:
|
||||
if (!nodep->isCycleDelay()) {
|
||||
if (m_inSynchDrive) {
|
||||
nodep->v3error("Only cycle delays can be used in synchronous drives"
|
||||
" (IEEE 1800-2017 14.16)");
|
||||
" (IEEE 1800-2023 14.16)");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (m_inAssign && !m_inSynchDrive) {
|
||||
nodep->v3error("Cycle delays not allowed as intra-assignment delays"
|
||||
" (IEEE 1800-2017 14.11)");
|
||||
" (IEEE 1800-2023 14.11)");
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
@@ -273,12 +274,14 @@ private:
|
||||
if (constp->isZero()) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: ##0 delays");
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
VL_DO_DANGLING(valuep->deleteTree(), valuep);
|
||||
return;
|
||||
}
|
||||
if (!m_defaultClockingp) {
|
||||
nodep->v3error("Usage of cycle delays requires default clocking"
|
||||
" (IEEE 1800-2017 14.11)");
|
||||
" (IEEE 1800-2023 14.11)");
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
VL_DO_DANGLING(valuep->deleteTree(), valuep);
|
||||
return;
|
||||
}
|
||||
AstEventControl* const controlp = new AstEventControl{
|
||||
@@ -302,24 +305,24 @@ private:
|
||||
void visit(AstSenTree* nodep) override {
|
||||
if (m_inSynchDrive) {
|
||||
nodep->v3error("Event controls cannot be used in "
|
||||
"synchronous drives (IEEE 1800-2017 14.16)");
|
||||
"synchronous drives (IEEE 1800-2023 14.16)");
|
||||
}
|
||||
}
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
if (AstClockingItem* const itemp = VN_CAST(nodep->varp()->user1p(), ClockingItem)) {
|
||||
if (nodep->access().isReadOrRW() && !nodep->user1()
|
||||
&& itemp->direction() == VDirection::OUTPUT) {
|
||||
nodep->v3error("Cannot read from output clockvar (IEEE 1800-2017 14.3)");
|
||||
nodep->v3error("Cannot read from output clockvar (IEEE 1800-2023 14.3)");
|
||||
}
|
||||
if (nodep->access().isWriteOrRW()) {
|
||||
if (itemp->direction() == VDirection::OUTPUT) {
|
||||
if (!m_inAssignDlyLhs) {
|
||||
nodep->v3error("Only non-blocking assignments can write "
|
||||
"to clockvars (IEEE 1800-2017 14.16)");
|
||||
"to clockvars (IEEE 1800-2023 14.16)");
|
||||
}
|
||||
if (m_inAssign) m_inSynchDrive = true;
|
||||
} else if (!nodep->user1() && itemp->direction() == VDirection::INPUT) {
|
||||
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2017 14.3)");
|
||||
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -463,7 +466,7 @@ private:
|
||||
if (clockingp->isDefault()) {
|
||||
if (m_defaultClockingp) {
|
||||
clockingp->v3error("Only one default clocking block allowed per module"
|
||||
" (IEEE 1800-2017 14.12)");
|
||||
" (IEEE 1800-2023 14.12)");
|
||||
}
|
||||
m_defaultClockingp = clockingp;
|
||||
}
|
||||
|
||||
+54
-21
@@ -17,7 +17,6 @@
|
||||
#include "V3PchAstMT.h"
|
||||
|
||||
#include "V3Broken.h"
|
||||
#include "V3EmitV.h"
|
||||
#include "V3File.h"
|
||||
|
||||
#include <iomanip>
|
||||
@@ -1171,8 +1170,50 @@ void AstNode::checkTreeIter(const AstNode* prevBackp) const VL_MT_STABLE {
|
||||
default: this->v3fatalSrc("Bad case"); break;
|
||||
}
|
||||
}
|
||||
if (v3Global.opt.debugWidth() && v3Global.widthMinUsage() == VWidthMinUsage::VERILOG_WIDTH) {
|
||||
if (const AstNodeExpr* const exprp = VN_CAST(this, NodeExpr)) {
|
||||
const char* const whyp = exprp->widthMismatch();
|
||||
if (whyp) {
|
||||
auto dtypeStr = [](const AstNodeExpr* exprp) VL_MT_STABLE {
|
||||
std::ostringstream ss;
|
||||
exprp->dtypep()->dumpSmall(ss);
|
||||
return ss.str();
|
||||
};
|
||||
if (const AstNodeUniop* const uniopp = VN_CAST(exprp, NodeUniop)) {
|
||||
UASSERT_OBJ(!whyp, uniopp,
|
||||
"widthMismatch detected " << whyp << "OUT:" << dtypeStr(uniopp)
|
||||
<< " LHS:" << dtypeStr(uniopp->lhsp()));
|
||||
} else if (const AstNodeBiop* const biopp = VN_CAST(exprp, NodeBiop)) {
|
||||
UASSERT_OBJ(!whyp, biopp,
|
||||
"widthMismatch detected " << whyp << "OUT:" << dtypeStr(biopp)
|
||||
<< " LHS:" << dtypeStr(biopp->lhsp())
|
||||
<< " RHS:" << dtypeStr(biopp->rhsp()));
|
||||
} else {
|
||||
UASSERT_OBJ(false, exprp,
|
||||
"widthMismatch detected " << whyp << " in an unexpected type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cppcheck-suppress unusedFunction // Debug only
|
||||
char* AstNode::dumpTreeJsonGdb(const AstNode* nodep) {
|
||||
if (!nodep) return strdup("{\"addr\":\"NULL\"}\n");
|
||||
std::stringstream nodepStream;
|
||||
nodep->dumpTreeJson(nodepStream);
|
||||
const std::string str = nodepStream.rdbuf()->str();
|
||||
return strdup(str.c_str());
|
||||
}
|
||||
// cppcheck-suppress unusedFunction // Debug only
|
||||
// identity func to allow for passing already done dumps to jtree
|
||||
char* AstNode::dumpTreeJsonGdb(const char* str) { return strdup(str); }
|
||||
// cppcheck-suppress unusedFunction // Debug only
|
||||
// allow for passing pointer literals like 0x42.. without manual cast
|
||||
char* AstNode::dumpTreeJsonGdb(intptr_t nodep) {
|
||||
if (!nodep) return strdup("{\"addr\":\"NULL\"}\n");
|
||||
return dumpTreeJsonGdb((const AstNode*)nodep);
|
||||
}
|
||||
// cppcheck-suppress unusedFunction // Debug only
|
||||
void AstNode::dumpGdb(const AstNode* nodep) { // For GDB only // LCOV_EXCL_LINE
|
||||
if (!nodep) {
|
||||
@@ -1276,7 +1317,7 @@ void AstNode::dumpTreeAndNext(std::ostream& os, const string& indent, int maxDep
|
||||
}
|
||||
}
|
||||
|
||||
void AstNode::dumpTreeFile(const string& filename, bool doDump, bool doCheck) {
|
||||
void AstNode::dumpTreeFile(const string& filename, bool doDump) {
|
||||
// Not const function as calls checkTree
|
||||
if (doDump) {
|
||||
{ // Write log & close
|
||||
@@ -1294,14 +1335,6 @@ void AstNode::dumpTreeFile(const string& filename, bool doDump, bool doCheck) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (doDump && v3Global.opt.debugEmitV()) V3EmitV::debugEmitV(filename + ".v");
|
||||
if (doCheck && (v3Global.opt.debugCheck() || ::dumpTreeLevel())) {
|
||||
// Error check
|
||||
checkTree();
|
||||
// Broken isn't part of check tree because it can munge iterp's
|
||||
// set by other steps if it is called in the middle of other operations
|
||||
if (AstNetlist* const netp = VN_CAST(this, Netlist)) V3Broken::brokenAll(netp);
|
||||
}
|
||||
}
|
||||
|
||||
static void drawChildren(std::ostream& os, const AstNode* thisp, const AstNode* childp,
|
||||
@@ -1340,10 +1373,11 @@ void AstNode::dumpTreeJsonFile(const string& filename, bool doDump) {
|
||||
*treejsonp << '\n';
|
||||
}
|
||||
|
||||
void AstNode::dumpJsonMetaFileGdb(const char* filename) { dumpJsonMetaFile(filename); }
|
||||
void AstNode::dumpJsonMetaFile(const string& filename) {
|
||||
UINFO(2, "Dumping " << filename << endl);
|
||||
const std::unique_ptr<std::ofstream> treejsonp{V3File::new_ofstream(filename)};
|
||||
if (treejsonp->fail()) v3fatal("Can't write " << filename);
|
||||
if (treejsonp->fail()) v3fatalStatic("Can't write " << filename);
|
||||
*treejsonp << '{';
|
||||
FileLine::fileNameNumMapDumpJson(*treejsonp);
|
||||
*treejsonp << ',';
|
||||
@@ -1530,8 +1564,7 @@ static VCastable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDTyp
|
||||
return VCastable::ENUM_IMPLICIT;
|
||||
if (fromNumericable) return VCastable::ENUM_EXPLICIT;
|
||||
} else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromConstp, Const)) {
|
||||
if (VN_IS(fromConstp, Const) && VN_AS(fromConstp, Const)->num().isNull())
|
||||
return VCastable::COMPATIBLE;
|
||||
if (fromConstp->isNull()) return VCastable::COMPATIBLE;
|
||||
} else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromDtp, ClassRefDType)) {
|
||||
const auto toClassp = VN_AS(toDtp, ClassRefDType)->classp();
|
||||
const auto fromClassp = VN_AS(fromDtp, ClassRefDType)->classp();
|
||||
@@ -1557,26 +1590,26 @@ VCastable AstNode::computeCastable(const AstNodeDType* toDtp, const AstNodeDType
|
||||
return castable;
|
||||
}
|
||||
|
||||
AstNodeDType* AstNode::getCommonClassTypep(AstNode* nodep1, AstNode* nodep2) {
|
||||
// Return the class type that both nodep1 and nodep2 are castable to.
|
||||
AstNodeDType* AstNode::getCommonClassTypep(AstNode* node1p, AstNode* node2p) {
|
||||
// Return the class type that both node1p and node2p are castable to.
|
||||
// If both are null, return the type of null constant.
|
||||
// If one is a class and one is null, return AstClassRefDType that points to that class.
|
||||
// If no common class type exists, return nullptr.
|
||||
|
||||
// First handle cases with null values and when one class is a super class of the other.
|
||||
if (VN_IS(nodep1, Const)) std::swap(nodep1, nodep2);
|
||||
if (VN_IS(node1p, Const)) std::swap(node1p, node2p);
|
||||
{
|
||||
const VCastable castable = computeCastable(nodep1->dtypep(), nodep2->dtypep(), nodep2);
|
||||
const VCastable castable = computeCastable(node1p->dtypep(), node2p->dtypep(), node2p);
|
||||
if (castable == VCastable::SAMEISH || castable == VCastable::COMPATIBLE) {
|
||||
return nodep1->dtypep();
|
||||
return node1p->dtypep();
|
||||
} else if (castable == VCastable::DYNAMIC_CLASS) {
|
||||
return nodep2->dtypep();
|
||||
return node2p->dtypep();
|
||||
}
|
||||
}
|
||||
|
||||
AstClassRefDType* classDtypep1 = VN_CAST(nodep1->dtypep(), ClassRefDType);
|
||||
AstClassRefDType* classDtypep1 = VN_CAST(node1p->dtypep(), ClassRefDType);
|
||||
while (classDtypep1) {
|
||||
const VCastable castable = computeCastable(classDtypep1, nodep2->dtypep(), nodep2);
|
||||
const VCastable castable = computeCastable(classDtypep1, node2p->dtypep(), node2p);
|
||||
if (castable == VCastable::COMPATIBLE) return classDtypep1;
|
||||
AstClassExtends* const extendsp = classDtypep1->classp()->extendsp();
|
||||
classDtypep1 = extendsp ? VN_AS(extendsp->dtypep(), ClassRefDType) : nullptr;
|
||||
|
||||
+58
-17
@@ -49,9 +49,6 @@ class VFlagLogicPacked {};
|
||||
class VFlagBitPacked {};
|
||||
class VFlagChildDType {}; // Used by parser.y to select constructor that sets childDType
|
||||
|
||||
// Used as key for another map, needs operator<, hence not an unordered_set
|
||||
using MTaskIdSet = std::set<int>; // Set of mtaskIds for Var sorting
|
||||
|
||||
//######################################################################
|
||||
|
||||
// For broken() function, return error string if have a match
|
||||
@@ -245,6 +242,51 @@ inline std::ostream& operator<<(std::ostream& os, const VAccess& rhs) { return o
|
||||
|
||||
// ######################################################################
|
||||
|
||||
class VBaseOverride final {
|
||||
bool m_extends : 1;
|
||||
bool m_final : 1;
|
||||
bool m_initial : 1;
|
||||
|
||||
public:
|
||||
VBaseOverride()
|
||||
: m_extends{false}
|
||||
, m_final{false}
|
||||
, m_initial{false} {}
|
||||
class Extends {};
|
||||
VBaseOverride(Extends)
|
||||
: m_extends{true}
|
||||
, m_final{false}
|
||||
, m_initial{false} {}
|
||||
class Final {};
|
||||
VBaseOverride(Final)
|
||||
: m_extends{false}
|
||||
, m_final{true}
|
||||
, m_initial{false} {}
|
||||
class Initial {};
|
||||
VBaseOverride(Initial)
|
||||
: m_extends{false}
|
||||
, m_final{false}
|
||||
, m_initial{true} {}
|
||||
void combine(const VBaseOverride& other) {
|
||||
m_extends |= other.m_extends;
|
||||
m_final |= other.m_final;
|
||||
m_initial |= other.m_initial;
|
||||
}
|
||||
bool isAny() const { return m_extends | m_final | m_initial; }
|
||||
bool isExtends() const { return m_extends; }
|
||||
bool isFinal() const { return m_final; }
|
||||
bool isInitial() const { return m_initial; }
|
||||
string ascii() const {
|
||||
string out;
|
||||
if (m_initial) out = VString::dot(out, " ", "initial");
|
||||
if (m_extends) out = VString::dot(out, " ", "extends");
|
||||
if (m_final) out = VString::dot(out, " ", "final");
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
// ######################################################################
|
||||
|
||||
class VSigning final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
@@ -323,7 +365,6 @@ public:
|
||||
enum en : uint8_t {
|
||||
// These must be in general -> most specific order, as we sort by it
|
||||
// in V3Const::visit AstSenTree
|
||||
ET_ILLEGAL,
|
||||
// Involving a variable
|
||||
ET_CHANGED, // Value changed
|
||||
ET_BOTHEDGE, // POSEDGE | NEGEDGE (i.e.: 'edge' in Verilog)
|
||||
@@ -343,8 +384,6 @@ public:
|
||||
enum en m_e;
|
||||
bool clockedStmt() const {
|
||||
static const bool clocked[] = {
|
||||
false, // ET_ILLEGAL
|
||||
|
||||
true, // ET_CHANGED
|
||||
true, // ET_BOTHEDGE
|
||||
true, // ET_POSEDGE
|
||||
@@ -368,20 +407,19 @@ public:
|
||||
case ET_BOTHEDGE: return ET_BOTHEDGE;
|
||||
case ET_POSEDGE: return ET_NEGEDGE;
|
||||
case ET_NEGEDGE: return ET_POSEDGE;
|
||||
default: UASSERT_STATIC(0, "Inverting bad edgeType()");
|
||||
default: UASSERT_STATIC(0, "Inverting bad edgeType()"); return ET_NEGEDGE;
|
||||
}
|
||||
return VEdgeType::ET_ILLEGAL;
|
||||
}
|
||||
const char* ascii() const {
|
||||
static const char* const names[]
|
||||
= {"%E-edge", "CHANGED", "BOTH", "POS", "NEG", "EVENT", "TRUE",
|
||||
"COMBO", "HYBRID", "STATIC", "INITIAL", "FINAL", "NEVER"};
|
||||
= {"CHANGED", "BOTH", "POS", "NEG", "EVENT", "TRUE",
|
||||
"COMBO", "HYBRID", "STATIC", "INITIAL", "FINAL", "NEVER"};
|
||||
return names[m_e];
|
||||
}
|
||||
const char* verilogKwd() const {
|
||||
static const char* const names[]
|
||||
= {"%E-edge", "[changed]", "edge", "posedge", "negedge", "[event]", "[true]",
|
||||
"*", "[hybrid]", "[static]", "[initial]", "[final]", "[never]"};
|
||||
= {"[changed]", "edge", "posedge", "negedge", "[event]", "[true]",
|
||||
"*", "[hybrid]", "[static]", "[initial]", "[final]", "[never]"};
|
||||
return names[m_e];
|
||||
}
|
||||
// Return true iff this and the other have mutually exclusive transitions
|
||||
@@ -397,8 +435,6 @@ public:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
VEdgeType()
|
||||
: m_e{ET_ILLEGAL} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VEdgeType(en _e)
|
||||
: m_e{_e} {}
|
||||
@@ -2002,7 +2038,7 @@ public:
|
||||
|
||||
// TODO stomp these width functions out, and call via dtypep() instead
|
||||
inline int width() const VL_MT_STABLE;
|
||||
inline int widthMin() const;
|
||||
inline int widthMin() const VL_MT_STABLE;
|
||||
int widthMinV() const {
|
||||
return v3Global.widthMinUsage() == VWidthMinUsage::VERILOG_WIDTH ? widthMin() : width();
|
||||
}
|
||||
@@ -2088,6 +2124,7 @@ public:
|
||||
// ACCESSORS for specific types
|
||||
// Alas these can't be virtual or they break when passed a nullptr
|
||||
inline bool isClassHandleValue() const;
|
||||
inline bool isNull() const;
|
||||
inline bool isZero() const;
|
||||
inline bool isOne() const;
|
||||
inline bool isNeqZero() const;
|
||||
@@ -2166,6 +2203,9 @@ public:
|
||||
string warnOther() const VL_REQUIRES(V3Error::s().m_mutex) { return fileline()->warnOther(); }
|
||||
|
||||
virtual void dump(std::ostream& str = std::cout) const;
|
||||
static char* dumpTreeJsonGdb(const AstNode* nodep); // For GDB only, free()d by caller
|
||||
static char* dumpTreeJsonGdb(const char* str); // For GDB only, free()d by caller
|
||||
static char* dumpTreeJsonGdb(intptr_t nodep); // For GDB only, free()d by caller
|
||||
static void dumpGdb(const AstNode* nodep); // For GDB only
|
||||
void dumpGdbHeader() const;
|
||||
|
||||
@@ -2216,7 +2256,7 @@ public:
|
||||
static void dumpTreeGdb(const AstNode* nodep); // For GDB only
|
||||
void dumpTreeAndNext(std::ostream& os = std::cout, const string& indent = " ",
|
||||
int maxDepth = 0) const;
|
||||
void dumpTreeFile(const string& filename, bool doDump = true, bool doCheck = true);
|
||||
void dumpTreeFile(const string& filename, bool doDump = true);
|
||||
static void dumpTreeFileGdb(const AstNode* nodep, const char* filenamep = nullptr);
|
||||
void dumpTreeDot(std::ostream& os = std::cout) const;
|
||||
void dumpTreeDotFile(const string& filename, bool doDump = true);
|
||||
@@ -2227,7 +2267,8 @@ public:
|
||||
virtual void dumpTreeJsonOpGen(std::ostream& os, const string& indent) const {};
|
||||
void dumpTreeJson(std::ostream& os, const string& indent = "") const;
|
||||
void dumpTreeJsonFile(const string& filename, bool doDump = true);
|
||||
void dumpJsonMetaFile(const string& filename);
|
||||
static void dumpJsonMetaFileGdb(const char* filename);
|
||||
static void dumpJsonMetaFile(const string& filename);
|
||||
|
||||
// Render node address for dumps. By default this is just the address
|
||||
// printed as hex, but with --dump-tree-addrids we map addresses to short
|
||||
|
||||
+3
-2
@@ -26,7 +26,7 @@
|
||||
// Inline METHODS
|
||||
|
||||
int AstNode::width() const VL_MT_STABLE { return dtypep() ? dtypep()->width() : 0; }
|
||||
int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() : 0; }
|
||||
int AstNode::widthMin() const VL_MT_STABLE { return dtypep() ? dtypep()->widthMin() : 0; }
|
||||
bool AstNode::width1() const { // V3Const uses to know it can optimize
|
||||
return dtypep() && dtypep()->width() == 1;
|
||||
}
|
||||
@@ -47,8 +47,9 @@ bool AstNode::isSigned() const VL_MT_STABLE { return dtypep() && dtypep()->isSig
|
||||
|
||||
bool AstNode::isClassHandleValue() const {
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isNull())
|
||||
|| VN_IS(dtypep(), ClassRefDType);
|
||||
|| (dtypep() && VN_IS(dtypep()->skipRefp(), ClassRefDType));
|
||||
}
|
||||
bool AstNode::isNull() const { return VN_IS(this, Const) && VN_AS(this, Const)->num().isNull(); }
|
||||
bool AstNode::isZero() const {
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqZero());
|
||||
}
|
||||
|
||||
+15
-9
@@ -53,7 +53,7 @@ public:
|
||||
// ACCESSORS
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
virtual void dumpSmall(std::ostream& str) const;
|
||||
virtual void dumpSmall(std::ostream& str) const VL_MT_STABLE;
|
||||
bool hasDType() const override { return true; }
|
||||
/// Require VlUnpacked, instead of [] for POD elements.
|
||||
/// A non-POD object is always compound, but some POD elements
|
||||
@@ -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;
|
||||
string cType(const string& name, bool forFunc, bool isRef, bool packed = false) const;
|
||||
// Represents a C++ LiteralType? (can be constexpr)
|
||||
bool isLiteralType() const VL_MT_STABLE;
|
||||
|
||||
private:
|
||||
class CTypeRecursed;
|
||||
CTypeRecursed cTypeRecurse(bool compound) const;
|
||||
CTypeRecursed cTypeRecurse(bool compound, bool packed) const;
|
||||
};
|
||||
class AstNodeArrayDType VL_NOT_FINAL : public AstNodeDType {
|
||||
// Array data type, ie "some_dtype var_name [2:0]"
|
||||
@@ -469,7 +469,7 @@ public:
|
||||
this->elementsp(elementsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstBracketArrayDType;
|
||||
bool similarDType(const AstNodeDType* samep) const override { V3ERROR_NA_RETURN(false); }
|
||||
bool similarDType(const AstNodeDType* samep) const override { return same(samep); }
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE { return childDTypep(); }
|
||||
// METHODS
|
||||
// Will be removed in V3Width, which relies on this
|
||||
@@ -510,11 +510,17 @@ public:
|
||||
int widthAlignBytes() const override { return 8; } // Assume
|
||||
int widthTotalBytes() const override { return 8; } // Assume
|
||||
bool isCompound() const override { return true; }
|
||||
static string typeToHold(uint64_t maxItem) {
|
||||
return (maxItem < (1ULL << 8)) ? "CData"
|
||||
: (maxItem < (1ULL << 16)) ? "SData"
|
||||
: (maxItem < (1ULL << 32)) ? "IData"
|
||||
: "QData";
|
||||
static string typeToHold(int width) {
|
||||
if (width <= 8)
|
||||
return "CData";
|
||||
else if (width <= 16)
|
||||
return "SData";
|
||||
else if (width <= VL_IDATASIZE)
|
||||
return "IData";
|
||||
else if (width <= VL_QUADSIZE)
|
||||
return "QData";
|
||||
else
|
||||
return "VlWide<" + std::to_string(VL_WORDS_I(width)) + ">";
|
||||
}
|
||||
};
|
||||
class AstClassRefDType final : public AstNodeDType {
|
||||
|
||||
@@ -65,6 +65,9 @@ public:
|
||||
|
||||
// Wrap This expression into an AstStmtExpr to denote it occurs in statement position
|
||||
inline AstStmtExpr* makeStmt();
|
||||
// Returns an error message if widthMin() is not correct otherwise returns nullptr like
|
||||
// broken()
|
||||
virtual const char* widthMismatch() const VL_MT_STABLE { return nullptr; }
|
||||
};
|
||||
class AstNodeBiop VL_NOT_FINAL : public AstNodeExpr {
|
||||
// Binary expression
|
||||
@@ -1109,6 +1112,34 @@ public:
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
};
|
||||
class AstCvtPackedToUnpackArray final : public AstNodeExpr {
|
||||
// Cast from packed array to dynamic queue data type
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
public:
|
||||
AstCvtPackedToUnpackArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_CvtPackedToUnpackArray(fl) {
|
||||
this->fromp(fromp);
|
||||
dtypeFrom(dtp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCvtPackedToUnpackArray;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
};
|
||||
class AstCvtUnpackArrayToPacked final : public AstNodeExpr {
|
||||
// Cast from dynamic queue data type to packed array
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
public:
|
||||
AstCvtUnpackArrayToPacked(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_CvtUnpackArrayToPacked(fl) {
|
||||
this->fromp(fromp);
|
||||
dtypeFrom(dtp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCvtUnpackArrayToPacked;
|
||||
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
|
||||
@@ -3830,6 +3861,7 @@ public:
|
||||
bool cleanRhs() const override { return false; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
const char* widthMismatch() const override VL_MT_STABLE;
|
||||
};
|
||||
class AstMul final : public AstNodeBiComAsv {
|
||||
public:
|
||||
@@ -3924,6 +3956,7 @@ public:
|
||||
bool cleanRhs() const override { return false; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
const char* widthMismatch() const override VL_MT_STABLE;
|
||||
};
|
||||
class AstXor final : public AstNodeBiComAsv {
|
||||
public:
|
||||
@@ -3946,6 +3979,7 @@ public:
|
||||
bool cleanRhs() const override { return false; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
const char* widthMismatch() const override VL_MT_STABLE;
|
||||
};
|
||||
|
||||
// === AstNodeDistBiop ===
|
||||
@@ -5082,6 +5116,7 @@ public:
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return false; }
|
||||
bool sizeMattersLhs() const override { return true; }
|
||||
const char* widthMismatch() const override VL_MT_STABLE;
|
||||
};
|
||||
class AstNullCheck final : public AstNodeUniop {
|
||||
// Return LHS after checking that LHS is non-null
|
||||
|
||||
+41
-26
@@ -76,7 +76,6 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
|
||||
bool m_dpiContext : 1; // DPI import context
|
||||
bool m_dpiOpenChild : 1; // DPI import open array child wrapper
|
||||
bool m_dpiTask : 1; // DPI import task (vs. void function)
|
||||
bool m_dpiTraceInit : 1; // DPI trace_init
|
||||
bool m_isConstructor : 1; // Class constructor
|
||||
bool m_isHideLocal : 1; // Verilog local
|
||||
bool m_isHideProtected : 1; // Verilog protected
|
||||
@@ -87,6 +86,7 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
|
||||
bool m_underGenerate : 1; // Under generate (for warning)
|
||||
bool m_virtual : 1; // Virtual method in class
|
||||
bool m_needProcess : 1; // Needs access to VlProcess of the caller
|
||||
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
|
||||
VLifetime m_lifetime; // Default lifetime of local vars
|
||||
VIsCached m_purity; // Pure state
|
||||
|
||||
@@ -105,7 +105,6 @@ protected:
|
||||
, m_dpiContext{false}
|
||||
, m_dpiOpenChild{false}
|
||||
, m_dpiTask{false}
|
||||
, m_dpiTraceInit{false}
|
||||
, m_isConstructor{false}
|
||||
, m_isHideLocal{false}
|
||||
, m_isHideProtected{false}
|
||||
@@ -161,8 +160,6 @@ public:
|
||||
bool dpiOpenChild() const { return m_dpiOpenChild; }
|
||||
void dpiTask(bool flag) { m_dpiTask = flag; }
|
||||
bool dpiTask() const { return m_dpiTask; }
|
||||
void dpiTraceInit(bool flag) { m_dpiTraceInit = flag; }
|
||||
bool dpiTraceInit() const { return m_dpiTraceInit; }
|
||||
void isConstructor(bool flag) { m_isConstructor = flag; }
|
||||
bool isConstructor() const { return m_isConstructor; }
|
||||
bool isHideLocal() const { return m_isHideLocal; }
|
||||
@@ -183,6 +180,8 @@ public:
|
||||
bool isVirtual() const { return m_virtual; }
|
||||
void setNeedProcess() { m_needProcess = true; }
|
||||
bool needProcess() const { return m_needProcess; }
|
||||
void baseOverride(const VBaseOverride& flag) { m_baseOverride = flag; }
|
||||
VBaseOverride baseOverride() const { return m_baseOverride; }
|
||||
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
||||
VLifetime lifetime() const { return m_lifetime; }
|
||||
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
|
||||
@@ -627,7 +626,6 @@ class AstCFunc final : public AstNode {
|
||||
bool m_dpiExportImpl : 1; // DPI export implementation (called from DPI dispatcher via lookup)
|
||||
bool m_dpiImportPrototype : 1; // This is the DPI import prototype (i.e.: provided by user)
|
||||
bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code
|
||||
bool m_dpiTraceInit : 1; // DPI trace_init
|
||||
bool m_needProcess : 1; // Needs access to VlProcess of the caller
|
||||
bool m_recursive : 1; // Recursive or part of recursion
|
||||
public:
|
||||
@@ -657,7 +655,6 @@ public:
|
||||
m_dpiExportImpl = false;
|
||||
m_dpiImportPrototype = false;
|
||||
m_dpiImportWrapper = false;
|
||||
m_dpiTraceInit = false;
|
||||
m_recursive = false;
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCFunc;
|
||||
@@ -731,8 +728,6 @@ public:
|
||||
void dpiImportPrototype(bool flag) { m_dpiImportPrototype = flag; }
|
||||
bool dpiImportWrapper() const { return m_dpiImportWrapper; }
|
||||
void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; }
|
||||
void dpiTraceInit(bool flag) { m_dpiTraceInit = flag; }
|
||||
bool dpiTraceInit() const { return m_dpiTraceInit; }
|
||||
bool isCoroutine() const { return m_rtnType == "VlCoroutine"; }
|
||||
void recursive(bool flag) { m_recursive = flag; }
|
||||
bool recursive() const { return m_recursive; }
|
||||
@@ -863,13 +858,42 @@ public:
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
// ACCESSORS
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Cell name
|
||||
bool maybePointedTo() const override { return true; }
|
||||
string origModName() const { return m_origModName; } // * = modp()->origName() before inlining
|
||||
void name(const string& name) override { m_name = name; }
|
||||
void scopep(AstScope* scp) { m_scopep = scp; }
|
||||
AstScope* scopep() const { return m_scopep; }
|
||||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
VTimescale timeunit() const { return m_timeunit; }
|
||||
};
|
||||
|
||||
class AstCellInlineScope final : public AstNode {
|
||||
// A particular scoped usage of a Cell Inline
|
||||
// Parents: Scope
|
||||
// Children: none
|
||||
//
|
||||
// @astgen ptr := m_scopep : Optional[AstScope] // Scope variable is underneath
|
||||
// @astgen ptr := m_cellp : Optional[AstCellInline] // Cell ref
|
||||
const string m_origModName; // Original name of module, ignoring name() changes, for LinkDot
|
||||
public:
|
||||
AstCellInlineScope(FileLine* fl, AstScope* scopep, AstCellInline* cellp)
|
||||
: ASTGEN_SUPER_CellInlineScope(fl)
|
||||
, m_scopep{scopep}
|
||||
, m_cellp{cellp} {
|
||||
UASSERT_OBJ(scopep, fl, "Scope must be non-null");
|
||||
UASSERT_OBJ(cellp, fl, "CellInline must be non-null");
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCellInlineScope;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
// ACCESSORS
|
||||
string name() const override VL_MT_STABLE { return m_cellp->name(); }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
AstScope* scopep() const VL_MT_STABLE { return m_scopep; } // Pointer to scope it's under
|
||||
string origModName() const {
|
||||
return m_cellp->origModName();
|
||||
} // * = modp()->origName() before inlining
|
||||
void scopep(AstScope* nodep) { m_scopep = nodep; }
|
||||
};
|
||||
|
||||
class AstClassExtends final : public AstNode {
|
||||
// class extends class name, or class implements class name
|
||||
// Children: List of AstParseRef for packages/classes
|
||||
@@ -1115,8 +1139,9 @@ class AstExecGraph final : public AstNode {
|
||||
|
||||
public:
|
||||
explicit AstExecGraph(FileLine* fl, const string& name) VL_MT_DISABLED;
|
||||
ASTGEN_MEMBERS_AstExecGraph;
|
||||
~AstExecGraph() override;
|
||||
ASTGEN_MEMBERS_AstExecGraph;
|
||||
void cloneRelink() override { V3ERROR_NA; }
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(!m_depGraphp);
|
||||
return nullptr;
|
||||
@@ -1170,6 +1195,7 @@ public:
|
||||
explicit AstMTaskBody(FileLine* fl)
|
||||
: ASTGEN_SUPER_MTaskBody(fl) {}
|
||||
ASTGEN_MEMBERS_AstMTaskBody;
|
||||
void cloneRelink() override { V3ERROR_NA; }
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(!m_execMTaskp);
|
||||
return nullptr;
|
||||
@@ -1267,9 +1293,6 @@ class AstNetlist final : public AstNode {
|
||||
VTimescale m_timeunit; // Global time unit
|
||||
VTimescale m_timeprecision; // Global time precision
|
||||
bool m_timescaleSpecified = false; // Input HDL specified timescale
|
||||
uint32_t m_nextFreeMTaskID = 1; // Next unique MTask ID within netlist
|
||||
// starts at 1 so 0 means no MTask ID
|
||||
uint32_t m_nextFreeMTaskProfilingID = 0; // Next unique ID to use for PGO
|
||||
public:
|
||||
AstNetlist();
|
||||
ASTGEN_MEMBERS_AstNetlist;
|
||||
@@ -1310,9 +1333,6 @@ public:
|
||||
void timeprecisionMerge(FileLine*, const VTimescale& value);
|
||||
void timescaleSpecified(bool specified) { m_timescaleSpecified = specified; }
|
||||
bool timescaleSpecified() const { return m_timescaleSpecified; }
|
||||
uint32_t allocNextMTaskID() { return m_nextFreeMTaskID++; }
|
||||
uint32_t allocNextMTaskProfilingID() { return m_nextFreeMTaskProfilingID++; }
|
||||
uint32_t usedMTaskProfilingIDs() const { return m_nextFreeMTaskProfilingID; }
|
||||
};
|
||||
class AstPackageExport final : public AstNode {
|
||||
// A package export declaration
|
||||
@@ -1469,6 +1489,7 @@ class AstScope final : public AstNode {
|
||||
// Children: NODEBLOCK
|
||||
// @astgen op1 := varsp : List[AstVarScope]
|
||||
// @astgen op2 := blocksp : List[AstNode] // Logic blocks/AstActive/AstCFunc
|
||||
// @astgen op3 := inlinesp : List[AstCellInlineScope] // Cell Inlines
|
||||
//
|
||||
// Below scope and cell are nullptr if top scope
|
||||
// @astgen ptr := m_aboveScopep : Optional[AstScope] // Scope above this one in the hierarchy
|
||||
@@ -1514,7 +1535,6 @@ class AstSenItem final : public AstNode {
|
||||
VEdgeType m_edgeType; // Edge type
|
||||
public:
|
||||
class Combo {}; // for constructor type-overload selection
|
||||
class Illegal {}; // for constructor type-overload selection
|
||||
class Static {}; // for constructor type-overload selection
|
||||
class Initial {}; // for constructor type-overload selection
|
||||
class Final {}; // for constructor type-overload selection
|
||||
@@ -1528,9 +1548,6 @@ public:
|
||||
AstSenItem(FileLine* fl, Combo)
|
||||
: ASTGEN_SUPER_SenItem(fl)
|
||||
, m_edgeType{VEdgeType::ET_COMBO} {}
|
||||
AstSenItem(FileLine* fl, Illegal)
|
||||
: ASTGEN_SUPER_SenItem(fl)
|
||||
, m_edgeType{VEdgeType::ET_ILLEGAL} {}
|
||||
AstSenItem(FileLine* fl, Static)
|
||||
: ASTGEN_SUPER_SenItem(fl)
|
||||
, m_edgeType{VEdgeType::ET_STATIC} {}
|
||||
@@ -1562,7 +1579,6 @@ public:
|
||||
bool isStatic() const { return edgeType() == VEdgeType::ET_STATIC; }
|
||||
bool isInitial() const { return edgeType() == VEdgeType::ET_INITIAL; }
|
||||
bool isFinal() const { return edgeType() == VEdgeType::ET_FINAL; }
|
||||
bool isIllegal() const { return edgeType() == VEdgeType::ET_ILLEGAL; }
|
||||
bool isNever() const { return edgeType() == VEdgeType::ET_NEVER; }
|
||||
};
|
||||
class AstSenTree final : public AstNode {
|
||||
@@ -1751,7 +1767,6 @@ class AstVar final : public AstNode {
|
||||
VDirection m_declDirection; // Declared direction input/output etc
|
||||
VLifetime m_lifetime; // Lifetime
|
||||
VVarAttrClocker m_attrClocker;
|
||||
MTaskIdSet m_mtaskIds; // MTaskID's that read or write this var
|
||||
int m_pinNum = 0; // For XML, if non-zero the connection pin number
|
||||
bool m_ansi : 1; // Params or pins declared in the module header, rather than the body
|
||||
bool m_declTyped : 1; // Declared as type (for dedup check)
|
||||
@@ -2105,9 +2120,6 @@ public:
|
||||
m_name = name;
|
||||
}
|
||||
static AstVar* scVarRecurse(AstNode* nodep);
|
||||
void addProducingMTaskId(int id) { m_mtaskIds.insert(id); }
|
||||
void addConsumingMTaskId(int id) { m_mtaskIds.insert(id); }
|
||||
const MTaskIdSet& mtaskIds() const { return m_mtaskIds; }
|
||||
void pinNum(int id) { m_pinNum = id; }
|
||||
int pinNum() const { return m_pinNum; }
|
||||
};
|
||||
@@ -2285,6 +2297,7 @@ class AstClass final : public AstNodeModule {
|
||||
// @astgen op4 := extendsp : List[AstClassExtends]
|
||||
// MEMBERS
|
||||
// @astgen ptr := m_classOrPackagep : Optional[AstClassPackage] // Package to be emitted with
|
||||
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
|
||||
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
|
||||
@@ -2320,6 +2333,8 @@ public:
|
||||
// Return true if this class is an extension of base class (SLOW)
|
||||
// Accepts nullptrs
|
||||
static bool isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp);
|
||||
void baseOverride(const VBaseOverride& flag) { m_baseOverride = flag; }
|
||||
VBaseOverride baseOverride() const { return m_baseOverride; }
|
||||
// Return the lowest class extended from, or this class
|
||||
AstClass* baseMostClassp();
|
||||
static bool isCacheableChild(const AstNode* nodep);
|
||||
|
||||
+78
-23
@@ -17,11 +17,11 @@
|
||||
#include "V3PchAstMT.h"
|
||||
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3ExecGraph.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Graph.h"
|
||||
#include "V3Hasher.h"
|
||||
#include "V3PartitionGraph.h" // Just for mtask dumping
|
||||
#include "V3String.h"
|
||||
|
||||
#include "V3Ast__gen_impl.h" // Generated by 'astgen'
|
||||
@@ -319,7 +319,7 @@ AstNodeExpr* AstInsideRange::newAndFromInside(AstNodeExpr* exprp, AstNodeExpr* l
|
||||
AstNodeExpr* const bp = new AstLte{fileline(), exprp->cloneTreePure(true), rhsp};
|
||||
ap->fileline()->modifyWarnOff(V3ErrorCode::UNSIGNED, true);
|
||||
bp->fileline()->modifyWarnOff(V3ErrorCode::CMPCONST, true);
|
||||
return new AstAnd{fileline(), ap, bp};
|
||||
return new AstLogAnd{fileline(), ap, bp};
|
||||
}
|
||||
|
||||
AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) {
|
||||
@@ -738,49 +738,66 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef) const {
|
||||
const CTypeRecursed info = cTypeRecurse(false);
|
||||
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef, bool packed) const {
|
||||
const CTypeRecursed info = cTypeRecurse(false, packed);
|
||||
return info.render(name, isRef);
|
||||
}
|
||||
|
||||
AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
||||
AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packed) const {
|
||||
// Legacy compound argument currently just passed through and unused
|
||||
CTypeRecursed info;
|
||||
|
||||
const AstNodeDType* const dtypep = this->skipRefp();
|
||||
if (const auto* const adtypep = VN_CAST(dtypep, AssocArrayDType)) {
|
||||
const CTypeRecursed key = adtypep->keyDTypep()->cTypeRecurse(true);
|
||||
const CTypeRecursed val = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
const CTypeRecursed key = adtypep->keyDTypep()->cTypeRecurse(true, false);
|
||||
const CTypeRecursed val = adtypep->subDTypep()->cTypeRecurse(true, false);
|
||||
info.m_type = "VlAssocArray<" + key.m_type + ", " + val.m_type + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, CDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
info.m_type = adtypep->name();
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) {
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true, false);
|
||||
info.m_type = "VlAssocArray<std::string, " + sub.m_type + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, DynArrayDType)) {
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true, false);
|
||||
info.m_type = "VlQueue<" + sub.m_type + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, QueueDType)) {
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true, false);
|
||||
info.m_type = "VlQueue<" + sub.m_type;
|
||||
// + 1 below as VlQueue uses 0 to mean unlimited, 1 to mean size() max is 1
|
||||
if (adtypep->boundp()) info.m_type += ", " + cvtToStr(adtypep->boundConst() + 1);
|
||||
info.m_type += ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, SampleQueueDType)) {
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true, false);
|
||||
info.m_type = "VlSampleQueue<" + sub.m_type + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, ClassRefDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
info.m_type = "VlClassRef<" + EmitCBase::prefixNameProtect(adtypep) + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, IfaceRefDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
info.m_type = EmitCBase::prefixNameProtect(adtypep->ifaceViaCellp()) + "*";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
if (adtypep->isCompound()) compound = true;
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(compound);
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(compound, false);
|
||||
info.m_type = "VlUnpacked<" + sub.m_type;
|
||||
info.m_type += ", " + cvtToStr(adtypep->declRange().elements());
|
||||
info.m_type += ">";
|
||||
} else if (VN_IS(dtypep, NodeUOrStructDType) && !VN_AS(dtypep, NodeUOrStructDType)->packed()) {
|
||||
const auto* const sdtypep = VN_AS(dtypep, NodeUOrStructDType);
|
||||
} else if (packed && (VN_IS(dtypep, PackArrayDType))) {
|
||||
const AstPackArrayDType* const adtypep = VN_CAST(dtypep, PackArrayDType);
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(false, true);
|
||||
info.m_type = std::move(sub.m_type);
|
||||
info.m_dims = "[" + cvtToStr(adtypep->elementsConst()) + "]" + sub.m_dims;
|
||||
} else if (VN_IS(dtypep, NodeUOrStructDType)
|
||||
&& (!VN_AS(dtypep, NodeUOrStructDType)->packed() || packed)) {
|
||||
const AstNodeUOrStructDType* const sdtypep = VN_AS(dtypep, NodeUOrStructDType);
|
||||
UASSERT_OBJ(!packed || sdtypep->packed(), this,
|
||||
"Unsupported type for packed struct or union");
|
||||
info.m_type = EmitCBase::prefixNameProtect(sdtypep);
|
||||
} else if (const AstBasicDType* const bdtypep = dtypep->basicp()) {
|
||||
// We don't print msb()/lsb() as multidim packed would require recursion,
|
||||
@@ -823,6 +840,13 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
||||
} else if (dtypep->isWide()) {
|
||||
info.m_type = "VlWide<" + cvtToStr(dtypep->widthWords()) + ">" + bitvec;
|
||||
}
|
||||
// CData, SData, IData, QData or VlWide are packed type.
|
||||
const bool packedType = VString::startsWith(info.m_type, "CData")
|
||||
|| VString::startsWith(info.m_type, "SData")
|
||||
|| VString::startsWith(info.m_type, "IData")
|
||||
|| VString::startsWith(info.m_type, "QData")
|
||||
|| VString::startsWith(info.m_type, "VlWide");
|
||||
UASSERT_OBJ(!packed || packedType, this, "Unsupported type for packed struct or union");
|
||||
} else {
|
||||
v3fatalSrc("Unknown data type in var type emitter: " << dtypep->prettyName());
|
||||
}
|
||||
@@ -1479,12 +1503,20 @@ void AstCell::dumpJson(std::ostream& str) const {
|
||||
void AstCellInline::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " -> " << origModName();
|
||||
str << " [scopep=" << nodeAddr(scopep()) << "]";
|
||||
}
|
||||
void AstCellInline::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStrFunc(str, origModName);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstCellInlineScope::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " -> " << origModName();
|
||||
str << " [scopep=" << nodeAddr(scopep()) << "]";
|
||||
}
|
||||
void AstCellInlineScope::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStrFunc(str, origModName);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
bool AstClass::isCacheableChild(const AstNode* nodep) {
|
||||
return (VN_IS(nodep, Var) || VN_IS(nodep, Constraint) || VN_IS(nodep, EnumItemRef)
|
||||
|| (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto())
|
||||
@@ -1515,6 +1547,7 @@ void AstClass::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFunc(str, isExtended);
|
||||
dumpJsonBoolFunc(str, isInterfaceClass);
|
||||
dumpJsonBoolFunc(str, isVirtual);
|
||||
if (baseOverride().isAny()) dumpJsonStr(str, "baseOverride", baseOverride().ascii());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstClassExtends::dump(std::ostream& str) const {
|
||||
@@ -1699,7 +1732,7 @@ void AstJumpGo::dump(std::ostream& str) const {
|
||||
if (labelp()) {
|
||||
labelp()->dump(str);
|
||||
} else {
|
||||
str << "%Error:UNLINKED";
|
||||
str << "%E:UNLINKED";
|
||||
}
|
||||
}
|
||||
void AstJumpGo::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
@@ -1714,7 +1747,7 @@ void AstJumpLabel::dump(std::ostream& str) const {
|
||||
if (blockp()) {
|
||||
blockp()->dump(str);
|
||||
} else {
|
||||
str << "%Error:UNLINKED";
|
||||
str << "%E:UNLINKED";
|
||||
}
|
||||
}
|
||||
void AstJumpLabel::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
@@ -1728,7 +1761,8 @@ AstNodeUOrStructDType* AstMemberDType::getChildStructp() const {
|
||||
while (AstNodeArrayDType* const asubdtp = VN_CAST(subdtp, NodeArrayDType)) {
|
||||
subdtp = asubdtp->subDTypep();
|
||||
}
|
||||
return VN_CAST(subdtp, NodeUOrStructDType); // Maybe nullptr
|
||||
// It's possible that `subdtp` is still a ref type, so skip it.
|
||||
return VN_CAST(subdtp->skipRefp(), NodeUOrStructDType); // Maybe nullptr
|
||||
}
|
||||
|
||||
bool AstMemberSel::same(const AstNode* samep) const {
|
||||
@@ -1743,7 +1777,7 @@ void AstMemberSel::dump(std::ostream& str) const {
|
||||
if (varp()) {
|
||||
varp()->dump(str);
|
||||
} else {
|
||||
str << "%Error:UNLINKED";
|
||||
str << "%E:UNLINKED";
|
||||
}
|
||||
}
|
||||
void AstMemberSel::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
@@ -1916,7 +1950,7 @@ void AstNodeDType::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFunc(str, generic);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstNodeDType::dumpSmall(std::ostream& str) const {
|
||||
void AstNodeDType::dumpSmall(std::ostream& str) const VL_MT_STABLE {
|
||||
str << "(" << (generic() ? "G/" : "") << ((isSigned() && !isDouble()) ? "s" : "")
|
||||
<< (isNosign() ? "n" : "") << (isDouble() ? "d" : "") << (isString() ? "str" : "");
|
||||
if (!isDouble() && !isString()) str << "w" << (widthSized() ? "" : "u") << width();
|
||||
@@ -2063,7 +2097,7 @@ void AstSliceSel::dump(std::ostream& str) const {
|
||||
if (declRange().ranged()) str << " decl" << declRange();
|
||||
}
|
||||
void AstSliceSel::dumpJson(std::ostream& str) const {
|
||||
if (declRange().ranged()) { dumpJsonStr(str, "declRange", cvtToStr(declRange())); }
|
||||
if (declRange().ranged()) dumpJsonStr(str, "declRange", cvtToStr(declRange()));
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstMTaskBody::dump(std::ostream& str) const {
|
||||
@@ -2424,6 +2458,7 @@ void AstNodeFTask::dump(std::ostream& str) const {
|
||||
if (dpiOpenChild()) str << " [DPIOPENCHILD]";
|
||||
if (dpiOpenParent()) str << " [DPIOPENPARENT]";
|
||||
if (prototype()) str << " [PROTOTYPE]";
|
||||
if (pureVirtual()) str << " [PUREVIRTUAL]";
|
||||
if (recursive()) str << " [RECURSIVE]";
|
||||
if (taskPublic()) str << " [PUBLIC]";
|
||||
if ((dpiImport() || dpiExport()) && cname() != name()) str << " [c=" << cname() << "]";
|
||||
@@ -2462,6 +2497,7 @@ void AstNodeFTask::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFunc(str, prototype);
|
||||
dumpJsonBoolFunc(str, recursive);
|
||||
dumpJsonBoolFunc(str, taskPublic);
|
||||
if (baseOverride().isAny()) dumpJsonStr(str, "baseOverride", baseOverride().ascii());
|
||||
dumpJsonStrFunc(str, cname);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
@@ -2517,7 +2553,7 @@ void AstCoverInc::dump(std::ostream& str) const {
|
||||
if (declp()) {
|
||||
declp()->dump(str);
|
||||
} else {
|
||||
str << "%Error:UNLINKED";
|
||||
str << "%E:UNLINKED";
|
||||
}
|
||||
}
|
||||
void AstCoverInc::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
@@ -2543,7 +2579,7 @@ void AstTraceInc::dump(std::ostream& str) const {
|
||||
if (declp()) {
|
||||
declp()->dump(str);
|
||||
} else {
|
||||
str << "%Error:UNLINKED";
|
||||
str << "%E:UNLINKED";
|
||||
}
|
||||
}
|
||||
void AstTraceInc::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
@@ -2757,3 +2793,22 @@ void AstDelay::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFunc(str, isCycleDelay);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
const char* AstAnd::widthMismatch() const VL_MT_STABLE {
|
||||
BROKEN_RTN(lhsp()->widthMin() != rhsp()->widthMin());
|
||||
BROKEN_RTN(lhsp()->widthMin() != widthMin());
|
||||
return nullptr;
|
||||
}
|
||||
const char* AstOr::widthMismatch() const VL_MT_STABLE {
|
||||
BROKEN_RTN(lhsp()->widthMin() != rhsp()->widthMin());
|
||||
BROKEN_RTN(lhsp()->widthMin() != widthMin());
|
||||
return nullptr;
|
||||
}
|
||||
const char* AstXor::widthMismatch() const VL_MT_STABLE {
|
||||
BROKEN_RTN(lhsp()->widthMin() != rhsp()->widthMin());
|
||||
BROKEN_RTN(lhsp()->widthMin() != widthMin());
|
||||
return nullptr;
|
||||
}
|
||||
const char* AstNot::widthMismatch() const VL_MT_STABLE {
|
||||
BROKEN_RTN(lhsp()->widthMin() != widthMin());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -372,7 +372,11 @@ void V3Broken::selfTest() {
|
||||
// Exercise addNewed and deleted for coverage, as otherwise only used with VL_LEAK_CHECKS
|
||||
FileLine* const fl = new FileLine{FileLine::commandLineFilename()};
|
||||
const AstNode* const newp = new AstBegin{fl, "[EditWrapper]", nullptr};
|
||||
// Don't actually do it with VL_LEAK_CHECKS, when new/delete calls these.
|
||||
// Otherwise you call addNewed twice on the same address, which is an error.
|
||||
#ifndef VL_LEAK_CHECKS
|
||||
addNewed(newp);
|
||||
deleted(newp);
|
||||
#endif
|
||||
VL_DO_DANGLING(delete newp, newp);
|
||||
}
|
||||
|
||||
+10
-5
@@ -82,7 +82,7 @@ class CastVisitor final : public VNVisitor {
|
||||
}
|
||||
void ensureCast(AstNodeExpr* nodep) {
|
||||
if (castSize(nodep->backp()) != castSize(nodep) || !nodep->user1()) {
|
||||
insertCast(nodep, castSize(nodep->backp()));
|
||||
if (!nodep->isNull()) insertCast(nodep, castSize(nodep->backp()));
|
||||
}
|
||||
}
|
||||
void ensureLower32Cast(AstCCast* nodep) {
|
||||
@@ -121,14 +121,14 @@ class CastVisitor final : public VNVisitor {
|
||||
// already checked by V3Width and dtypep of a condition operator is a type of their
|
||||
// common base class, so both classes can be safely casted.
|
||||
const AstClassRefDType* const thenClassDtypep
|
||||
= VN_CAST(nodep->thenp()->dtypep(), ClassRefDType);
|
||||
= VN_CAST(nodep->thenp()->dtypep()->skipRefp(), ClassRefDType);
|
||||
const AstClassRefDType* const elseClassDtypep
|
||||
= VN_CAST(nodep->elsep()->dtypep(), ClassRefDType);
|
||||
= VN_CAST(nodep->elsep()->dtypep()->skipRefp(), ClassRefDType);
|
||||
const bool castRequired = thenClassDtypep && elseClassDtypep
|
||||
&& (thenClassDtypep->classp() != elseClassDtypep->classp());
|
||||
if (castRequired) {
|
||||
const AstClass* const commonBaseClassp
|
||||
= VN_AS(nodep->dtypep(), ClassRefDType)->classp();
|
||||
= VN_AS(nodep->dtypep()->skipRefp(), ClassRefDType)->classp();
|
||||
if (thenClassDtypep->classp() != commonBaseClassp) {
|
||||
AstNodeExpr* thenp = nodep->thenp()->unlinkFrBack();
|
||||
nodep->thenp(new AstCCast{thenp->fileline(), thenp, nodep});
|
||||
@@ -201,7 +201,7 @@ class CastVisitor final : public VNVisitor {
|
||||
// Constants are of unknown size if smaller than 33 bits, because
|
||||
// we're too lazy to wrap every constant in the universe in
|
||||
// ((IData)#).
|
||||
nodep->user1(nodep->isQuad() || nodep->isWide());
|
||||
nodep->user1(nodep->isQuad() || nodep->isWide() || nodep->isNull());
|
||||
}
|
||||
|
||||
// Null dereference protection
|
||||
@@ -212,6 +212,11 @@ class CastVisitor final : public VNVisitor {
|
||||
void visit(AstCMethodCall* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureNullChecked(nodep->fromp());
|
||||
nodep->user1(true);
|
||||
}
|
||||
void visit(AstCMethodHard* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(true);
|
||||
}
|
||||
void visit(AstMemberSel* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
|
||||
+43
-8
@@ -26,6 +26,9 @@
|
||||
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
#include <queue>
|
||||
#include <set>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
@@ -46,6 +49,11 @@ class ClassVisitor final : public VNVisitor {
|
||||
const AstNodeFTask* m_ftaskp = nullptr; // Current task
|
||||
std::vector<std::pair<AstNode*, AstScope*>> m_toScopeMoves;
|
||||
std::vector<std::pair<AstNode*, AstNodeModule*>> m_toPackageMoves;
|
||||
std::set<AstTypedef*> m_typedefps; // Contains all typedef nodes
|
||||
std::set<AstNodeUOrStructDType*> m_strDtypeps; // Contains all packed structs and unions
|
||||
// Contains all public packed structs and unions, using a queue to
|
||||
// mark embedded struct / union public by BFS
|
||||
std::queue<AstNodeUOrStructDType*> m_pubStrDtypeps;
|
||||
|
||||
// METHODS
|
||||
|
||||
@@ -150,7 +158,7 @@ class ClassVisitor final : public VNVisitor {
|
||||
// have a pointer to it yet
|
||||
m_toScopeMoves.emplace_back(nodep, m_packageScopep);
|
||||
}
|
||||
if (!m_ftaskp && nodep->lifetime().isStatic()) {
|
||||
if (!m_ftaskp && (nodep->lifetime().isStatic() || nodep->isParam())) {
|
||||
m_toPackageMoves.emplace_back(nodep, m_classPackagep);
|
||||
// We're really moving the VarScope but we might not
|
||||
// have a pointer to it yet
|
||||
@@ -190,12 +198,12 @@ class ClassVisitor final : public VNVisitor {
|
||||
void visit(AstInitial* nodep) override {
|
||||
// But not AstInitialAutomatic, which remains under the class
|
||||
iterateChildren(nodep);
|
||||
if (m_packageScopep) { m_toScopeMoves.emplace_back(nodep, m_packageScopep); }
|
||||
if (m_packageScopep) m_toScopeMoves.emplace_back(nodep, m_packageScopep);
|
||||
}
|
||||
void visit(AstInitialStatic* nodep) override {
|
||||
// But not AstInitialAutomatic, which remains under the class
|
||||
iterateChildren(nodep);
|
||||
if (m_packageScopep) { m_toScopeMoves.emplace_back(nodep, m_packageScopep); }
|
||||
if (m_packageScopep) m_toScopeMoves.emplace_back(nodep, m_packageScopep);
|
||||
}
|
||||
|
||||
void setStructModulep(AstNodeUOrStructDType* const dtypep) {
|
||||
@@ -204,22 +212,27 @@ class ClassVisitor final : public VNVisitor {
|
||||
dtypep->classOrPackagep(m_classPackagep ? m_classPackagep : m_modp);
|
||||
dtypep->name(
|
||||
m_names.get(dtypep->name() + (VN_IS(dtypep, UnionDType) ? "__union" : "__struct")));
|
||||
if (dtypep->packed()) m_strDtypeps.insert(dtypep);
|
||||
|
||||
for (const AstMemberDType* itemp = dtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
AstNodeUOrStructDType* const subp = itemp->getChildStructp();
|
||||
// Recurse only into anonymous unpacked structs inside this definition,
|
||||
// other unpacked structs will be reached from another typedefs
|
||||
if (subp && !subp->packed() && subp->name().empty()) setStructModulep(subp);
|
||||
// Recurse only into anonymous structs inside this definition,
|
||||
// other structs will be reached from another typedefs
|
||||
if (subp && subp->name().empty()) setStructModulep(subp);
|
||||
}
|
||||
}
|
||||
void visit(AstTypedef* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
AstNodeUOrStructDType* const dtypep = VN_CAST(nodep->dtypep(), NodeUOrStructDType);
|
||||
if (dtypep && dtypep->packed()) {
|
||||
m_typedefps.insert(nodep);
|
||||
if (nodep->attrPublic()) m_pubStrDtypeps.push(dtypep);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
if (m_classPackagep) m_classPackagep->addStmtsp(nodep->unlinkFrBack());
|
||||
|
||||
AstNodeUOrStructDType* const dtypep = VN_CAST(nodep->dtypep(), NodeUOrStructDType);
|
||||
if (dtypep && !dtypep->packed()) {
|
||||
if (dtypep) {
|
||||
dtypep->name(nodep->name());
|
||||
setStructModulep(dtypep);
|
||||
}
|
||||
@@ -258,6 +271,28 @@ public:
|
||||
nodep->unlinkFrBack();
|
||||
modp->addStmtsp(nodep);
|
||||
}
|
||||
// BFS to mark public typedefs.
|
||||
std::set<AstNodeUOrStructDType*> pubStrDtypeps;
|
||||
while (!m_pubStrDtypeps.empty()) {
|
||||
AstNodeUOrStructDType* const dtypep = m_pubStrDtypeps.front();
|
||||
m_pubStrDtypeps.pop();
|
||||
if (pubStrDtypeps.insert(dtypep).second) {
|
||||
for (const AstMemberDType* itemp = dtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
if (AstNodeUOrStructDType* const subp = itemp->getChildStructp())
|
||||
m_pubStrDtypeps.push(subp);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (AstTypedef* typedefp : m_typedefps) {
|
||||
AstNodeUOrStructDType* const sdtypep = VN_AS(typedefp->dtypep(), NodeUOrStructDType);
|
||||
if (pubStrDtypeps.count(sdtypep)) typedefp->attrPublic(true);
|
||||
}
|
||||
// Clear package pointer of non-public packed struct / union type, which will never be
|
||||
// exported.
|
||||
for (AstNodeUOrStructDType* sdtypep : m_strDtypeps) {
|
||||
if (!pubStrDtypeps.count(sdtypep)) sdtypep->classOrPackagep(nullptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -224,6 +224,11 @@ class CleanVisitor final : public VNVisitor {
|
||||
iterateChildren(nodep);
|
||||
setClean(nodep, true);
|
||||
}
|
||||
void visit(AstConsPackMember* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureClean(nodep->rhsp());
|
||||
setClean(nodep, true);
|
||||
}
|
||||
void visit(AstSel* nodep) override {
|
||||
operandTriop(nodep);
|
||||
setClean(nodep, nodep->cleanOut());
|
||||
|
||||
@@ -53,6 +53,7 @@ class ConvertWriteRefsToRead final : public VNVisitor {
|
||||
if (nodep->access().isWriteOnly()) {
|
||||
nodep->replaceWith(
|
||||
new AstVarRef{nodep->fileline(), nodep->varScopep(), VAccess::READ});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +67,9 @@ public:
|
||||
// Clock state, as a visitor of each AstNode
|
||||
|
||||
class ClockVisitor final : public VNVisitor {
|
||||
// NODE STATE
|
||||
// ???
|
||||
const VNUser1InUse m_user1InUse;
|
||||
// STATE
|
||||
AstCFunc* m_evalp = nullptr; // The '_eval' function
|
||||
AstScope* m_scopep = nullptr; // Current scope
|
||||
|
||||
+1
-1
@@ -286,7 +286,7 @@ public:
|
||||
}
|
||||
void update(const V3ConfigFile& file) {
|
||||
// Copy in all Attributes
|
||||
for (const auto& itr : file.m_lineAttrs) { m_lineAttrs[itr.first] |= itr.second; }
|
||||
for (const auto& itr : file.m_lineAttrs) m_lineAttrs[itr.first] |= itr.second;
|
||||
// Copy in all ignores
|
||||
for (const auto& ignLine : file.m_ignLines) m_ignLines.insert(ignLine);
|
||||
// Update the iterator after the list has changed
|
||||
|
||||
+74
-24
@@ -684,7 +684,7 @@ public:
|
||||
// Reduction ops are transformed in the same way.
|
||||
// &{v[0], v[1]} => 2'b11 == (2'b11 & v)
|
||||
static AstNodeExpr* simplify(AstNodeExpr* nodep, int resultWidth, unsigned externalOps,
|
||||
VDouble0& reduction, VNDeleter& deleterr) {
|
||||
VDouble0& reduction) {
|
||||
UASSERT_OBJ(1 <= resultWidth && resultWidth <= 64, nodep, "resultWidth out of range");
|
||||
|
||||
// Walk tree, gathering all terms referenced in expression
|
||||
@@ -727,7 +727,7 @@ public:
|
||||
}
|
||||
// Set width and widthMin precisely
|
||||
resultp->dtypeChgWidth(resultWidth, 1);
|
||||
for (AstNode* const termp : termps) deleterr.pushDeletep(termp);
|
||||
for (AstNode* const termp : termps) VL_DO_DANGLING(termp->deleteTree(), termp);
|
||||
return resultp;
|
||||
}
|
||||
const ResultTerm result = v->getResultTerm();
|
||||
@@ -803,7 +803,7 @@ public:
|
||||
|
||||
// Only substitute the result if beneficial as determined by operation count
|
||||
if (visitor.m_ops <= resultOps) {
|
||||
for (AstNode* const termp : termps) deleterr.pushDeletep(termp);
|
||||
for (AstNode* const termp : termps) VL_DO_DANGLING(termp->deleteTree(), termp);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1187,11 +1187,9 @@ class ConstVisitor final : public VNVisitor {
|
||||
const AstAnd* const andp = VN_CAST(nodep, And);
|
||||
const int width = nodep->width();
|
||||
if (andp && isConst(andp->lhsp(), 1)) { // 1 & BitOpTree
|
||||
newp = ConstBitOpTreeVisitor::simplify(andp->rhsp(), width, 1, m_statBitOpReduction,
|
||||
deleter());
|
||||
newp = ConstBitOpTreeVisitor::simplify(andp->rhsp(), width, 1, m_statBitOpReduction);
|
||||
} else { // BitOpTree
|
||||
newp = ConstBitOpTreeVisitor::simplify(nodep, width, 0, m_statBitOpReduction,
|
||||
deleter());
|
||||
newp = ConstBitOpTreeVisitor::simplify(nodep, width, 0, m_statBitOpReduction);
|
||||
}
|
||||
|
||||
if (newp) {
|
||||
@@ -1345,7 +1343,8 @@ class ConstVisitor final : public VNVisitor {
|
||||
= new AstSel{nodep->fileline(), ap->unlinkFrBack(), newLsb, nodep->widthConst()};
|
||||
newp->dtypeFrom(nodep);
|
||||
if (debug() >= 9) newp->dumpTree("- SEL(SH)-ou: ");
|
||||
VL_DO_DANGLING(nodep->replaceWith(newp), nodep);
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1822,16 +1821,20 @@ class ConstVisitor final : public VNVisitor {
|
||||
// {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()->cloneTreePure(false);
|
||||
AstNodeExpr* const lrp = lp->rhsp()->cloneTreePure(false);
|
||||
AstNodeExpr* const rlp = rp->lhsp()->cloneTreePure(false);
|
||||
AstNodeExpr* const rrp = rp->rhsp()->cloneTreePure(false);
|
||||
if (concatMergeable(lp, rp, 0)) {
|
||||
AstConcat* const newlp = new AstConcat{rlp->fileline(), llp, rlp};
|
||||
AstConcat* const newrp = new AstConcat{rrp->fileline(), lrp, rrp};
|
||||
AstNodeExpr* const llp = lp->lhsp();
|
||||
AstNodeExpr* const lrp = lp->rhsp();
|
||||
AstNodeExpr* const rlp = rp->lhsp();
|
||||
AstNodeExpr* const rrp = rp->rhsp();
|
||||
AstConcat* const newlp = new AstConcat{rlp->fileline(), llp->cloneTreePure(false),
|
||||
rlp->cloneTreePure(false)};
|
||||
AstConcat* const newrp = new AstConcat{rrp->fileline(), lrp->cloneTreePure(false),
|
||||
rrp->cloneTreePure(false)};
|
||||
// use the lhs to replace the parent concat
|
||||
lp->lhsp()->replaceWith(newlp);
|
||||
lp->rhsp()->replaceWith(newrp);
|
||||
llp->replaceWith(newlp);
|
||||
VL_DO_DANGLING(pushDeletep(llp), llp);
|
||||
lrp->replaceWith(newrp);
|
||||
VL_DO_DANGLING(pushDeletep(lrp), lrp);
|
||||
lp->dtypeChgWidthSigned(newlp->width(), newlp->width(), VSigning::UNSIGNED);
|
||||
UINFO(5, "merged " << nodep << endl);
|
||||
VL_DO_DANGLING(pushDeletep(rp->unlinkFrBack()), rp);
|
||||
@@ -2170,6 +2173,21 @@ class ConstVisitor final : public VNVisitor {
|
||||
"array to a variable of size greater than 64");
|
||||
}
|
||||
srcp = new AstCvtDynArrayToPacked{srcp->fileline(), srcp, srcDTypep};
|
||||
} else if (VN_IS(srcDTypep, UnpackArrayDType)) {
|
||||
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 AstCvtUnpackArrayToPacked{srcp->fileline(), srcp,
|
||||
nodep->lhsp()->dtypep()};
|
||||
// Handling the case where lhs is wider than rhs by inserting zeros. StreamL does
|
||||
// not require this, since the left streaming operator implicitly handles this.
|
||||
const uint32_t packedBits = nodep->lhsp()->widthMin();
|
||||
const uint32_t unpackBits
|
||||
= srcDTypep->arrayUnpackedElements() * srcDTypep->subDTypep()->widthMin();
|
||||
const uint32_t offset = packedBits > unpackBits ? packedBits - unpackBits : 0;
|
||||
srcp = new AstShiftL{srcp->fileline(), srcp,
|
||||
new AstConst{srcp->fileline(), offset}, 64};
|
||||
}
|
||||
nodep->rhsp(srcp);
|
||||
VL_DO_DANGLING(pushDeletep(streamp), streamp);
|
||||
@@ -2190,10 +2208,17 @@ class ConstVisitor final : public VNVisitor {
|
||||
} 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};
|
||||
if (VN_IS(dstp->dtypep(), UnpackArrayDType)) {
|
||||
streamp
|
||||
= new AstCvtPackedToUnpackArray{nodep->fileline(), streamp, dstp->dtypep()};
|
||||
} else {
|
||||
UASSERT(sWidth >= dWidth, "sWidth >= dWidth should have caused an error earlier");
|
||||
if (dWidth == 0) {
|
||||
streamp
|
||||
= new AstCvtPackedToDynArray{nodep->fileline(), streamp, dstp->dtypep()};
|
||||
} else if (sWidth >= dWidth) {
|
||||
streamp = new AstSel{streamp->fileline(), streamp, sWidth - dWidth, dWidth};
|
||||
}
|
||||
}
|
||||
nodep->lhsp(dstp);
|
||||
nodep->rhsp(streamp);
|
||||
@@ -2207,10 +2232,24 @@ class ConstVisitor final : public VNVisitor {
|
||||
AstNodeExpr* srcp = nodep->rhsp()->unlinkFrBack();
|
||||
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};
|
||||
if (VN_IS(dstp->dtypep(), UnpackArrayDType)) {
|
||||
const int dstBitWidth
|
||||
= dWidth * VN_AS(dstp->dtypep(), UnpackArrayDType)->arrayUnpackedElements();
|
||||
// Handling the case where rhs is wider than lhs. StreamL does not require this
|
||||
// since the combination of the left streaming operation and the implicit
|
||||
// truncation in VL_ASSIGN_UNPACK automatically selects the left-most bits.
|
||||
if (sWidth > dstBitWidth) {
|
||||
srcp
|
||||
= new AstSel{streamp->fileline(), srcp, sWidth - dstBitWidth, dstBitWidth};
|
||||
}
|
||||
srcp = new AstCvtPackedToUnpackArray{nodep->fileline(), srcp, dstp->dtypep()};
|
||||
} else {
|
||||
UASSERT(sWidth >= dWidth, "sWidth >= dWidth should have caused an error earlier");
|
||||
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);
|
||||
@@ -2230,6 +2269,14 @@ class ConstVisitor final : public VNVisitor {
|
||||
srcp->unlinkFrBack();
|
||||
streamp->lhsp(new AstCvtDynArrayToPacked{srcp->fileline(), srcp, lhsDtypep});
|
||||
streamp->dtypeFrom(lhsDtypep);
|
||||
} else if (VN_IS(srcDTypep, UnpackArrayDType)) {
|
||||
if (lhsDtypep->widthMin() > 64) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assignment of stream of unpacked "
|
||||
"array to a variable of size greater than 64");
|
||||
}
|
||||
srcp->unlinkFrBack();
|
||||
streamp->lhsp(new AstCvtUnpackArrayToPacked{srcp->fileline(), srcp, lhsDtypep});
|
||||
streamp->dtypeFrom(lhsDtypep);
|
||||
}
|
||||
} else if (m_doV && replaceAssignMultiSel(nodep)) {
|
||||
return true;
|
||||
@@ -3300,6 +3347,9 @@ class ConstVisitor final : public VNVisitor {
|
||||
if (nodep->precondsp()) {
|
||||
nodep->replaceWith(nodep->precondsp());
|
||||
} else {
|
||||
nodep->v3warn(UNUSEDLOOP,
|
||||
"Loop condition is always false; body will never execute");
|
||||
nodep->fileline()->modifyWarnOff(V3ErrorCode::UNUSEDLOOP, true);
|
||||
nodep->unlinkFrBack();
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
|
||||
+22
-20
@@ -38,20 +38,21 @@ void DfgGraph::addGraph(DfgGraph& other) {
|
||||
m_size += other.m_size;
|
||||
other.m_size = 0;
|
||||
|
||||
const auto moveVertexList = [this](V3List<DfgVertex*>& src, V3List<DfgVertex*>& dst) {
|
||||
if (DfgVertex* vtxp = src.begin()) {
|
||||
vtxp->m_verticesEnt.moveAppend(src, dst, vtxp);
|
||||
do {
|
||||
vtxp->m_userCnt = 0;
|
||||
vtxp->m_graphp = this;
|
||||
vtxp = vtxp->verticesNext();
|
||||
} while (vtxp);
|
||||
}
|
||||
};
|
||||
|
||||
moveVertexList(other.m_varVertices, m_varVertices);
|
||||
moveVertexList(other.m_constVertices, m_constVertices);
|
||||
moveVertexList(other.m_opVertices, m_opVertices);
|
||||
for (DfgVertexVar& vtx : other.m_varVertices) {
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = this;
|
||||
}
|
||||
m_varVertices.splice(m_varVertices.end(), other.m_varVertices);
|
||||
for (DfgConst& vtx : other.m_constVertices) {
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = this;
|
||||
}
|
||||
m_constVertices.splice(m_constVertices.end(), other.m_constVertices);
|
||||
for (DfgVertex& vtx : other.m_opVertices) {
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = this;
|
||||
}
|
||||
m_opVertices.splice(m_opVertices.end(), other.m_opVertices);
|
||||
}
|
||||
|
||||
static const string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&vtx) + '"'; }
|
||||
@@ -74,6 +75,8 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
||||
} else if (varVtxp->hasExtRefs()) {
|
||||
os << ", shape=box, style=filled, fillcolor=firebrick2"; // Red
|
||||
} else if (varVtxp->hasModRefs()) {
|
||||
os << ", shape=box, style=filled, fillcolor=darkorange1"; // Orange
|
||||
} else if (varVtxp->hasDfgRefs()) {
|
||||
os << ", shape=box, style=filled, fillcolor=gold2"; // Yellow
|
||||
} else if (varVtxp->keep()) {
|
||||
os << ", shape=box, style=filled, fillcolor=grey";
|
||||
@@ -98,6 +101,8 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
||||
} else if (arrVtxp->hasExtRefs()) {
|
||||
os << ", shape=box3d, style=filled, fillcolor=firebrick2"; // Red
|
||||
} else if (arrVtxp->hasModRefs()) {
|
||||
os << ", shape=box3d, style=filled, fillcolor=darkorange1"; // Orange
|
||||
} else if (arrVtxp->hasDfgRefs()) {
|
||||
os << ", shape=box3d, style=filled, fillcolor=gold2"; // Yellow
|
||||
} else if (arrVtxp->keep()) {
|
||||
os << ", shape=box3d, style=filled, fillcolor=grey";
|
||||
@@ -229,7 +234,7 @@ static void dumpDotUpstreamConeFromVertex(std::ostream& os, const DfgVertex& vtx
|
||||
// Emit all DfgVarPacked vertices that have external references driven by this vertex
|
||||
vtx.forEachSink([&](const DfgVertex& dst) {
|
||||
if (const DfgVarPacked* const varVtxp = dst.cast<DfgVarPacked>()) {
|
||||
if (varVtxp->hasRefs()) dumpDotVertexAndSourceEdges(os, dst);
|
||||
if (varVtxp->hasNonLocalRefs()) dumpDotVertexAndSourceEdges(os, dst);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -263,7 +268,7 @@ void DfgGraph::dumpDotAllVarConesPrefixed(const string& label) const {
|
||||
// Check if this vertex drives a variable referenced outside the DFG.
|
||||
const DfgVarPacked* const sinkp
|
||||
= vtx.findSink<DfgVarPacked>([](const DfgVarPacked& sink) { //
|
||||
return sink.hasRefs();
|
||||
return sink.hasNonLocalRefs();
|
||||
});
|
||||
|
||||
// We only dump cones driving an externally referenced variable
|
||||
@@ -341,10 +346,7 @@ DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType*
|
||||
dfg.addVertex(*this);
|
||||
}
|
||||
|
||||
DfgVertex::~DfgVertex() {
|
||||
// TODO: It would be best to intern these via AstTypeTable to save the effort
|
||||
if (VN_IS(m_dtypep, UnpackArrayDType)) VL_DO_DANGLING(delete m_dtypep, m_dtypep);
|
||||
}
|
||||
DfgVertex::~DfgVertex() {}
|
||||
|
||||
bool DfgVertex::selfEquals(const DfgVertex& that) const { return true; }
|
||||
|
||||
|
||||
+333
-354
@@ -55,6 +55,8 @@
|
||||
#endif
|
||||
|
||||
class DfgEdge;
|
||||
class DfgVertex;
|
||||
class DfgGraph;
|
||||
class DfgVisitor;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -89,140 +91,6 @@ constexpr bool operator==(VDfgType lhs, VDfgType::en rhs) { return lhs.m_e == rh
|
||||
constexpr bool operator==(VDfgType::en lhs, VDfgType rhs) { return lhs == rhs.m_e; }
|
||||
inline std::ostream& operator<<(std::ostream& os, const VDfgType& t) { return os << t.ascii(); }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Dataflow graph
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class DfgGraph final {
|
||||
friend class DfgVertex;
|
||||
|
||||
// TYPES
|
||||
|
||||
// RAII handle for DfgVertex user data
|
||||
class UserDataInUse final {
|
||||
DfgGraph* m_graphp; // The referenced graph
|
||||
|
||||
public:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
UserDataInUse(DfgGraph* graphp)
|
||||
: m_graphp{graphp} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
UserDataInUse(UserDataInUse&& that) {
|
||||
UASSERT(that.m_graphp, "Moving from empty");
|
||||
m_graphp = std::exchange(that.m_graphp, nullptr);
|
||||
}
|
||||
VL_UNCOPYABLE(UserDataInUse);
|
||||
UserDataInUse& operator=(UserDataInUse&& that) {
|
||||
UASSERT(that.m_graphp, "Moving from empty");
|
||||
m_graphp = std::exchange(that.m_graphp, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~UserDataInUse() {
|
||||
if (m_graphp) m_graphp->m_userCurrent = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// MEMBERS
|
||||
|
||||
// Variables and constants make up a significant proportion of vertices (40-50% was observed
|
||||
// in large designs), and they can often be treated specially in algorithms, which in turn
|
||||
// enables significant Verilation performance gains, so we keep these in separate lists for
|
||||
// direct access.
|
||||
V3List<DfgVertex*> m_varVertices; // The variable vertices in the graph
|
||||
V3List<DfgVertex*> m_constVertices; // The constant vertices in the graph
|
||||
V3List<DfgVertex*> m_opVertices; // The operation vertices in the graph
|
||||
|
||||
size_t m_size = 0; // Number of vertices in the graph
|
||||
uint32_t m_userCurrent = 0; // Vertex user data generation number currently in use
|
||||
uint32_t m_userCnt = 0; // Vertex user data generation counter
|
||||
// Parent of the graph (i.e.: the module containing the logic represented by this graph).
|
||||
AstModule* const m_modulep;
|
||||
const string m_name; // Name of graph (for debugging)
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
explicit DfgGraph(AstModule& module, const string& name = "") VL_MT_DISABLED;
|
||||
~DfgGraph() VL_MT_DISABLED;
|
||||
VL_UNCOPYABLE(DfgGraph);
|
||||
|
||||
// METHODS
|
||||
public:
|
||||
// Add DfgVertex to this graph (assumes not yet contained).
|
||||
inline void addVertex(DfgVertex& vtx);
|
||||
// Remove DfgVertex form this graph (assumes it is contained).
|
||||
inline void removeVertex(DfgVertex& vtx);
|
||||
// Number of vertices in this graph
|
||||
size_t size() const { return m_size; }
|
||||
// Parent module
|
||||
AstModule* modulep() const { return m_modulep; }
|
||||
// Name of this graph
|
||||
const string& name() const { return m_name; }
|
||||
|
||||
// Reset Vertex user data
|
||||
UserDataInUse userDataInUse() {
|
||||
UASSERT(!m_userCurrent, "Conflicting use of DfgVertex user data");
|
||||
++m_userCnt;
|
||||
UASSERT(m_userCnt, "'m_userCnt' overflow");
|
||||
m_userCurrent = m_userCnt;
|
||||
return UserDataInUse{this};
|
||||
}
|
||||
|
||||
// Access to vertex lists for faster iteration in important contexts
|
||||
inline DfgVertexVar* varVerticesBeginp() const;
|
||||
inline DfgVertexVar* varVerticesRbeginp() const;
|
||||
inline DfgConst* constVerticesBeginp() const;
|
||||
inline DfgConst* constVerticesRbeginp() const;
|
||||
inline DfgVertex* opVerticesBeginp() const;
|
||||
inline DfgVertex* opVerticesRbeginp() const;
|
||||
|
||||
// Calls given function 'f' for each vertex in the graph. It is safe to manipulate any vertices
|
||||
// in the graph, or to delete/unlink the vertex passed to 'f' during iteration. It is however
|
||||
// not safe to delete/unlink any vertex in the same graph other than the one passed to 'f'.
|
||||
inline void forEachVertex(std::function<void(DfgVertex&)> f);
|
||||
|
||||
// 'const' variant of 'forEachVertex'. No mutation allowed.
|
||||
inline void forEachVertex(std::function<void(const DfgVertex&)> f) const;
|
||||
|
||||
// Add contents of other graph to this graph. Leaves other graph empty.
|
||||
void addGraph(DfgGraph& other) VL_MT_DISABLED;
|
||||
|
||||
// Split this graph into individual components (unique sub-graphs with no edges between them).
|
||||
// Also removes any vertices that are not weakly connected to any variable.
|
||||
// Leaves 'this' graph empty.
|
||||
std::vector<std::unique_ptr<DfgGraph>> splitIntoComponents(std::string label) VL_MT_DISABLED;
|
||||
|
||||
// Extract cyclic sub-graphs from 'this' graph. Cyclic sub-graphs are those that contain at
|
||||
// least one strongly connected component (SCC) plus any other vertices that feed or sink from
|
||||
// the SCCs, up to a variable boundary. This means that the returned graphs are guaranteed to
|
||||
// be cyclic, but they are not guaranteed to be strongly connected (however, they are always
|
||||
// at least weakly connected). Trivial SCCs that are acyclic (i.e.: vertices that are not part
|
||||
// of a cycle) are left in 'this' graph. This means that at the end 'this' graph is guaranteed
|
||||
// to be a DAG (acyclic). 'this' will not necessarily be a connected graph at the end, even if
|
||||
// it was originally connected.
|
||||
std::vector<std::unique_ptr<DfgGraph>>
|
||||
extractCyclicComponents(std::string label) VL_MT_DISABLED;
|
||||
|
||||
// Dump graph in Graphviz format into the given stream 'os'. 'label' is added to the name of
|
||||
// the graph which is included in the output.
|
||||
void dumpDot(std::ostream& os, const string& label = "") const VL_MT_DISABLED;
|
||||
// Dump graph in Graphviz format into a new file with the given 'fileName'. 'label' is added to
|
||||
// the name of the graph which is included in the output.
|
||||
void dumpDotFile(const string& fileName, const string& label = "") const VL_MT_DISABLED;
|
||||
// Dump graph in Graphviz format into a new automatically numbered debug file. 'label' is
|
||||
// added to the name of the graph, which is included in the file name and the output.
|
||||
void dumpDotFilePrefixed(const string& label = "") const VL_MT_DISABLED;
|
||||
// Dump upstream (source) logic cone starting from given vertex into a file with the given
|
||||
// 'fileName'. 'name' is the name of the graph, which is included in the output.
|
||||
void dumpDotUpstreamCone(const string& fileName, const DfgVertex& vtx,
|
||||
const string& name = "") const VL_MT_DISABLED;
|
||||
// Dump all individual logic cones driving external variables in Graphviz format into separate
|
||||
// new automatically numbered debug files. 'label' is added to the name of the graph, which is
|
||||
// included in the file names and the output. This is useful for very large graphs that are
|
||||
// otherwise difficult to browse visually due to their size.
|
||||
void dumpDotAllVarConesPrefixed(const string& label = "") const VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Dataflow graph edge
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -264,7 +132,8 @@ class DfgVertex VL_NOT_FINAL {
|
||||
using UserDataStorage = void*; // Storage allocated for user data
|
||||
|
||||
// STATE
|
||||
V3ListEnt<DfgVertex*> m_verticesEnt; // V3List handle of this vertex, kept under the DfgGraph
|
||||
V3ListLinks<DfgVertex> m_links; // V3List links
|
||||
|
||||
protected:
|
||||
DfgEdge* m_sinksp = nullptr; // List of sinks of this vertex
|
||||
FileLine* const m_filelinep; // Source location
|
||||
@@ -280,6 +149,14 @@ protected:
|
||||
public:
|
||||
virtual ~DfgVertex() VL_MT_DISABLED;
|
||||
|
||||
private:
|
||||
V3ListLinks<DfgVertex>& links() { return m_links; }
|
||||
|
||||
public:
|
||||
// List type that can store Vertex (which must be a DfgVertex) instances via m_links
|
||||
template <typename Vertex>
|
||||
using List = V3List<DfgVertex, &DfgVertex::links, Vertex>;
|
||||
|
||||
// METHODS
|
||||
private:
|
||||
// Visitor accept method
|
||||
@@ -330,10 +207,11 @@ public:
|
||||
UDEBUGONLY(UASSERT_OBJ(isSupportedDType(nodep->dtypep()), nodep, "Unsupported dtype"););
|
||||
// For simplicity, all packed types are represented with a fixed type
|
||||
if (AstUnpackArrayDType* const typep = VN_CAST(nodep->dtypep(), UnpackArrayDType)) {
|
||||
// TODO: these need interning via AstTypeTable otherwise they leak
|
||||
return new AstUnpackArrayDType{typep->fileline(),
|
||||
dtypeForWidth(typep->subDTypep()->width()),
|
||||
typep->rangep()->cloneTree(false)};
|
||||
AstNodeDType* const dtypep = new AstUnpackArrayDType{
|
||||
typep->fileline(), dtypeForWidth(typep->subDTypep()->width()),
|
||||
typep->rangep()->cloneTree(false)};
|
||||
v3Global.rootp()->typeTablep()->addTypesp(dtypep);
|
||||
return dtypep;
|
||||
}
|
||||
return dtypeForWidth(nodep->width());
|
||||
}
|
||||
@@ -348,54 +226,15 @@ public:
|
||||
|
||||
// Retrieve user data, constructing it fresh on first try.
|
||||
template <typename T>
|
||||
T& user() {
|
||||
static_assert(sizeof(T) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T' is too large for allocated storage");
|
||||
static_assert(alignof(T) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T' is larger than allocated storage");
|
||||
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
UDEBUGONLY(UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving"););
|
||||
if (m_userCnt != userCurrent) {
|
||||
m_userCnt = userCurrent;
|
||||
// cppcheck-has-bug-suppress uninitvar
|
||||
VL_ATTR_UNUSED T* const resultp = new (storagep) T{};
|
||||
UDEBUGONLY(UASSERT_OBJ(resultp == storagep, this, "Something is odd"););
|
||||
}
|
||||
return *storagep;
|
||||
}
|
||||
inline T& user();
|
||||
|
||||
// Retrieve user data, must be current.
|
||||
template <typename T>
|
||||
T& getUser() {
|
||||
static_assert(sizeof(T) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T' is too large for allocated storage");
|
||||
static_assert(alignof(T) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T' is larger than allocated storage");
|
||||
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
|
||||
#if VL_DEBUG
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");
|
||||
UASSERT_OBJ(m_userCnt == userCurrent, this, "DfgVertex user data is stale");
|
||||
#endif
|
||||
return *storagep;
|
||||
}
|
||||
inline T& getUser();
|
||||
|
||||
// Set user data, becomes current.
|
||||
template <typename T>
|
||||
typename std::enable_if<sizeof(T) <= sizeof(void*), void>::type setUser(T value) {
|
||||
static_assert(sizeof(T) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T' is too large for allocated storage");
|
||||
static_assert(alignof(T) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T' is larger than allocated storage");
|
||||
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
#if VL_DEBUG
|
||||
UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");
|
||||
#endif
|
||||
m_userCnt = userCurrent;
|
||||
*storagep = value;
|
||||
}
|
||||
inline void setUser(T value);
|
||||
|
||||
// Width of result
|
||||
uint32_t width() const {
|
||||
@@ -448,10 +287,6 @@ public:
|
||||
// Relink all sinks to be driven from the given new source
|
||||
void replaceWith(DfgVertex* newSourcep) VL_MT_DISABLED;
|
||||
|
||||
// Access to vertex list for faster iteration in important contexts
|
||||
DfgVertex* verticesNext() const { return m_verticesEnt.nextp(); }
|
||||
DfgVertex* verticesPrev() const { return m_verticesEnt.prevp(); }
|
||||
|
||||
// Calls given function 'f' for each source vertex of this vertex
|
||||
// Unconnected source edges are not iterated.
|
||||
inline void forEachSource(std::function<void(DfgVertex&)> f) VL_MT_DISABLED;
|
||||
@@ -504,9 +339,6 @@ public:
|
||||
// Is this a DfgConst that is all ones
|
||||
inline bool isOnes() const VL_MT_DISABLED;
|
||||
|
||||
// Should this vertex be inlined when rendering to Ast, or be stored to a temporary
|
||||
inline bool inlined() const VL_MT_DISABLED;
|
||||
|
||||
// Methods that allow DfgVertex to participate in error reporting/messaging
|
||||
void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
|
||||
m_filelinep->v3errorEnd(str);
|
||||
@@ -569,9 +401,6 @@ public:
|
||||
virtual const string srcName(size_t idx) const = 0;
|
||||
};
|
||||
|
||||
// Specializations of privateTypeTest
|
||||
#include "V3Dfg__gen_type_tests.h" // From ./astgen
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Dfg vertex visitor
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -585,152 +414,6 @@ public:
|
||||
#include "V3Dfg__gen_visitor_decls.h" // From ./astgen
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Inline method definitions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void DfgGraph::addVertex(DfgVertex& vtx) {
|
||||
// Note: changes here need to be replicated in DfgGraph::addGraph
|
||||
++m_size;
|
||||
if (vtx.is<DfgConst>()) {
|
||||
vtx.m_verticesEnt.pushBack(m_constVertices, &vtx);
|
||||
} else if (vtx.is<DfgVertexVar>()) {
|
||||
vtx.m_verticesEnt.pushBack(m_varVertices, &vtx);
|
||||
} else {
|
||||
vtx.m_verticesEnt.pushBack(m_opVertices, &vtx);
|
||||
}
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = this;
|
||||
}
|
||||
|
||||
void DfgGraph::removeVertex(DfgVertex& vtx) {
|
||||
// Note: changes here need to be replicated in DfgGraph::addGraph
|
||||
--m_size;
|
||||
if (vtx.is<DfgConst>()) {
|
||||
vtx.m_verticesEnt.unlink(m_constVertices, &vtx);
|
||||
} else if (vtx.is<DfgVertexVar>()) {
|
||||
vtx.m_verticesEnt.unlink(m_varVertices, &vtx);
|
||||
} else {
|
||||
vtx.m_verticesEnt.unlink(m_opVertices, &vtx);
|
||||
}
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = nullptr;
|
||||
}
|
||||
|
||||
void DfgGraph::forEachVertex(std::function<void(DfgVertex&)> f) {
|
||||
for (DfgVertex *vtxp = m_varVertices.begin(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
f(*vtxp);
|
||||
}
|
||||
for (DfgVertex *vtxp = m_constVertices.begin(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
f(*vtxp);
|
||||
}
|
||||
for (DfgVertex *vtxp = m_opVertices.begin(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
f(*vtxp);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgGraph::forEachVertex(std::function<void(const DfgVertex&)> f) const {
|
||||
for (const DfgVertex* vtxp = m_varVertices.begin(); vtxp; vtxp = vtxp->verticesNext()) {
|
||||
f(*vtxp);
|
||||
}
|
||||
for (const DfgVertex* vtxp = m_constVertices.begin(); vtxp; vtxp = vtxp->verticesNext()) {
|
||||
f(*vtxp);
|
||||
}
|
||||
for (const DfgVertex* vtxp = m_opVertices.begin(); vtxp; vtxp = vtxp->verticesNext()) {
|
||||
f(*vtxp);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSource(std::function<void(DfgVertex&)> f) {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSource(std::function<void(const DfgVertex&)> f) const {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSink(std::function<void(DfgVertex&)> f) {
|
||||
for (const DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->m_nextp;
|
||||
f(*edgep->m_sinkp);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSink(std::function<void(const DfgVertex&)> f) const {
|
||||
for (const DfgEdge* edgep = m_sinksp; edgep; edgep = edgep->m_nextp) f(*edgep->m_sinkp);
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSourceEdge(std::function<void(DfgEdge&, size_t)> f) {
|
||||
const auto pair = sourceEdges();
|
||||
DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) f(edgesp[i], i);
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSourceEdge(std::function<void(const DfgEdge&, size_t)> f) const {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) f(edgesp[i], i);
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSinkEdge(std::function<void(DfgEdge&)> f) {
|
||||
for (DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->m_nextp;
|
||||
f(*edgep);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSinkEdge(std::function<void(const DfgEdge&)> f) const {
|
||||
for (DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->m_nextp;
|
||||
f(*edgep);
|
||||
}
|
||||
}
|
||||
|
||||
const DfgEdge* DfgVertex::findSourceEdge(std::function<bool(const DfgEdge&, size_t)> p) const {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
const DfgEdge& edge = edgesp[i];
|
||||
if (p(edge, i)) return &edge;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Vertex>
|
||||
Vertex* DfgVertex::findSink(std::function<bool(const Vertex&)> p) const {
|
||||
static_assert(std::is_base_of<DfgVertex, Vertex>::value,
|
||||
"'Vertex' must be subclass of 'DfgVertex'");
|
||||
for (DfgEdge* edgep = m_sinksp; edgep; edgep = edgep->m_nextp) {
|
||||
if (Vertex* const sinkp = edgep->m_sinkp->cast<Vertex>()) {
|
||||
if (p(*sinkp)) return sinkp;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Vertex>
|
||||
Vertex* DfgVertex::findSink() const {
|
||||
static_assert(!std::is_same<DfgVertex, Vertex>::value,
|
||||
"'Vertex' must be proper subclass of 'DfgVertex'");
|
||||
return findSink<Vertex>([](const Vertex&) { return true; });
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// DfgVertex sub-types follow
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -905,20 +588,281 @@ public:
|
||||
// The rest of the DfgVertex subclasses are generated by 'astgen' from AstNodeExpr nodes
|
||||
#include "V3Dfg__gen_auto_classes.h"
|
||||
|
||||
DfgVertexVar* DfgGraph::varVerticesBeginp() const {
|
||||
return static_cast<DfgVertexVar*>(m_varVertices.begin());
|
||||
//------------------------------------------------------------------------------
|
||||
// Dataflow graph
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class DfgGraph final {
|
||||
friend class DfgVertex;
|
||||
|
||||
// TYPES
|
||||
|
||||
// RAII handle for DfgVertex user data
|
||||
class UserDataInUse final {
|
||||
DfgGraph* m_graphp; // The referenced graph
|
||||
|
||||
public:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
UserDataInUse(DfgGraph* graphp)
|
||||
: m_graphp{graphp} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
UserDataInUse(UserDataInUse&& that) {
|
||||
UASSERT(that.m_graphp, "Moving from empty");
|
||||
m_graphp = std::exchange(that.m_graphp, nullptr);
|
||||
}
|
||||
VL_UNCOPYABLE(UserDataInUse);
|
||||
UserDataInUse& operator=(UserDataInUse&& that) {
|
||||
UASSERT(that.m_graphp, "Moving from empty");
|
||||
m_graphp = std::exchange(that.m_graphp, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~UserDataInUse() {
|
||||
if (m_graphp) m_graphp->m_userCurrent = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// MEMBERS
|
||||
|
||||
// Variables and constants make up a significant proportion of vertices (40-50% was observed
|
||||
// in large designs), and they can often be treated specially in algorithms, which in turn
|
||||
// enables significant Verilation performance gains, so we keep these in separate lists for
|
||||
// direct access.
|
||||
DfgVertex::List<DfgVertexVar> m_varVertices; // The variable vertices in the graph
|
||||
DfgVertex::List<DfgConst> m_constVertices; // The constant vertices in the graph
|
||||
DfgVertex::List<DfgVertex> m_opVertices; // The operation vertices in the graph
|
||||
|
||||
size_t m_size = 0; // Number of vertices in the graph
|
||||
uint32_t m_userCurrent = 0; // Vertex user data generation number currently in use
|
||||
uint32_t m_userCnt = 0; // Vertex user data generation counter
|
||||
// Parent of the graph (i.e.: the module containing the logic represented by this graph).
|
||||
AstModule* const m_modulep;
|
||||
const string m_name; // Name of graph (for debugging)
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
explicit DfgGraph(AstModule& module, const string& name = "") VL_MT_DISABLED;
|
||||
~DfgGraph() VL_MT_DISABLED;
|
||||
VL_UNCOPYABLE(DfgGraph);
|
||||
|
||||
// METHODS
|
||||
public:
|
||||
// Add DfgVertex to this graph (assumes not yet contained).
|
||||
inline void addVertex(DfgVertex& vtx);
|
||||
// Remove DfgVertex form this graph (assumes it is contained).
|
||||
inline void removeVertex(DfgVertex& vtx);
|
||||
// Number of vertices in this graph
|
||||
size_t size() const { return m_size; }
|
||||
// Parent module
|
||||
AstModule* modulep() const { return m_modulep; }
|
||||
// Name of this graph
|
||||
const string& name() const { return m_name; }
|
||||
|
||||
// Reset Vertex user data
|
||||
UserDataInUse userDataInUse() {
|
||||
UASSERT(!m_userCurrent, "Conflicting use of DfgVertex user data");
|
||||
++m_userCnt;
|
||||
UASSERT(m_userCnt, "'m_userCnt' overflow");
|
||||
m_userCurrent = m_userCnt;
|
||||
return UserDataInUse{this};
|
||||
}
|
||||
|
||||
// Access to vertex lists
|
||||
DfgVertex::List<DfgVertexVar>& varVertices() { return m_varVertices; }
|
||||
const DfgVertex::List<DfgVertexVar>& varVertices() const { return m_varVertices; }
|
||||
DfgVertex::List<DfgConst>& constVertices() { return m_constVertices; }
|
||||
const DfgVertex::List<DfgConst>& constVertices() const { return m_constVertices; }
|
||||
DfgVertex::List<DfgVertex>& opVertices() { return m_opVertices; }
|
||||
const DfgVertex::List<DfgVertex>& opVertices() const { return m_opVertices; }
|
||||
|
||||
// Calls given function 'f' for each vertex in the graph. It is safe to manipulate any vertices
|
||||
// in the graph, or to delete/unlink the vertex passed to 'f' during iteration. It is however
|
||||
// not safe to delete/unlink any vertex in the same graph other than the one passed to 'f'.
|
||||
inline void forEachVertex(std::function<void(DfgVertex&)> f);
|
||||
|
||||
// 'const' variant of 'forEachVertex'. No mutation allowed.
|
||||
inline void forEachVertex(std::function<void(const DfgVertex&)> f) const;
|
||||
|
||||
// Add contents of other graph to this graph. Leaves other graph empty.
|
||||
void addGraph(DfgGraph& other) VL_MT_DISABLED;
|
||||
|
||||
// Split this graph into individual components (unique sub-graphs with no edges between them).
|
||||
// Also removes any vertices that are not weakly connected to any variable.
|
||||
// Leaves 'this' graph empty.
|
||||
std::vector<std::unique_ptr<DfgGraph>> splitIntoComponents(std::string label) VL_MT_DISABLED;
|
||||
|
||||
// Extract cyclic sub-graphs from 'this' graph. Cyclic sub-graphs are those that contain at
|
||||
// least one strongly connected component (SCC) plus any other vertices that feed or sink from
|
||||
// the SCCs, up to a variable boundary. This means that the returned graphs are guaranteed to
|
||||
// be cyclic, but they are not guaranteed to be strongly connected (however, they are always
|
||||
// at least weakly connected). Trivial SCCs that are acyclic (i.e.: vertices that are not part
|
||||
// of a cycle) are left in 'this' graph. This means that at the end 'this' graph is guaranteed
|
||||
// to be a DAG (acyclic). 'this' will not necessarily be a connected graph at the end, even if
|
||||
// it was originally connected.
|
||||
std::vector<std::unique_ptr<DfgGraph>>
|
||||
extractCyclicComponents(std::string label) VL_MT_DISABLED;
|
||||
|
||||
// Dump graph in Graphviz format into the given stream 'os'. 'label' is added to the name of
|
||||
// the graph which is included in the output.
|
||||
void dumpDot(std::ostream& os, const string& label = "") const VL_MT_DISABLED;
|
||||
// Dump graph in Graphviz format into a new file with the given 'fileName'. 'label' is added to
|
||||
// the name of the graph which is included in the output.
|
||||
void dumpDotFile(const string& fileName, const string& label = "") const VL_MT_DISABLED;
|
||||
// Dump graph in Graphviz format into a new automatically numbered debug file. 'label' is
|
||||
// added to the name of the graph, which is included in the file name and the output.
|
||||
void dumpDotFilePrefixed(const string& label = "") const VL_MT_DISABLED;
|
||||
// Dump upstream (source) logic cone starting from given vertex into a file with the given
|
||||
// 'fileName'. 'name' is the name of the graph, which is included in the output.
|
||||
void dumpDotUpstreamCone(const string& fileName, const DfgVertex& vtx,
|
||||
const string& name = "") const VL_MT_DISABLED;
|
||||
// Dump all individual logic cones driving external variables in Graphviz format into separate
|
||||
// new automatically numbered debug files. 'label' is added to the name of the graph, which is
|
||||
// included in the file names and the output. This is useful for very large graphs that are
|
||||
// otherwise difficult to browse visually due to their size.
|
||||
void dumpDotAllVarConesPrefixed(const string& label = "") const VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
// Specializations of privateTypeTest
|
||||
#include "V3Dfg__gen_type_tests.h" // From ./astgen
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Inline method definitions - for DfgVertex
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <typename T>
|
||||
T& DfgVertex::user() {
|
||||
static_assert(sizeof(T) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T' is too large for allocated storage");
|
||||
static_assert(alignof(T) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T' is larger than allocated storage");
|
||||
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
UDEBUGONLY(UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving"););
|
||||
if (m_userCnt != userCurrent) {
|
||||
m_userCnt = userCurrent;
|
||||
// cppcheck-has-bug-suppress uninitvar
|
||||
VL_ATTR_UNUSED T* const resultp = new (storagep) T{};
|
||||
UDEBUGONLY(UASSERT_OBJ(resultp == storagep, this, "Something is odd"););
|
||||
}
|
||||
return *storagep;
|
||||
}
|
||||
DfgVertexVar* DfgGraph::varVerticesRbeginp() const {
|
||||
return static_cast<DfgVertexVar*>(m_varVertices.rbegin());
|
||||
|
||||
template <typename T>
|
||||
T& DfgVertex::getUser() {
|
||||
static_assert(sizeof(T) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T' is too large for allocated storage");
|
||||
static_assert(alignof(T) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T' is larger than allocated storage");
|
||||
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
|
||||
#if VL_DEBUG
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");
|
||||
UASSERT_OBJ(m_userCnt == userCurrent, this, "DfgVertex user data is stale");
|
||||
#endif
|
||||
return *storagep;
|
||||
}
|
||||
DfgConst* DfgGraph::constVerticesBeginp() const {
|
||||
return static_cast<DfgConst*>(m_constVertices.begin());
|
||||
|
||||
template <typename T>
|
||||
void DfgVertex::setUser(T value) {
|
||||
static_assert(sizeof(T) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T' is too large for allocated storage");
|
||||
static_assert(alignof(T) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T' is larger than allocated storage");
|
||||
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
#if VL_DEBUG
|
||||
UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");
|
||||
#endif
|
||||
m_userCnt = userCurrent;
|
||||
*storagep = value;
|
||||
}
|
||||
DfgConst* DfgGraph::constVerticesRbeginp() const {
|
||||
return static_cast<DfgConst*>(m_constVertices.rbegin());
|
||||
|
||||
void DfgVertex::forEachSource(std::function<void(DfgVertex&)> f) {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSource(std::function<void(const DfgVertex&)> f) const {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSink(std::function<void(DfgVertex&)> f) {
|
||||
for (const DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->m_nextp;
|
||||
f(*edgep->m_sinkp);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSink(std::function<void(const DfgVertex&)> f) const {
|
||||
for (const DfgEdge* edgep = m_sinksp; edgep; edgep = edgep->m_nextp) f(*edgep->m_sinkp);
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSourceEdge(std::function<void(DfgEdge&, size_t)> f) {
|
||||
const auto pair = sourceEdges();
|
||||
DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) f(edgesp[i], i);
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSourceEdge(std::function<void(const DfgEdge&, size_t)> f) const {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) f(edgesp[i], i);
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSinkEdge(std::function<void(DfgEdge&)> f) {
|
||||
for (DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->m_nextp;
|
||||
f(*edgep);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSinkEdge(std::function<void(const DfgEdge&)> f) const {
|
||||
for (DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->m_nextp;
|
||||
f(*edgep);
|
||||
}
|
||||
}
|
||||
|
||||
const DfgEdge* DfgVertex::findSourceEdge(std::function<bool(const DfgEdge&, size_t)> p) const {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
const DfgEdge& edge = edgesp[i];
|
||||
if (p(edge, i)) return &edge;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Vertex>
|
||||
Vertex* DfgVertex::findSink(std::function<bool(const Vertex&)> p) const {
|
||||
static_assert(std::is_base_of<DfgVertex, Vertex>::value,
|
||||
"'Vertex' must be subclass of 'DfgVertex'");
|
||||
for (DfgEdge* edgep = m_sinksp; edgep; edgep = edgep->m_nextp) {
|
||||
if (Vertex* const sinkp = edgep->m_sinkp->cast<Vertex>()) {
|
||||
if (p(*sinkp)) return sinkp;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Vertex>
|
||||
Vertex* DfgVertex::findSink() const {
|
||||
static_assert(!std::is_same<DfgVertex, Vertex>::value,
|
||||
"'Vertex' must be proper subclass of 'DfgVertex'");
|
||||
return findSink<Vertex>([](const Vertex&) { return true; });
|
||||
}
|
||||
DfgVertex* DfgGraph::opVerticesBeginp() const { return m_opVertices.begin(); }
|
||||
DfgVertex* DfgGraph::opVerticesRbeginp() const { return m_opVertices.rbegin(); }
|
||||
|
||||
bool DfgVertex::isZero() const {
|
||||
if (const DfgConst* const constp = cast<DfgConst>()) return constp->isZero();
|
||||
@@ -930,13 +874,48 @@ bool DfgVertex::isOnes() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DfgVertex::inlined() const {
|
||||
// Inline vertices that drive only a single node, or are special
|
||||
if (!hasMultipleSinks()) return true;
|
||||
if (is<DfgConst>()) return true;
|
||||
if (is<DfgVertexVar>()) return true;
|
||||
if (const DfgArraySel* const selp = cast<DfgArraySel>()) return selp->bitp()->is<DfgConst>();
|
||||
return false;
|
||||
//------------------------------------------------------------------------------
|
||||
// Inline method definitions - for DfgGraph
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void DfgGraph::addVertex(DfgVertex& vtx) {
|
||||
// Note: changes here need to be replicated in DfgGraph::addGraph
|
||||
++m_size;
|
||||
if (DfgConst* const cVtxp = vtx.cast<DfgConst>()) {
|
||||
m_constVertices.linkBack(cVtxp);
|
||||
} else if (DfgVertexVar* const vVtxp = vtx.cast<DfgVertexVar>()) {
|
||||
m_varVertices.linkBack(vVtxp);
|
||||
} else {
|
||||
m_opVertices.linkBack(&vtx);
|
||||
}
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = this;
|
||||
}
|
||||
|
||||
void DfgGraph::removeVertex(DfgVertex& vtx) {
|
||||
// Note: changes here need to be replicated in DfgGraph::addGraph
|
||||
--m_size;
|
||||
if (DfgConst* const cVtxp = vtx.cast<DfgConst>()) {
|
||||
m_constVertices.unlink(cVtxp);
|
||||
} else if (DfgVertexVar* const vVtxp = vtx.cast<DfgVertexVar>()) {
|
||||
m_varVertices.unlink(vVtxp);
|
||||
} else {
|
||||
m_opVertices.unlink(&vtx);
|
||||
}
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = nullptr;
|
||||
}
|
||||
|
||||
void DfgGraph::forEachVertex(std::function<void(DfgVertex&)> f) {
|
||||
for (DfgVertexVar* const vtxp : m_varVertices.unlinkable()) f(*vtxp);
|
||||
for (DfgConst* const vtxp : m_constVertices.unlinkable()) f(*vtxp);
|
||||
for (DfgVertex* const vtxp : m_opVertices.unlinkable()) f(*vtxp);
|
||||
}
|
||||
|
||||
void DfgGraph::forEachVertex(std::function<void(const DfgVertex&)> f) const {
|
||||
for (const DfgVertexVar& vtx : m_varVertices) f(vtx);
|
||||
for (const DfgConst& vtx : m_constVertices) f(vtx);
|
||||
for (const DfgVertex& vtx : m_opVertices) f(vtx);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -439,11 +439,14 @@ class AstToDfgVisitor final : public VNVisitor {
|
||||
if (nodep->isSc()) return;
|
||||
// No need to (and in fact cannot) handle variables with unsupported dtypes
|
||||
if (!DfgVertex::isSupportedDType(nodep->dtypep())) return;
|
||||
// Mark ports as having external references
|
||||
if (nodep->isIO()) getNet(nodep)->setHasExtRefs();
|
||||
// Mark variables that are the target of a hierarchical reference
|
||||
// (these flags were set up in DataflowPrepVisitor)
|
||||
if (nodep->user2()) getNet(nodep)->setHasExtRefs();
|
||||
|
||||
// Mark variables with external references
|
||||
if (nodep->isIO() // Ports
|
||||
|| nodep->user2() // Target of a hierarchical reference
|
||||
|| nodep->isForceable() // Forceable
|
||||
) {
|
||||
getNet(nodep)->setHasExtRefs();
|
||||
}
|
||||
}
|
||||
|
||||
void visit(AstAssignW* nodep) override {
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Dfg vertex cache to find existing vertices
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2024 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
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// A cache for DfgGraph, to find existing vertices with identical inputs.
|
||||
//
|
||||
// Beware that if you use data-structure, you must invalidate the cache any
|
||||
// time you change the inputs of an existing vertex, otherwise you will
|
||||
// have a very bad day.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "V3DfgCache.h"
|
||||
|
||||
namespace V3DfgCacheInternal {
|
||||
|
||||
void V3DfgCache::cache(DfgVertex* vtxp) {
|
||||
switch (vtxp->type()) {
|
||||
#define VERTEX_CACHE_ADD_CASE(t) \
|
||||
case t::dfgType(): V3DfgCacheInternal::cache(m_cache##t, reinterpret_cast<t*>(vtxp)); break;
|
||||
FOREACH_CACHED_VERTEX_TYPE(VERTEX_CACHE_ADD_CASE)
|
||||
#undef VERTEX_CACHE_ADD_CASE
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void V3DfgCache::invalidateByValue(DfgVertex* vtxp) {
|
||||
switch (vtxp->type()) {
|
||||
#define VERTEX_CACHE_ADD_CASE(t) \
|
||||
case t::dfgType(): \
|
||||
V3DfgCacheInternal::invalidateByValue(m_cache##t, reinterpret_cast<t*>(vtxp)); \
|
||||
break;
|
||||
FOREACH_CACHED_VERTEX_TYPE(VERTEX_CACHE_ADD_CASE)
|
||||
#undef VERTEX_CACHE_ADD_CASE
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace V3DfgCacheInternal
|
||||
@@ -0,0 +1,402 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Dfg vertex cache to find existing vertices
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2024 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
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// A cache for DfgGraph, to find existing vertices with identical inputs.
|
||||
//
|
||||
// Beware that if you use this data-structure, you must invalidate the
|
||||
// cache any time you change the inputs of an existing vertex, otherwise
|
||||
// you will have a very bad day.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_V3DFGCACHE_H_
|
||||
#define VERILATOR_V3DFGCACHE_H_
|
||||
|
||||
#include "V3Dfg.h"
|
||||
|
||||
#include <tuple>
|
||||
|
||||
// Template specializatoins must be defiend before they are used,
|
||||
// so this is implemented in a namespace
|
||||
|
||||
namespace V3DfgCacheInternal {
|
||||
|
||||
// Hash constants by value, everything else by identity
|
||||
inline V3Hash vertexHash(const DfgVertex* vtxp) {
|
||||
if (const DfgConst* const constp = vtxp->cast<DfgConst>()) return constp->num().toHash();
|
||||
return V3Hash{reinterpret_cast<uint64_t>(vtxp)};
|
||||
}
|
||||
|
||||
// Constants are equal by value, everything else is equal by identity
|
||||
inline bool vertexEqual(const DfgVertex* ap, const DfgVertex* bp) {
|
||||
if (ap == bp) return true;
|
||||
if (ap->type() != bp->type()) return false;
|
||||
if (const DfgConst* const aConstp = ap->cast<DfgConst>()) {
|
||||
const DfgConst* const bConstp = bp->as<DfgConst>();
|
||||
return aConstp->num().isCaseEq(bConstp->num());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
class KeySel final {
|
||||
const DfgVertex* const m_fromp;
|
||||
const uint32_t m_lsb;
|
||||
const uint32_t m_width;
|
||||
|
||||
public:
|
||||
KeySel(DfgVertex* fromp, uint32_t lsb, uint32_t width)
|
||||
: m_fromp{fromp}
|
||||
, m_lsb{lsb}
|
||||
, m_width{width} {}
|
||||
|
||||
struct Hash final {
|
||||
size_t operator()(const KeySel& key) const {
|
||||
V3Hash hash{vertexHash(key.m_fromp)};
|
||||
hash += key.m_lsb;
|
||||
hash += key.m_width;
|
||||
return hash.value();
|
||||
}
|
||||
};
|
||||
|
||||
struct Equal final {
|
||||
bool operator()(const KeySel& a, const KeySel& b) const {
|
||||
return a.m_lsb == b.m_lsb && a.m_width == b.m_width
|
||||
&& vertexEqual(a.m_fromp, b.m_fromp);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class KeyUnary final {
|
||||
const DfgVertex* const m_source0p;
|
||||
|
||||
public:
|
||||
KeyUnary(DfgVertex* source0p)
|
||||
: m_source0p{source0p} {}
|
||||
|
||||
struct Hash final {
|
||||
size_t operator()(const KeyUnary& key) const { //
|
||||
return vertexHash(key.m_source0p).value();
|
||||
}
|
||||
};
|
||||
|
||||
struct Equal final {
|
||||
bool operator()(const KeyUnary& a, const KeyUnary& b) const {
|
||||
return vertexEqual(a.m_source0p, b.m_source0p);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class KeyBinary final {
|
||||
const DfgVertex* const m_source0p;
|
||||
const DfgVertex* const m_source1p;
|
||||
|
||||
public:
|
||||
KeyBinary(DfgVertex* source0p, DfgVertex* source1p)
|
||||
: m_source0p{source0p}
|
||||
, m_source1p{source1p} {}
|
||||
|
||||
struct Hash final {
|
||||
size_t operator()(const KeyBinary& key) const {
|
||||
V3Hash hash{vertexHash(key.m_source0p)};
|
||||
hash += vertexHash(key.m_source1p);
|
||||
return hash.value();
|
||||
}
|
||||
};
|
||||
|
||||
struct Equal final {
|
||||
bool operator()(const KeyBinary& a, const KeyBinary& b) const {
|
||||
return vertexEqual(a.m_source0p, b.m_source0p)
|
||||
&& vertexEqual(a.m_source1p, b.m_source1p);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class KeyTernary final {
|
||||
const DfgVertex* const m_source0p;
|
||||
const DfgVertex* const m_source1p;
|
||||
const DfgVertex* const m_source2p;
|
||||
|
||||
public:
|
||||
KeyTernary(DfgVertex* source0p, DfgVertex* source1p, DfgVertex* source2p)
|
||||
: m_source0p{source0p}
|
||||
, m_source1p{source1p}
|
||||
, m_source2p{source2p} {}
|
||||
|
||||
struct Hash final {
|
||||
size_t operator()(const KeyTernary& key) const {
|
||||
V3Hash hash{vertexHash(key.m_source0p)};
|
||||
hash += vertexHash(key.m_source1p);
|
||||
hash += vertexHash(key.m_source2p);
|
||||
return hash.value();
|
||||
}
|
||||
};
|
||||
|
||||
struct Equal final {
|
||||
bool operator()(const KeyTernary& a, const KeyTernary& b) const {
|
||||
return vertexEqual(a.m_source0p, b.m_source0p)
|
||||
&& vertexEqual(a.m_source1p, b.m_source1p)
|
||||
&& vertexEqual(a.m_source2p, b.m_source2p);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template <typename Key, typename Val>
|
||||
using Cache = std::unordered_map<Key, Val, typename Key::Hash, typename Key::Equal>;
|
||||
|
||||
using CacheSel = Cache<KeySel, DfgSel*>;
|
||||
using CacheUnary = Cache<KeyUnary, DfgVertexUnary*>;
|
||||
using CacheBinary = Cache<KeyBinary, DfgVertexBinary*>;
|
||||
using CacheTernary = Cache<KeyTernary, DfgVertexTernary*>;
|
||||
|
||||
// These return a reference to the mapped entry, inserting a nullptr if not yet exists
|
||||
inline DfgSel*& getEntry(CacheSel& cache, AstNodeDType* dtypep, DfgVertex* src0p, uint32_t lsb) {
|
||||
UASSERT_OBJ(VN_IS(dtypep, BasicDType), dtypep, "non-packed has no 'width()'");
|
||||
return cache
|
||||
.emplace(std::piecewise_construct, //
|
||||
std::forward_as_tuple(src0p, lsb, dtypep->width()), //
|
||||
std::forward_as_tuple(nullptr))
|
||||
.first->second;
|
||||
}
|
||||
|
||||
inline DfgVertexUnary*& getEntry(CacheUnary& cache, AstNodeDType*, DfgVertex* src0p) {
|
||||
return cache
|
||||
.emplace(std::piecewise_construct, //
|
||||
std::forward_as_tuple(src0p), //
|
||||
std::forward_as_tuple(nullptr))
|
||||
.first->second;
|
||||
}
|
||||
|
||||
inline DfgVertexBinary*& getEntry(CacheBinary& cache, AstNodeDType*, DfgVertex* src0p,
|
||||
DfgVertex* src1p) {
|
||||
return cache
|
||||
.emplace(std::piecewise_construct, //
|
||||
std::forward_as_tuple(src0p, src1p), //
|
||||
std::forward_as_tuple(nullptr))
|
||||
.first->second;
|
||||
}
|
||||
|
||||
inline DfgVertexTernary*& getEntry(CacheTernary& cache, AstNodeDType*, DfgVertex* src0p,
|
||||
DfgVertex* src1p, DfgVertex* src2p) {
|
||||
return cache
|
||||
.emplace(std::piecewise_construct, //
|
||||
std::forward_as_tuple(src0p, src1p, src2p), //
|
||||
std::forward_as_tuple(nullptr))
|
||||
.first->second;
|
||||
}
|
||||
|
||||
// These return a reference to the mapped entry, inserting a nullptr if not yet exists
|
||||
inline CacheSel::iterator find(CacheSel& cache, AstNodeDType* dtypep, DfgVertex* src0p,
|
||||
uint32_t lsb) {
|
||||
UASSERT_OBJ(VN_IS(dtypep, BasicDType), dtypep, "non-packed has no 'width()'");
|
||||
const uint32_t width = dtypep->width();
|
||||
return cache.find({src0p, lsb, width});
|
||||
}
|
||||
|
||||
inline CacheUnary::iterator find(CacheUnary& cache, AstNodeDType*, DfgVertex* src0p) {
|
||||
return cache.find({src0p});
|
||||
}
|
||||
|
||||
inline CacheBinary::iterator find(CacheBinary& cache, AstNodeDType*, DfgVertex* src0p,
|
||||
DfgVertex* src1p) {
|
||||
return cache.find({src0p, src1p});
|
||||
}
|
||||
|
||||
inline CacheTernary::iterator find(CacheTernary& cache, AstNodeDType*, DfgVertex* src0p,
|
||||
DfgVertex* src1p, DfgVertex* src2p) {
|
||||
return cache.find({src0p, src1p, src2p});
|
||||
}
|
||||
|
||||
// These set the operands of a new vertex
|
||||
inline void setOperands(DfgSel* vtxp, DfgVertex* fromp, uint32_t lsb) {
|
||||
vtxp->fromp(fromp);
|
||||
vtxp->lsb(lsb);
|
||||
}
|
||||
|
||||
inline void setOperands(DfgVertexUnary* vtxp, DfgVertex* src0p) { //
|
||||
vtxp->relinkSource<0>(src0p);
|
||||
}
|
||||
|
||||
inline void setOperands(DfgVertexBinary* vtxp, DfgVertex* src0p, DfgVertex* src1p) {
|
||||
vtxp->relinkSource<0>(src0p);
|
||||
vtxp->relinkSource<1>(src1p);
|
||||
}
|
||||
|
||||
inline void setOperands(DfgVertexTernary* vtxp, DfgVertex* src0p, DfgVertex* src1p,
|
||||
DfgVertex* src2p) {
|
||||
vtxp->relinkSource<0>(src0p);
|
||||
vtxp->relinkSource<1>(src1p);
|
||||
vtxp->relinkSource<2>(src2p);
|
||||
}
|
||||
|
||||
// Get or create (and insert) vertex with given operands
|
||||
template <typename Vertex, typename Cache, typename... Operands>
|
||||
inline Vertex* getOrCreate(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep, Cache& cache,
|
||||
Operands... operands) {
|
||||
typename Cache::mapped_type& entrypr = getEntry(cache, dtypep, operands...);
|
||||
if (!entrypr) {
|
||||
Vertex* const newp = new Vertex{dfg, flp, dtypep};
|
||||
setOperands(newp, operands...);
|
||||
entrypr = newp;
|
||||
}
|
||||
return reinterpret_cast<Vertex*>(entrypr);
|
||||
}
|
||||
|
||||
// These add an existing vertex to the table, if an equivalent does not yet exist
|
||||
inline void cache(CacheSel& cache, DfgSel* vtxp) {
|
||||
DfgSel*& entrypr = getEntry(cache, vtxp->dtypep(), vtxp->fromp(), vtxp->lsb());
|
||||
if (!entrypr) entrypr = vtxp;
|
||||
}
|
||||
|
||||
inline void cache(CacheUnary& cache, DfgVertexUnary* vtxp) {
|
||||
DfgVertexUnary*& entrypr = getEntry(cache, vtxp->dtypep(), vtxp->source<0>());
|
||||
if (!entrypr) entrypr = vtxp;
|
||||
}
|
||||
|
||||
inline void cache(CacheBinary& cache, DfgVertexBinary* vtxp) {
|
||||
DfgVertexBinary*& entrypr
|
||||
= getEntry(cache, vtxp->dtypep(), vtxp->source<0>(), vtxp->source<1>());
|
||||
if (!entrypr) entrypr = vtxp;
|
||||
}
|
||||
|
||||
inline void cache(CacheTernary& cache, DfgVertexTernary* vtxp) {
|
||||
DfgVertexTernary*& entrypr
|
||||
= getEntry(cache, vtxp->dtypep(), vtxp->source<0>(), vtxp->source<1>(), vtxp->source<2>());
|
||||
if (!entrypr) entrypr = vtxp;
|
||||
}
|
||||
|
||||
// These remove an existing vertex from the cache, if it is the cached vertex
|
||||
inline void invalidateByValue(CacheSel& cache, DfgSel* vtxp) {
|
||||
const auto it = find(cache, vtxp->dtypep(), vtxp->fromp(), vtxp->lsb());
|
||||
if (it != cache.end() && it->second == vtxp) cache.erase(it);
|
||||
}
|
||||
|
||||
inline void invalidateByValue(CacheUnary& cache, DfgVertexUnary* vtxp) {
|
||||
const auto it = find(cache, vtxp->dtypep(), vtxp->source<0>());
|
||||
if (it != cache.end() && it->second == vtxp) cache.erase(it);
|
||||
}
|
||||
|
||||
inline void invalidateByValue(CacheBinary& cache, DfgVertexBinary* vtxp) {
|
||||
const auto it = find(cache, vtxp->dtypep(), vtxp->source<0>(), vtxp->source<1>());
|
||||
if (it != cache.end() && it->second == vtxp) cache.erase(it);
|
||||
}
|
||||
|
||||
inline void invalidateByValue(CacheTernary& cache, DfgVertexTernary* vtxp) {
|
||||
const auto it
|
||||
= find(cache, vtxp->dtypep(), vtxp->source<0>(), vtxp->source<1>(), vtxp->source<2>());
|
||||
if (it != cache.end() && it->second == vtxp) cache.erase(it);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
|
||||
// This type defines the cache type corresponding to a vertex type
|
||||
template <typename Vertex> struct CacheTypeImpl : public CacheTypeImpl<typename Vertex::Super> {};
|
||||
template <> struct CacheTypeImpl<DfgSel> { using type = CacheSel; };
|
||||
template <> struct CacheTypeImpl<DfgVertexUnary> { using type = CacheUnary; };
|
||||
template <> struct CacheTypeImpl<DfgVertexBinary> { using type = CacheBinary; };
|
||||
template <> struct CacheTypeImpl<DfgVertexTernary> { using type = CacheTernary; };
|
||||
template <typename Vertex> using CacheType = typename CacheTypeImpl<Vertex>::type;
|
||||
|
||||
// Just add new lines in here for new vertex types you need to be able to cache
|
||||
|
||||
#define FOREACH_CACHED_VERTEX_TYPE(macro) \
|
||||
macro(DfgSel) \
|
||||
macro(DfgNot) \
|
||||
macro(DfgNegate) \
|
||||
macro(DfgConcat) \
|
||||
macro(DfgNeq) \
|
||||
macro(DfgShiftL) \
|
||||
macro(DfgShiftR) \
|
||||
macro(DfgShiftRS) \
|
||||
macro(DfgAdd) \
|
||||
macro(DfgSub) \
|
||||
macro(DfgMul) \
|
||||
macro(DfgMulS) \
|
||||
macro(DfgEq) \
|
||||
macro(DfgAnd) \
|
||||
macro(DfgOr) \
|
||||
macro(DfgXor) \
|
||||
macro(DfgRedAnd) \
|
||||
macro(DfgRedOr) \
|
||||
macro(DfgRedXor) \
|
||||
macro(DfgCond)
|
||||
|
||||
// clang-format on
|
||||
|
||||
class V3DfgCache final {
|
||||
DfgGraph& m_dfg; // The DfgGraph we are caching the vertices of
|
||||
|
||||
// The per type caches
|
||||
#define VERTEX_CACHE_DECLARE_LUT(t) CacheType<t> m_cache##t;
|
||||
FOREACH_CACHED_VERTEX_TYPE(VERTEX_CACHE_DECLARE_LUT)
|
||||
#undef VERTEX_CACHE_DECLARE_LUT
|
||||
|
||||
// Specializations return one of the above m_cache members
|
||||
template <typename Vertex>
|
||||
inline CacheType<Vertex>& cacheForType();
|
||||
|
||||
public:
|
||||
V3DfgCache(DfgGraph& dfg)
|
||||
: m_dfg{dfg} {}
|
||||
|
||||
// Find a vertex of type 'Vertex', with the given operands, or create a new one and add it.
|
||||
template <typename Vertex, typename... Operands>
|
||||
inline Vertex* getOrCreate(FileLine* flp, AstNodeDType* dtypep, Operands... operands);
|
||||
|
||||
// Add an existing vertex of the table. If an equivalent already exists, then nothing happens.
|
||||
void cache(DfgVertex* vtxp);
|
||||
|
||||
// Remove an exiting vertex, it is the cached vertex.
|
||||
void invalidateByValue(DfgVertex* vtxp);
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
// The per-type specializations of 'V3DfgCache::cacheForType'
|
||||
#define VERTEX_CACHE_DEFINE_LUT_SPECIALIZATION(t) \
|
||||
template <> inline CacheType<t>& V3DfgCache::cacheForType<t>() { return m_cache ## t; }
|
||||
FOREACH_CACHED_VERTEX_TYPE(VERTEX_CACHE_DEFINE_LUT_SPECIALIZATION)
|
||||
#undef VERTEX_CACHE_DEFINE_LUT_SPECIALIZATION
|
||||
// clang-format on
|
||||
|
||||
// Find a vertex of type 'Vertex', with the given operands, or create a new one and add it
|
||||
template <typename Vertex, typename... Operands>
|
||||
Vertex* V3DfgCache::getOrCreate(FileLine* flp, AstNodeDType* dtypep, Operands... operands) {
|
||||
static_assert(std::is_final<Vertex>::value, "Must invoke on final vertex type");
|
||||
constexpr bool isSel = std::is_same<DfgSel, Vertex>::value;
|
||||
constexpr bool isUnary = !isSel && std::is_base_of<DfgVertexUnary, Vertex>::value;
|
||||
constexpr bool isBinary = std::is_base_of<DfgVertexBinary, Vertex>::value;
|
||||
constexpr bool isTernary = std::is_base_of<DfgVertexTernary, Vertex>::value;
|
||||
static_assert(isSel || isUnary || isBinary || isTernary,
|
||||
"'get' called with unknown vertex type");
|
||||
|
||||
static_assert(!isSel || sizeof...(Operands) == 2, //
|
||||
"Wrong number of operands to DfgSel");
|
||||
static_assert(!isUnary || sizeof...(Operands) == 1,
|
||||
"Wrong number of operands to unary vertex");
|
||||
static_assert(!isBinary || sizeof...(Operands) == 2,
|
||||
"Wrong number of operands to binary vertex");
|
||||
static_assert(!isTernary || sizeof...(Operands) == 3,
|
||||
"Wrong number of operands to ternary vertex");
|
||||
|
||||
return V3DfgCacheInternal::getOrCreate<Vertex, CacheType<Vertex>, Operands...>(
|
||||
m_dfg, flp, dtypep, cacheForType<Vertex>(), operands...);
|
||||
}
|
||||
|
||||
} // namespace V3DfgCacheInternal
|
||||
|
||||
// Export only the public interface class
|
||||
using V3DfgCacheInternal::V3DfgCache;
|
||||
|
||||
#endif // VERILATOR_V3DFGCACHE_H_
|
||||
+39
-50
@@ -44,13 +44,12 @@ class SplitIntoComponents final {
|
||||
queue.reserve(m_dfg.size());
|
||||
|
||||
// any sort of interesting logic must involve a variable, so we only need to iterate them
|
||||
for (DfgVertexVar *vtxp = m_dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
for (DfgVertexVar& vtx : m_dfg.varVertices()) {
|
||||
// If already assigned this vertex to a component, then continue
|
||||
if (vtxp->user<size_t>()) continue;
|
||||
if (vtx.user<size_t>()) continue;
|
||||
|
||||
// Start depth first traversal at this vertex
|
||||
queue.push_back(vtxp);
|
||||
queue.push_back(&vtx);
|
||||
|
||||
// Depth first traversal
|
||||
do {
|
||||
@@ -74,16 +73,15 @@ class SplitIntoComponents final {
|
||||
}
|
||||
}
|
||||
|
||||
void moveVertices(DfgVertex* headp) {
|
||||
for (DfgVertex *vtxp = headp, *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
DfgVertex& vtx = *vtxp;
|
||||
if (const size_t component = vtx.user<size_t>()) {
|
||||
m_dfg.removeVertex(vtx);
|
||||
m_components[component - 1]->addVertex(vtx);
|
||||
template <typename Vertex>
|
||||
void moveVertices(DfgVertex::List<Vertex>& list) {
|
||||
for (DfgVertex* const vtxp : list.unlinkable()) {
|
||||
if (const size_t component = vtxp->user<size_t>()) {
|
||||
m_dfg.removeVertex(*vtxp);
|
||||
m_components[component - 1]->addVertex(*vtxp);
|
||||
} else {
|
||||
// This vertex is not connected to a variable and is hence unused, remove here
|
||||
vtx.unlinkDelete(m_dfg);
|
||||
VL_DO_DANGLING(vtxp->unlinkDelete(m_dfg), vtxp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,9 +99,9 @@ class SplitIntoComponents final {
|
||||
m_components[i - 1].reset(new DfgGraph{*m_dfg.modulep(), m_prefix + cvtToStr(i - 1)});
|
||||
}
|
||||
// Move the vertices to the component graphs
|
||||
moveVertices(m_dfg.varVerticesBeginp());
|
||||
moveVertices(m_dfg.constVerticesBeginp());
|
||||
moveVertices(m_dfg.opVerticesBeginp());
|
||||
moveVertices(m_dfg.varVertices());
|
||||
moveVertices(m_dfg.constVertices());
|
||||
moveVertices(m_dfg.opVertices());
|
||||
//
|
||||
UASSERT(m_dfg.size() == 0, "'this' DfgGraph should have been emptied");
|
||||
}
|
||||
@@ -238,9 +236,8 @@ class ExtractCyclicComponents final {
|
||||
// We can leverage some properties of the input graph to gain a bit of speed. Firstly, we
|
||||
// know constant nodes have no in edges, so they cannot be part of a non-trivial SCC. Mark
|
||||
// them as such without starting a whole traversal.
|
||||
for (DfgConst *vtxp = m_dfg.constVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
VertexState& vtxState = allocState(*vtxp);
|
||||
for (DfgConst& vtx : m_dfg.constVertices()) {
|
||||
VertexState& vtxState = allocState(vtx);
|
||||
vtxState.index = 0;
|
||||
vtxState.component = 0;
|
||||
}
|
||||
@@ -248,16 +245,15 @@ class ExtractCyclicComponents final {
|
||||
// Next, we know that all SCCs must include a variable (as the input graph was converted
|
||||
// from an AST, we can only have a cycle by going through a variable), so we only start
|
||||
// traversals through them, and only if we know they have both in and out edges.
|
||||
for (DfgVertexVar *vtxp = m_dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
if (vtxp->arity() > 0 && vtxp->hasSinks()) {
|
||||
VertexState& vtxState = getOrAllocState(*vtxp);
|
||||
for (DfgVertexVar& vtx : m_dfg.varVertices()) {
|
||||
if (vtx.arity() > 0 && vtx.hasSinks()) {
|
||||
VertexState& vtxState = getOrAllocState(vtx);
|
||||
// If not yet visited, start a traversal
|
||||
if (vtxState.index == UNASSIGNED) visitColorSCCs(*vtxp, vtxState);
|
||||
if (vtxState.index == UNASSIGNED) visitColorSCCs(vtx, vtxState);
|
||||
} else {
|
||||
VertexState& vtxState = getOrAllocState(*vtxp);
|
||||
VertexState& vtxState = getOrAllocState(vtx);
|
||||
UDEBUGONLY(UASSERT_OBJ(vtxState.index == UNASSIGNED || vtxState.component == 0,
|
||||
vtxp, "Non circular variable must be in a trivial SCC"););
|
||||
&vtx, "Non circular variable must be in a trivial SCC"););
|
||||
vtxState.index = 0;
|
||||
vtxState.component = 0;
|
||||
}
|
||||
@@ -265,9 +261,8 @@ class ExtractCyclicComponents final {
|
||||
|
||||
// Finally, everything we did not visit through the traversal of a variable cannot be in an
|
||||
// SCC, (otherwise we would have found it from a variable).
|
||||
for (DfgVertex *vtxp = m_dfg.opVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
VertexState& vtxState = getOrAllocState(*vtxp);
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
VertexState& vtxState = getOrAllocState(vtx);
|
||||
if (vtxState.index == UNASSIGNED) {
|
||||
vtxState.index = 0;
|
||||
vtxState.component = 0;
|
||||
@@ -306,9 +301,7 @@ class ExtractCyclicComponents final {
|
||||
// Ensure that component boundaries are always at variables, by merging SCCs. Merging stops
|
||||
// at variable boundaries, so we don't need to iterate variables. Constants are reachable
|
||||
// from their sinks, or are unused, so we don't need to iterate them either.
|
||||
for (DfgVertex *vtxp = m_dfg.opVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
DfgVertex& vtx = *vtxp;
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
// Start DFS from each vertex that is in a non-trivial SCC, and merge everything
|
||||
// that is reachable from it into this component.
|
||||
if (const size_t target = state(vtx).component) visitMergeSCCs(vtx, target);
|
||||
@@ -329,11 +322,13 @@ class ExtractCyclicComponents final {
|
||||
clonep = new DfgVarArray{m_dfg, aVtxp->varp()};
|
||||
}
|
||||
UASSERT_OBJ(clonep, &vtx, "Unhandled 'DfgVertexVar' sub-type");
|
||||
if (vtx.hasModRefs()) clonep->setHasModRefs();
|
||||
if (vtx.hasExtRefs()) clonep->setHasExtRefs();
|
||||
VertexState& cloneStatep = allocState(*clonep);
|
||||
cloneStatep.component = component;
|
||||
// We need to mark both the original and the clone as having additional references
|
||||
vtx.setHasModRefs();
|
||||
clonep->setHasModRefs();
|
||||
// We need to mark both the original and the clone as having references in other DFGs
|
||||
vtx.setHasDfgRefs();
|
||||
clonep->setHasDfgRefs();
|
||||
}
|
||||
return *clonep;
|
||||
}
|
||||
@@ -398,8 +393,7 @@ class ExtractCyclicComponents final {
|
||||
|
||||
static void packSources(DfgGraph& dfg) {
|
||||
// Remove undriven variable sources
|
||||
for (DfgVertexVar *vtxp = dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
for (DfgVertexVar* const vtxp : dfg.varVertices().unlinkable()) {
|
||||
if (DfgVarPacked* const varp = vtxp->cast<DfgVarPacked>()) {
|
||||
varp->packSources();
|
||||
if (!varp->hasSinks() && varp->arity() == 0) {
|
||||
@@ -417,9 +411,9 @@ class ExtractCyclicComponents final {
|
||||
}
|
||||
}
|
||||
|
||||
void moveVertices(DfgVertex* headp) {
|
||||
for (DfgVertex *vtxp = headp, *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
template <typename Vertex>
|
||||
void moveVertices(DfgVertex::List<Vertex>& list) {
|
||||
for (DfgVertex* const vtxp : list.unlinkable()) {
|
||||
DfgVertex& vtx = *vtxp;
|
||||
if (const size_t component = state(vtx).component) {
|
||||
m_dfg.removeVertex(vtx);
|
||||
@@ -479,17 +473,12 @@ class ExtractCyclicComponents final {
|
||||
// earlier merging of components ensured crossing in fact only happen at variable
|
||||
// boundaries). Note that fixing up the edges can create clones of variables. Clones do
|
||||
// not need fixing up, so we do not need to iterate them.
|
||||
DfgVertex* const lastp = m_dfg.varVerticesRbeginp();
|
||||
for (DfgVertexVar *vtxp = m_dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
// It is possible the last vertex (with a nullptr for 'nextp') gets cloned, and hence
|
||||
// it's 'nextp' would become none nullptr as the clone is added. However, we don't need
|
||||
// to iterate clones anyway, so it's ok to get the 'nextp' early in the loop.
|
||||
nextp = vtxp->verticesNext();
|
||||
DfgVertexVar& vtx = *vtxp;
|
||||
DfgVertex* const lastp = m_dfg.varVertices().backp();
|
||||
for (DfgVertexVar& vtx : m_dfg.varVertices()) {
|
||||
// Fix up the edges crossing components
|
||||
fixEdges(vtx);
|
||||
// Don't iterate clones added during this loop
|
||||
if (vtxp == lastp) break;
|
||||
if (&vtx == lastp) break;
|
||||
}
|
||||
|
||||
// Pack sources of variables to remove the now undriven inputs
|
||||
@@ -505,9 +494,9 @@ class ExtractCyclicComponents final {
|
||||
|
||||
// Move other vertices to their component graphs
|
||||
// After this, vertex states are invalid as we moved the vertices
|
||||
moveVertices(m_dfg.varVerticesBeginp());
|
||||
moveVertices(m_dfg.constVerticesBeginp());
|
||||
moveVertices(m_dfg.opVerticesBeginp());
|
||||
moveVertices(m_dfg.varVertices());
|
||||
moveVertices(m_dfg.constVertices());
|
||||
moveVertices(m_dfg.opVertices());
|
||||
|
||||
// Check results for consistency
|
||||
if (VL_UNLIKELY(m_doExpensiveChecks)) {
|
||||
|
||||
+22
-203
@@ -126,11 +126,6 @@ AstSliceSel* makeNode<AstSliceSel, DfgSliceSel, AstNodeExpr*, AstNodeExpr*, AstN
|
||||
} // namespace
|
||||
|
||||
class DfgToAstVisitor final : DfgVisitor {
|
||||
// NODE STATE
|
||||
// AstVar::user1() bool: this is a temporary we are introducing
|
||||
|
||||
const VNUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
|
||||
AstModule* const m_modp; // The parent/result module
|
||||
@@ -144,81 +139,11 @@ class DfgToAstVisitor final : DfgVisitor {
|
||||
|
||||
// METHODS
|
||||
|
||||
// Given a DfgVarPacked, return the canonical AstVar that can be used for this DfgVarPacked.
|
||||
// Also builds the m_canonVars map as a side effect.
|
||||
AstVar* getCanonicalVar(const DfgVarPacked* vtxp) {
|
||||
// If variable driven (at least partially) outside the DFG, then we have no choice
|
||||
if (!vtxp->isDrivenFullyByDfg()) return vtxp->varp();
|
||||
|
||||
// Look up map
|
||||
const auto it = m_canonVars.find(vtxp->varp());
|
||||
if (it != m_canonVars.end()) return it->second;
|
||||
|
||||
// Not known yet, compute it (for all vars driven fully from the same driver)
|
||||
std::vector<const DfgVarPacked*> varps;
|
||||
vtxp->source(0)->forEachSink([&](const DfgVertex& vtx) {
|
||||
if (const DfgVarPacked* const varVtxp = vtx.cast<DfgVarPacked>()) {
|
||||
if (varVtxp->isDrivenFullyByDfg()) varps.push_back(varVtxp);
|
||||
}
|
||||
});
|
||||
UASSERT_OBJ(!varps.empty(), vtxp, "The input vtxp is always available");
|
||||
std::stable_sort(varps.begin(), varps.end(),
|
||||
[](const DfgVarPacked* ap, const DfgVarPacked* bp) {
|
||||
if (ap->hasExtRefs() != bp->hasExtRefs()) return ap->hasExtRefs();
|
||||
const FileLine& aFl = *(ap->fileline());
|
||||
const FileLine& bFl = *(bp->fileline());
|
||||
if (const int cmp = aFl.operatorCompare(bFl)) return cmp < 0;
|
||||
return ap->varp()->name() < bp->varp()->name();
|
||||
});
|
||||
AstVar* const canonVarp = varps.front()->varp();
|
||||
|
||||
// Add results to map
|
||||
for (const DfgVarPacked* const varp : varps) m_canonVars.emplace(varp->varp(), canonVarp);
|
||||
|
||||
// Return it
|
||||
return canonVarp;
|
||||
}
|
||||
|
||||
// Given a DfgVertex, return an AstVar that will hold the value of the given DfgVertex once we
|
||||
// are done with converting this Dfg into Ast form.
|
||||
AstVar* getResultVar(DfgVertex* vtxp) {
|
||||
const auto pair = m_resultVars.emplace(vtxp, nullptr);
|
||||
AstVar*& varp = pair.first->second;
|
||||
if (pair.second) {
|
||||
// If this vertex is a DfgVarPacked, then we know the variable. If this node is not a
|
||||
// DfgVarPacked, then first we try to find a DfgVarPacked driven by this node, and use
|
||||
// that, otherwise we create a temporary
|
||||
if (const DfgVarPacked* const thisDfgVarPackedp = vtxp->cast<DfgVarPacked>()) {
|
||||
// This is a DfgVarPacked
|
||||
varp = getCanonicalVar(thisDfgVarPackedp);
|
||||
} else if (const DfgVarArray* const thisDfgVarArrayp = vtxp->cast<DfgVarArray>()) {
|
||||
// This is a DfgVarArray
|
||||
varp = thisDfgVarArrayp->varp();
|
||||
} else if (const DfgVarPacked* const sinkDfgVarPackedp = vtxp->findSink<DfgVarPacked>(
|
||||
[](const DfgVarPacked& var) { return var.isDrivenFullyByDfg(); })) {
|
||||
// We found a DfgVarPacked driven fully by this node
|
||||
varp = getCanonicalVar(sinkDfgVarPackedp);
|
||||
} else {
|
||||
// No DfgVarPacked driven fully by this node. Create a temporary.
|
||||
// TODO: should we reuse parts when the AstVar is used as an rvalue?
|
||||
const string name = m_tmpNames.get(vtxp->hash().toString());
|
||||
// Note: It is ok for these temporary variables to be always unsigned. They are
|
||||
// read only by other expressions within the graph and all expressions interpret
|
||||
// their operands based on the expression type, not the operand type.
|
||||
AstNodeDType* const dtypep = v3Global.rootp()->findBitDType(
|
||||
vtxp->width(), vtxp->width(), VSigning::UNSIGNED);
|
||||
varp = new AstVar{vtxp->fileline(), VVarType::MODULETEMP, name, dtypep};
|
||||
varp->user1(true); // Mark as temporary
|
||||
// Add temporary AstVar to containing module
|
||||
m_modp->addStmtsp(varp);
|
||||
}
|
||||
// Add to map
|
||||
}
|
||||
return varp;
|
||||
}
|
||||
|
||||
AstNodeExpr* convertDfgVertexToAstNodeExpr(DfgVertex* vtxp) {
|
||||
UASSERT_OBJ(!m_resultp, vtxp, "Result already computed");
|
||||
UASSERT_OBJ(!vtxp->hasMultipleSinks() || vtxp->is<DfgVertexVar>()
|
||||
|| vtxp->is<DfgArraySel>() || vtxp->is<DfgConst>(),
|
||||
vtxp, "Intermediate DFG value with multiple uses");
|
||||
iterate(vtxp);
|
||||
UASSERT_OBJ(m_resultp, vtxp, "Missing result");
|
||||
AstNodeExpr* const resultp = m_resultp;
|
||||
@@ -226,25 +151,18 @@ class DfgToAstVisitor final : DfgVisitor {
|
||||
return resultp;
|
||||
}
|
||||
|
||||
AstNodeExpr* convertSource(DfgVertex* vtxp) {
|
||||
if (vtxp->inlined()) {
|
||||
// Inlined vertices are simply recursively converted
|
||||
UASSERT_OBJ(vtxp->hasSinks(), vtxp, "Must have one sink: " << vtxp->typeName());
|
||||
return convertDfgVertexToAstNodeExpr(vtxp);
|
||||
} else {
|
||||
// Vertices that are not inlined need a variable, just return a reference
|
||||
return new AstVarRef{vtxp->fileline(), getResultVar(vtxp), VAccess::READ};
|
||||
}
|
||||
void addResultEquation(FileLine* flp, AstNodeExpr* lhsp, AstNodeExpr* rhsp) {
|
||||
m_modp->addStmtsp(new AstAssignW{flp, lhsp, rhsp});
|
||||
++m_ctx.m_resultEquations;
|
||||
}
|
||||
|
||||
void convertCanonicalVarDriver(const DfgVarPacked* dfgVarp) {
|
||||
const auto wRef = [dfgVarp]() {
|
||||
return new AstVarRef{dfgVarp->fileline(), dfgVarp->varp(), VAccess::WRITE};
|
||||
};
|
||||
void convertVarDriver(const DfgVarPacked* dfgVarp) {
|
||||
if (dfgVarp->isDrivenFullyByDfg()) {
|
||||
// Whole variable is driven. Render driver and assign directly to whole variable.
|
||||
FileLine* const flp = dfgVarp->driverFileLine(0);
|
||||
AstVarRef* const lhsp = new AstVarRef{flp, dfgVarp->varp(), VAccess::WRITE};
|
||||
AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(dfgVarp->source(0));
|
||||
addResultEquation(dfgVarp->driverFileLine(0), wRef(), rhsp);
|
||||
addResultEquation(flp, lhsp, rhsp);
|
||||
} else {
|
||||
// Variable is driven partially. Render each driver as a separate assignment.
|
||||
dfgVarp->forEachSourceEdge([&](const DfgEdge& edge, size_t idx) {
|
||||
@@ -253,35 +171,10 @@ class DfgToAstVisitor final : DfgVisitor {
|
||||
AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(edge.sourcep());
|
||||
// Create select LValue
|
||||
FileLine* const flp = dfgVarp->driverFileLine(idx);
|
||||
AstVarRef* const refp = new AstVarRef{flp, dfgVarp->varp(), VAccess::WRITE};
|
||||
AstConst* const lsbp = new AstConst{flp, dfgVarp->driverLsb(idx)};
|
||||
AstConst* const widthp = new AstConst{flp, edge.sourcep()->width()};
|
||||
AstSel* const lhsp = new AstSel{flp, wRef(), lsbp, widthp};
|
||||
// Add assignment of the value to the selected bits
|
||||
addResultEquation(flp, lhsp, rhsp);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void convertDuplicateVarDriver(const DfgVarPacked* dfgVarp, AstVar* canonVarp) {
|
||||
const auto rRef = [canonVarp]() {
|
||||
return new AstVarRef{canonVarp->fileline(), canonVarp, VAccess::READ};
|
||||
};
|
||||
const auto wRef = [dfgVarp]() {
|
||||
return new AstVarRef{dfgVarp->fileline(), dfgVarp->varp(), VAccess::WRITE};
|
||||
};
|
||||
if (dfgVarp->isDrivenFullyByDfg()) {
|
||||
// Whole variable is driven. Just assign from the canonical variable.
|
||||
addResultEquation(dfgVarp->driverFileLine(0), wRef(), rRef());
|
||||
} else {
|
||||
// Variable is driven partially. Assign from parts of the canonical var.
|
||||
dfgVarp->forEachSourceEdge([&](const DfgEdge& edge, size_t idx) {
|
||||
UASSERT_OBJ(edge.sourcep(), dfgVarp, "Should have removed undriven sources");
|
||||
// Create select LValue
|
||||
FileLine* const flp = dfgVarp->driverFileLine(idx);
|
||||
AstConst* const lsbp = new AstConst{flp, dfgVarp->driverLsb(idx)};
|
||||
AstConst* const widthp = new AstConst{flp, edge.sourcep()->width()};
|
||||
AstSel* const rhsp = new AstSel{flp, rRef(), lsbp, widthp->cloneTreePure(false)};
|
||||
AstSel* const lhsp = new AstSel{flp, wRef(), lsbp->cloneTreePure(false), widthp};
|
||||
AstSel* const lhsp = new AstSel{flp, refp, lsbp, widthp};
|
||||
// Add assignment of the value to the selected bits
|
||||
addResultEquation(flp, lhsp, rhsp);
|
||||
});
|
||||
@@ -304,18 +197,13 @@ class DfgToAstVisitor final : DfgVisitor {
|
||||
});
|
||||
}
|
||||
|
||||
void addResultEquation(FileLine* flp, AstNodeExpr* lhsp, AstNodeExpr* rhsp) {
|
||||
m_modp->addStmtsp(new AstAssignW{flp, lhsp, rhsp});
|
||||
++m_ctx.m_resultEquations;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(DfgVertex* vtxp) override { // LCOV_EXCL_START
|
||||
vtxp->v3fatal("Unhandled DfgVertex: " << vtxp->typeName());
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
void visit(DfgVarPacked* vtxp) override {
|
||||
m_resultp = new AstVarRef{vtxp->fileline(), getCanonicalVar(vtxp), VAccess::READ};
|
||||
m_resultp = new AstVarRef{vtxp->fileline(), vtxp->varp(), VAccess::READ};
|
||||
}
|
||||
|
||||
void visit(DfgVarArray* vtxp) override {
|
||||
@@ -328,7 +216,7 @@ class DfgToAstVisitor final : DfgVisitor {
|
||||
|
||||
void visit(DfgSel* vtxp) override {
|
||||
FileLine* const flp = vtxp->fileline();
|
||||
AstNodeExpr* const fromp = convertSource(vtxp->fromp());
|
||||
AstNodeExpr* const fromp = convertDfgVertexToAstNodeExpr(vtxp->fromp());
|
||||
AstConst* const lsbp = new AstConst{flp, vtxp->lsb()};
|
||||
AstConst* const widthp = new AstConst{flp, vtxp->width()};
|
||||
m_resultp = new AstSel{flp, fromp, lsbp, widthp};
|
||||
@@ -336,8 +224,8 @@ class DfgToAstVisitor final : DfgVisitor {
|
||||
|
||||
void visit(DfgMux* vtxp) override {
|
||||
FileLine* const flp = vtxp->fileline();
|
||||
AstNodeExpr* const fromp = convertSource(vtxp->fromp());
|
||||
AstNodeExpr* const lsbp = convertSource(vtxp->lsbp());
|
||||
AstNodeExpr* const fromp = convertDfgVertexToAstNodeExpr(vtxp->fromp());
|
||||
AstNodeExpr* const lsbp = convertDfgVertexToAstNodeExpr(vtxp->lsbp());
|
||||
AstConst* const widthp = new AstConst{flp, vtxp->width()};
|
||||
m_resultp = new AstSel{flp, fromp, lsbp, widthp};
|
||||
}
|
||||
@@ -351,89 +239,20 @@ class DfgToAstVisitor final : DfgVisitor {
|
||||
, m_ctx{ctx} {
|
||||
// Convert the graph back to combinational assignments
|
||||
|
||||
// Used by DfgVertex::hash
|
||||
const auto userDataInUse = dfg.userDataInUse();
|
||||
|
||||
// We can eliminate some variables completely
|
||||
std::vector<AstVar*> redundantVarps;
|
||||
|
||||
// First render variable assignments
|
||||
for (DfgVertexVar *vtxp = dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
|
||||
// The graph must have been regularized, so we only need to render assignments
|
||||
for (DfgVertexVar& vtx : dfg.varVertices()) {
|
||||
// If there is no driver (this vertex is an input to the graph), then nothing to do.
|
||||
if (!vtxp->isDrivenByDfg()) continue;
|
||||
if (!vtx.isDrivenByDfg()) continue;
|
||||
|
||||
// Render packed variable assignments
|
||||
if (const DfgVarPacked* const dfgVarp = vtxp->cast<DfgVarPacked>()) {
|
||||
// The driver of this DfgVarPacked might drive multiple variables. Only emit one
|
||||
// assignment from the driver to an arbitrarily chosen canonical variable, and
|
||||
// assign the other variables from that canonical variable
|
||||
AstVar* const canonVarp = getCanonicalVar(dfgVarp);
|
||||
if (canonVarp == dfgVarp->varp()) {
|
||||
// This is the canonical variable, so render the driver
|
||||
convertCanonicalVarDriver(dfgVarp);
|
||||
} else if (dfgVarp->keep()) {
|
||||
// Not the canonical variable but it must be kept
|
||||
convertDuplicateVarDriver(dfgVarp, canonVarp);
|
||||
} else {
|
||||
// Not a canonical var, and it can be removed. We will replace all references
|
||||
// to it with the canonical variable, and hence this can be removed.
|
||||
redundantVarps.push_back(dfgVarp->varp());
|
||||
++m_ctx.m_replacedVars;
|
||||
}
|
||||
// Done
|
||||
if (const DfgVarPacked* const dfgVarp = vtx.cast<DfgVarPacked>()) {
|
||||
convertVarDriver(dfgVarp);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Render array variable assignments
|
||||
if (const DfgVarArray* dfgVarp = vtxp->cast<DfgVarArray>()) {
|
||||
// We don't canonicalize arrays, so just render the drivers
|
||||
convertArrayDiver(dfgVarp);
|
||||
// Done
|
||||
continue;
|
||||
}
|
||||
convertArrayDiver(vtx.as<DfgVarArray>());
|
||||
}
|
||||
|
||||
// Constants are always inlined, so we only need to iterate proper operations
|
||||
for (DfgVertex *vtxp = dfg.opVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
|
||||
// If the vertex is known to be inlined, then there is nothing to do
|
||||
if (vtxp->inlined()) continue;
|
||||
|
||||
// Check if this uses a temporary, vs one of the vars rendered above
|
||||
AstVar* const resultVarp = getResultVar(vtxp);
|
||||
if (resultVarp->user1()) {
|
||||
// We introduced a temporary for this DfgVertex
|
||||
++m_ctx.m_intermediateVars;
|
||||
FileLine* const flp = vtxp->fileline();
|
||||
// Just render the logic
|
||||
AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(vtxp);
|
||||
// The lhs is the temporary
|
||||
AstNodeExpr* const lhsp = new AstVarRef{flp, resultVarp, VAccess::WRITE};
|
||||
// Add assignment of the value to the variable
|
||||
addResultEquation(flp, lhsp, rhsp);
|
||||
}
|
||||
}
|
||||
|
||||
// Remap all references to point to the canonical variables, if one exists
|
||||
VNDeleter deleter;
|
||||
m_modp->foreach([&](AstVarRef* refp) {
|
||||
// Any variable that is written partially outside the DFG will have itself as the
|
||||
// canonical var, so need not be replaced, furthermore, if a variable is traced, we
|
||||
// don't want to update the write-refs we just created above, so we only replace
|
||||
// read-only references to those variables to those variables we know are not written
|
||||
// in non-DFG logic.
|
||||
if (!refp->access().isReadOnly() || refp->varp()->user3()) return;
|
||||
const auto it = m_canonVars.find(refp->varp());
|
||||
if (it == m_canonVars.end() || it->second == refp->varp()) return;
|
||||
refp->replaceWith(new AstVarRef{refp->fileline(), it->second, refp->access()});
|
||||
deleter.pushDeletep(refp);
|
||||
});
|
||||
|
||||
// Remove redundant variables
|
||||
for (AstVar* const varp : redundantVarps) varp->unlinkFrBack()->deleteTree();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
+13
-35
@@ -25,8 +25,6 @@
|
||||
#include "V3AstUserAllocator.h"
|
||||
#include "V3Dfg.h"
|
||||
#include "V3DfgPasses.h"
|
||||
#include "V3DfgPatternStats.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Graph.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
@@ -242,7 +240,7 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
|
||||
// NODE STATE
|
||||
// AstVar::user1 -> Used by V3DfgPasses::astToDfg and DfgPassed::dfgToAst
|
||||
// AstVar::user1 -> Used by V3DfgPasses::astToDfg, V3DfgPasses::eliminateVars
|
||||
// AstVar::user2 -> bool: Flag indicating referenced by AstVarXRef (set just below)
|
||||
// AstVar::user3 -> bool: Flag indicating written by logic not representable as DFG
|
||||
// (set by V3DfgPasses::astToDfg)
|
||||
@@ -254,8 +252,6 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
|
||||
|
||||
V3DfgOptimizationContext ctx{label};
|
||||
|
||||
V3DfgPatternStats patternStats;
|
||||
|
||||
// Run the optimization phase
|
||||
for (AstNode* nodep = netlistp->modulesp(); nodep; nodep = nodep->nextp()) {
|
||||
// Only optimize proper modules
|
||||
@@ -282,14 +278,6 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
|
||||
// Quick sanity check
|
||||
UASSERT_OBJ(dfg->size() == 0, nodep, "DfgGraph should have become empty");
|
||||
|
||||
// For each cyclic component
|
||||
for (auto& component : cyclicComponents) {
|
||||
if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
|
||||
// TODO: Apply optimizations safe for cyclic graphs
|
||||
// Add back under the main DFG (we will convert everything back in one go)
|
||||
dfg->addGraph(*component);
|
||||
}
|
||||
|
||||
// For each acyclic component
|
||||
for (auto& component : acyclicComponents) {
|
||||
if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
|
||||
@@ -299,8 +287,18 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
|
||||
dfg->addGraph(*component);
|
||||
}
|
||||
|
||||
// Accumulate patterns from the optimized graph for reporting
|
||||
if (v3Global.opt.stats()) patternStats.accumulate(*dfg);
|
||||
// Eliminate redundant variables. Run this on the whole acyclic DFG. It needs to traverse
|
||||
// the module to perform variable substitutions. Doing this by component would do
|
||||
// redundant traversals and can be extremely slow in large modules with many components.
|
||||
V3DfgPasses::eliminateVars(*dfg, ctx.m_eliminateVarsContext);
|
||||
|
||||
// For each cyclic component
|
||||
for (auto& component : cyclicComponents) {
|
||||
if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
|
||||
// TODO: Apply optimizations safe for cyclic graphs
|
||||
// Add back under the main DFG (we will convert everything back in one go)
|
||||
dfg->addGraph(*component);
|
||||
}
|
||||
|
||||
// Convert back to Ast
|
||||
if (dumpDfgLevel() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-optimized");
|
||||
@@ -308,25 +306,5 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
|
||||
UASSERT_OBJ(resultModp == modp, modp, "Should be the same module");
|
||||
}
|
||||
|
||||
// Print the collected patterns
|
||||
if (v3Global.opt.stats()) {
|
||||
// Label to lowercase, without spaces
|
||||
std::string ident = label;
|
||||
std::transform(ident.begin(), ident.end(), ident.begin(), [](unsigned char c) { //
|
||||
return c == ' ' ? '_' : std::tolower(c);
|
||||
});
|
||||
|
||||
// File to dump to
|
||||
const std::string filename = v3Global.opt.hierTopDataDir() + "/" + v3Global.opt.prefix()
|
||||
+ "__stats_dfg_patterns__" + ident + ".txt";
|
||||
|
||||
// Open, write, close
|
||||
std::ofstream* const ofp = V3File::new_ofstream(filename);
|
||||
if (ofp->fail()) v3fatal("Can't write " << filename);
|
||||
patternStats.dump(label, *ofp);
|
||||
ofp->close();
|
||||
VL_DO_DANGLING(delete ofp, ofp);
|
||||
}
|
||||
|
||||
V3Global::dumpCheckGlobalTree("dfg-optimize", 0, dumpTreeEitherLevel() >= 3);
|
||||
}
|
||||
|
||||
+159
-91
@@ -19,6 +19,7 @@
|
||||
#include "V3DfgPasses.h"
|
||||
|
||||
#include "V3Dfg.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3String.h"
|
||||
|
||||
@@ -29,9 +30,16 @@ V3DfgCseContext::~V3DfgCseContext() {
|
||||
m_eliminated);
|
||||
}
|
||||
|
||||
DfgRemoveVarsContext::~DfgRemoveVarsContext() {
|
||||
V3Stats::addStat("Optimizations, DFG " + m_label + " Remove vars, variables removed",
|
||||
m_removed);
|
||||
V3DfgRegularizeContext::~V3DfgRegularizeContext() {
|
||||
V3Stats::addStat("Optimizations, DFG " + m_label + " Regularize, temporaries introduced",
|
||||
m_temporariesIntroduced);
|
||||
}
|
||||
|
||||
V3DfgEliminateVarsContext::~V3DfgEliminateVarsContext() {
|
||||
V3Stats::addStat("Optimizations, DFG " + m_label + " EliminateVars, variables replaced",
|
||||
m_varsReplaced);
|
||||
V3Stats::addStat("Optimizations, DFG " + m_label + " EliminateVars, variables removed",
|
||||
m_varsRemoved);
|
||||
}
|
||||
|
||||
static std::string getPrefix(const std::string& label) {
|
||||
@@ -62,10 +70,25 @@ V3DfgOptimizationContext::~V3DfgOptimizationContext() {
|
||||
V3Stats::addStat(prefix + "Ast2Dfg, non-representable (unknown)", m_nonRepUnknown);
|
||||
V3Stats::addStat(prefix + "Ast2Dfg, non-representable (var ref)", m_nonRepVarRef);
|
||||
V3Stats::addStat(prefix + "Ast2Dfg, non-representable (width)", m_nonRepWidth);
|
||||
V3Stats::addStat(prefix + "Dfg2Ast, intermediate variables", m_intermediateVars);
|
||||
V3Stats::addStat(prefix + "Dfg2Ast, replaced variables", m_replacedVars);
|
||||
V3Stats::addStat(prefix + "Dfg2Ast, result equations", m_resultEquations);
|
||||
|
||||
// Print the collected patterns
|
||||
if (v3Global.opt.stats()) {
|
||||
// Label to lowercase, without spaces
|
||||
std::string ident = m_label;
|
||||
std::transform(ident.begin(), ident.end(), ident.begin(), [](unsigned char c) { //
|
||||
return c == ' ' ? '_' : std::tolower(c);
|
||||
});
|
||||
|
||||
// File to dump to
|
||||
const std::string filename = v3Global.opt.hierTopDataDir() + "/" + v3Global.opt.prefix()
|
||||
+ "__stats_dfg_patterns__" + ident + ".txt";
|
||||
// Open, write, close
|
||||
const std::unique_ptr<std::ofstream> ofp{V3File::new_ofstream(filename)};
|
||||
if (ofp->fail()) v3fatal("Can't write " << filename);
|
||||
m_patternStats.dump(m_label, *ofp);
|
||||
}
|
||||
|
||||
// Check the stats are consistent
|
||||
UASSERT(m_inputEquations
|
||||
== m_representable + m_nonRepDType + m_nonRepImpure + m_nonRepTiming + m_nonRepLhs
|
||||
@@ -86,29 +109,22 @@ void V3DfgPasses::cse(DfgGraph& dfg, V3DfgCseContext& ctx) {
|
||||
|
||||
// Pre-hash variables, these are all unique, so just set their hash to a unique value
|
||||
uint32_t varHash = 0;
|
||||
for (DfgVertexVar *vtxp = dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
vtxp->user<V3Hash>() = V3Hash{++varHash};
|
||||
}
|
||||
for (DfgVertexVar& vtx : dfg.varVertices()) vtx.user<V3Hash>() = V3Hash{++varHash};
|
||||
|
||||
// Similarly pre-hash constants for speed. While we don't combine constants, we do want
|
||||
// expressions using the same constants to be combined, so we do need to hash equal
|
||||
// constants to equal values.
|
||||
for (DfgConst *vtxp = dfg.constVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
if (VL_LIKELY(nextp)) VL_PREFETCH_RW(nextp);
|
||||
for (DfgConst* const vtxp : dfg.constVertices().unlinkable()) {
|
||||
// Delete unused constants while we are at it.
|
||||
if (!vtxp->hasSinks()) {
|
||||
vtxp->unlinkDelete(dfg);
|
||||
VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp);
|
||||
continue;
|
||||
}
|
||||
vtxp->user<V3Hash>() = vtxp->num().toHash() + varHash;
|
||||
}
|
||||
|
||||
// Combine operation vertices
|
||||
for (DfgVertex *vtxp = dfg.opVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
if (VL_LIKELY(nextp)) VL_PREFETCH_RW(nextp);
|
||||
for (DfgVertex* const vtxp : dfg.opVertices().unlinkable()) {
|
||||
// Delete unused nodes while we are at it.
|
||||
if (!vtxp->hasSinks()) {
|
||||
vtxp->unlinkDelete(dfg);
|
||||
@@ -121,7 +137,7 @@ void V3DfgPasses::cse(DfgGraph& dfg, V3DfgCseContext& ctx) {
|
||||
if (candidatep->equals(*vtxp, equalsCache)) {
|
||||
++ctx.m_eliminated;
|
||||
vtxp->replaceWith(candidatep);
|
||||
vtxp->unlinkDelete(dfg);
|
||||
VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp);
|
||||
replaced = true;
|
||||
break;
|
||||
}
|
||||
@@ -135,18 +151,22 @@ void V3DfgPasses::cse(DfgGraph& dfg, V3DfgCseContext& ctx) {
|
||||
removeUnused(dfg);
|
||||
}
|
||||
|
||||
void V3DfgPasses::inlineVars(const DfgGraph& dfg) {
|
||||
for (DfgVertexVar *vtxp = dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
if (DfgVarPacked* const varp = vtxp->cast<DfgVarPacked>()) {
|
||||
void V3DfgPasses::inlineVars(DfgGraph& dfg) {
|
||||
for (DfgVertexVar& vtx : dfg.varVertices()) {
|
||||
if (DfgVarPacked* const varp = vtx.cast<DfgVarPacked>()) {
|
||||
// Don't inline SystemC variables, as SystemC types are not interchangeable with
|
||||
// internal types, and hence the variables are not interchangeable either.
|
||||
if (varp->hasSinks() && varp->isDrivenFullyByDfg() && !varp->varp()->isSc()) {
|
||||
DfgVertex* const driverp = varp->source(0);
|
||||
|
||||
// If driven from a SystemC variable, don't inline this variable
|
||||
// We must keep the original driver in certain cases, when swapping them would
|
||||
// not be functionally or technically (implementation reasons) equivalent
|
||||
if (DfgVertexVar* const driverVarp = driverp->cast<DfgVarPacked>()) {
|
||||
if (driverVarp->varp()->isSc()) continue;
|
||||
const AstVar* const astVarp = driverVarp->varp();
|
||||
// If driven from a SystemC variable
|
||||
if (astVarp->isSc()) continue;
|
||||
// If the variable is forceable
|
||||
if (astVarp->isForceable()) continue;
|
||||
}
|
||||
|
||||
varp->forEachSinkEdge([=](DfgEdge& edge) {
|
||||
@@ -161,63 +181,6 @@ void V3DfgPasses::inlineVars(const DfgGraph& dfg) {
|
||||
}
|
||||
}
|
||||
|
||||
void V3DfgPasses::removeVars(DfgGraph& dfg, DfgRemoveVarsContext& ctx) {
|
||||
for (DfgVertexVar *vtxp = dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
|
||||
// We can only eliminate DfgVarPacked vertices at the moment
|
||||
DfgVarPacked* const varp = vtxp->cast<DfgVarPacked>();
|
||||
if (!varp) continue;
|
||||
|
||||
// Can't remove if it has consumers
|
||||
if (varp->hasSinks()) continue;
|
||||
|
||||
// Otherwise if it has drivers
|
||||
if (varp->isDrivenByDfg()) {
|
||||
// Can't remove if read in the module and driven here (i.e.: it's an output of the DFG)
|
||||
if (varp->hasModRefs()) continue;
|
||||
|
||||
// Can't remove if referenced externally, or other special reasons
|
||||
if (varp->keep()) continue;
|
||||
|
||||
// If the driver of this variable is not an inlined vertex, then we would need a
|
||||
// temporary when rendering the graph. Instead of introducing a temporary, keep the
|
||||
// first variable that is driven by that driver. Note that we still remove if the only
|
||||
// sinks we have are variables, as we might be able to remove all of them (we can be
|
||||
// sure the not inlined if we have at least 2 non-variable sinks).
|
||||
if (varp->isDrivenFullyByDfg()) {
|
||||
DfgVertex* const driverp = varp->source(0);
|
||||
if (!driverp->inlined()) {
|
||||
unsigned nonVarSinks = 0;
|
||||
const DfgVarPacked* firstp = nullptr;
|
||||
const bool found = driverp->findSink<DfgVertex>([&](const DfgVertex& sink) {
|
||||
if (const DfgVarPacked* const sinkVarp = sink.cast<DfgVarPacked>()) {
|
||||
if (!firstp) firstp = sinkVarp;
|
||||
} else {
|
||||
++nonVarSinks;
|
||||
}
|
||||
// We can stop as soon as we found the first var, and 2 non-var sinks
|
||||
return firstp && nonVarSinks >= 2;
|
||||
});
|
||||
// Keep this DfgVarPacked if needed
|
||||
if (found && firstp == varp) continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OK, we can delete this DfgVarPacked from the graph.
|
||||
|
||||
// If not referenced outside the DFG, then also delete the referenced AstVar (now unused).
|
||||
if (!varp->hasRefs()) {
|
||||
++ctx.m_removed;
|
||||
varp->varp()->unlinkFrBack()->deleteTree();
|
||||
}
|
||||
|
||||
// Unlink and delete vertex
|
||||
varp->unlinkDelete(dfg);
|
||||
}
|
||||
}
|
||||
|
||||
void V3DfgPasses::removeUnused(DfgGraph& dfg) {
|
||||
// DfgVertex::user is the next pointer of the work list elements
|
||||
const auto userDataInUse = dfg.userDataInUse();
|
||||
@@ -231,16 +194,14 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) {
|
||||
DfgVertex* workListp = sentinelp;
|
||||
|
||||
// Add all unused vertices to the work list. This also allocates all DfgVertex::user.
|
||||
for (DfgVertex *vtxp = dfg.opVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
if (VL_LIKELY(nextp)) VL_PREFETCH_RW(nextp);
|
||||
if (vtxp->hasSinks()) {
|
||||
for (DfgVertex& vtx : dfg.opVertices()) {
|
||||
if (vtx.hasSinks()) {
|
||||
// This vertex is used. Allocate user, but don't add to work list.
|
||||
vtxp->setUser<DfgVertex*>(nullptr);
|
||||
vtx.setUser<DfgVertex*>(nullptr);
|
||||
} else {
|
||||
// This vertex is unused. Add to work list.
|
||||
vtxp->setUser<DfgVertex*>(workListp);
|
||||
workListp = vtxp;
|
||||
vtx.setUser<DfgVertex*>(workListp);
|
||||
workListp = &vtx;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,12 +230,117 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) {
|
||||
}
|
||||
|
||||
// Finally remove unused constants
|
||||
for (DfgConst *vtxp = dfg.constVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
if (!vtxp->hasSinks()) vtxp->unlinkDelete(dfg);
|
||||
for (DfgConst* const vtxp : dfg.constVertices().unlinkable()) {
|
||||
if (!vtxp->hasSinks()) VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp);
|
||||
}
|
||||
}
|
||||
|
||||
void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) {
|
||||
const auto userDataInUse = dfg.userDataInUse();
|
||||
|
||||
// Head of work list. Note that we want all next pointers in the list to be non-zero
|
||||
// (including that of the last element). This allows us to do two important things: detect
|
||||
// if an element is in the list by checking for a non-zero next pointer, and easy
|
||||
// prefetching without conditionals. The address of the graph is a good sentinel as it is a
|
||||
// valid memory address, and we can easily check for the end of the list.
|
||||
DfgVertex* const sentinelp = reinterpret_cast<DfgVertex*>(&dfg);
|
||||
DfgVertex* workListp = sentinelp;
|
||||
|
||||
// Add all variables to the initial work list
|
||||
for (DfgVertexVar& vtx : dfg.varVertices()) {
|
||||
vtx.setUser<DfgVertex*>(workListp);
|
||||
workListp = &vtx;
|
||||
}
|
||||
|
||||
const auto addToWorkList = [&](DfgVertex& vtx) {
|
||||
// If already in work list then nothing to do
|
||||
DfgVertex*& nextInWorklistp = vtx.user<DfgVertex*>();
|
||||
if (nextInWorklistp) return;
|
||||
// Actually add to work list.
|
||||
nextInWorklistp = workListp;
|
||||
workListp = &vtx;
|
||||
};
|
||||
|
||||
// List of variables we are replacing
|
||||
std::vector<AstVar*> replacedVariables;
|
||||
// AstVar::user1p() : AstVar* -> The replacement variables
|
||||
const VNUser1InUse user1InUse;
|
||||
|
||||
// Process the work list
|
||||
while (workListp != sentinelp) {
|
||||
// Pick up the head of the work list
|
||||
DfgVertex* const vtxp = workListp;
|
||||
// Detach the head
|
||||
workListp = vtxp->getUser<DfgVertex*>();
|
||||
// Prefetch next item
|
||||
VL_PREFETCH_RW(workListp);
|
||||
|
||||
// Remove unused non-variable vertices
|
||||
if (!vtxp->is<DfgVertexVar>() && !vtxp->hasSinks()) {
|
||||
// Add sources of removed vertex to work list
|
||||
vtxp->forEachSource(addToWorkList);
|
||||
// Remove the unused vertex
|
||||
vtxp->unlinkDelete(dfg);
|
||||
continue;
|
||||
}
|
||||
|
||||
// We can only eliminate DfgVarPacked vertices at the moment
|
||||
DfgVarPacked* const varp = vtxp->cast<DfgVarPacked>();
|
||||
if (!varp) continue;
|
||||
|
||||
// Can't remove if it has external drivers
|
||||
if (!varp->isDrivenFullyByDfg()) continue;
|
||||
|
||||
// Can't remove if must be kept (including external, non module references)
|
||||
if (varp->keep()) continue;
|
||||
|
||||
// Can't remove if referenced in other DFGs of the same module (otherwise might rm twice)
|
||||
if (varp->hasDfgRefs()) continue;
|
||||
|
||||
// If it has multiple sinks, it can't be eliminated
|
||||
if (varp->hasMultipleSinks()) continue;
|
||||
|
||||
if (!varp->hasModRefs()) {
|
||||
// If it is only referenced in this DFG, it can be removed
|
||||
++ctx.m_varsRemoved;
|
||||
varp->replaceWith(varp->source(0));
|
||||
varp->varp()->unlinkFrBack()->deleteTree();
|
||||
} else if (DfgVarPacked* const driverp = varp->source(0)->cast<DfgVarPacked>()) {
|
||||
// If it's driven from another variable, it can be replaced by that. However, we do not
|
||||
// want to propagate SystemC variables into the design.
|
||||
if (driverp->varp()->isSc()) continue;
|
||||
// Mark it for replacement
|
||||
++ctx.m_varsReplaced;
|
||||
UASSERT_OBJ(!varp->hasSinks(), varp, "Variable inlining should make this impossible");
|
||||
UASSERT(!varp->varp()->user1p(), "Replacement already exists");
|
||||
replacedVariables.emplace_back(varp->varp());
|
||||
varp->varp()->user1p(driverp->varp());
|
||||
} else {
|
||||
// Otherwise this *is* the canonical var
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add sources of redundant variable to the work list
|
||||
vtxp->forEachSource(addToWorkList);
|
||||
// Remove the redundant variable
|
||||
vtxp->unlinkDelete(dfg);
|
||||
}
|
||||
|
||||
// Job done if no replacements possible
|
||||
if (replacedVariables.empty()) return;
|
||||
|
||||
// Apply variable replacements in the module
|
||||
VNDeleter deleter;
|
||||
dfg.modulep()->foreach([&](AstVarRef* refp) {
|
||||
AstVar* varp = refp->varp();
|
||||
while (AstVar* const replacementp = VN_AS(varp->user1p(), Var)) varp = replacementp;
|
||||
refp->varp(varp);
|
||||
});
|
||||
|
||||
// Remove the replaced variables
|
||||
for (AstVar* const varp : replacedVariables) varp->unlinkFrBack()->deleteTree();
|
||||
}
|
||||
|
||||
void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgOptimizationContext& ctx) {
|
||||
// There is absolutely nothing useful we can do with a graph of size 2 or less
|
||||
if (dfg.size() <= 2) return;
|
||||
@@ -301,6 +367,8 @@ void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgOptimizationContext& ctx) {
|
||||
// We just did CSE above, so without peephole there is no need to run it again these
|
||||
apply(4, "cse1 ", [&]() { cse(dfg, ctx.m_cseContext1); });
|
||||
}
|
||||
apply(4, "removeVars ", [&]() { removeVars(dfg, ctx.m_removeVarsContext); });
|
||||
// Accumulate patterns for reporting
|
||||
if (v3Global.opt.stats()) ctx.m_patternStats.accumulate(dfg);
|
||||
apply(4, "regularize", [&]() { regularize(dfg, ctx.m_regularizeContext); });
|
||||
if (dumpDfgLevel() >= 8) dfg.dumpDotAllVarConesPrefixed(ctx.prefix() + "optimized");
|
||||
}
|
||||
|
||||
+34
-10
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "config_build.h"
|
||||
|
||||
#include "V3DfgPatternStats.h"
|
||||
#include "V3DfgPeephole.h"
|
||||
#include "V3ThreadSafety.h"
|
||||
|
||||
@@ -39,14 +40,32 @@ public:
|
||||
~V3DfgCseContext() VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
class DfgRemoveVarsContext final {
|
||||
class V3DfgRegularizeContext final {
|
||||
const std::string m_label; // Label to apply to stats
|
||||
|
||||
// Used to generate unique names for different DFGs within the same hashed name
|
||||
std::unordered_map<std::string, uint32_t> m_multiplicity;
|
||||
|
||||
public:
|
||||
VDouble0 m_temporariesIntroduced; // Number of temporaries introduced
|
||||
|
||||
std::string tmpNamePrefix(DfgGraph&); // Return prefix to use for given graph
|
||||
|
||||
explicit V3DfgRegularizeContext(const std::string& label)
|
||||
: m_label{label} {}
|
||||
~V3DfgRegularizeContext() VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
class V3DfgEliminateVarsContext final {
|
||||
const std::string m_label; // Label to apply to stats
|
||||
|
||||
public:
|
||||
VDouble0 m_removed; // Number of redundant variables removed
|
||||
explicit DfgRemoveVarsContext(const std::string& label)
|
||||
VDouble0 m_varsReplaced; // Number of variables replaced
|
||||
VDouble0 m_varsRemoved; // Number of variables removed
|
||||
|
||||
explicit V3DfgEliminateVarsContext(const std::string& label)
|
||||
: m_label{label} {}
|
||||
~DfgRemoveVarsContext() VL_MT_DISABLED;
|
||||
~V3DfgEliminateVarsContext() VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
class V3DfgOptimizationContext final {
|
||||
@@ -66,14 +85,16 @@ public:
|
||||
VDouble0 m_nonRepUnknown; // Equations non-representable due to unknown node
|
||||
VDouble0 m_nonRepVarRef; // Equations non-representable due to variable reference
|
||||
VDouble0 m_nonRepWidth; // Equations non-representable due to width mismatch
|
||||
VDouble0 m_intermediateVars; // Number of intermediate variables introduced
|
||||
VDouble0 m_replacedVars; // Number of variables replaced
|
||||
VDouble0 m_resultEquations; // Number of result combinational equations
|
||||
|
||||
V3DfgCseContext m_cseContext0{m_label + " 1st"};
|
||||
V3DfgCseContext m_cseContext1{m_label + " 2nd"};
|
||||
V3DfgPeepholeContext m_peepholeContext{m_label};
|
||||
DfgRemoveVarsContext m_removeVarsContext{m_label};
|
||||
V3DfgRegularizeContext m_regularizeContext{m_label};
|
||||
V3DfgEliminateVarsContext m_eliminateVarsContext{m_label};
|
||||
|
||||
V3DfgPatternStats m_patternStats;
|
||||
|
||||
explicit V3DfgOptimizationContext(const std::string& label) VL_MT_DISABLED;
|
||||
~V3DfgOptimizationContext() VL_MT_DISABLED;
|
||||
|
||||
@@ -104,13 +125,16 @@ AstModule* dfgToAst(DfgGraph&, V3DfgOptimizationContext&) VL_MT_DISABLED;
|
||||
// Common subexpression elimination
|
||||
void cse(DfgGraph&, V3DfgCseContext&) VL_MT_DISABLED;
|
||||
// Inline fully driven variables
|
||||
void inlineVars(const DfgGraph&) VL_MT_DISABLED;
|
||||
void inlineVars(DfgGraph&) VL_MT_DISABLED;
|
||||
// Peephole optimizations
|
||||
void peephole(DfgGraph&, V3DfgPeepholeContext&) VL_MT_DISABLED;
|
||||
// Remove redundant variables
|
||||
void removeVars(DfgGraph&, DfgRemoveVarsContext&) VL_MT_DISABLED;
|
||||
// Regularize graph. This must be run before converting back to Ast.
|
||||
void regularize(DfgGraph&, V3DfgRegularizeContext&) VL_MT_DISABLED;
|
||||
// Remove unused nodes
|
||||
void removeUnused(DfgGraph&) VL_MT_DISABLED;
|
||||
// Eliminate (remove or replace) redundant variables. Also removes resulting unused logic.
|
||||
void eliminateVars(DfgGraph&, V3DfgEliminateVarsContext&) VL_MT_DISABLED;
|
||||
|
||||
} // namespace V3DfgPasses
|
||||
|
||||
#endif
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "V3PchAstNoMT.h"
|
||||
#ifndef VERILATOR_V3DFGPATTERNSTATS_H_
|
||||
#define VERILATOR_V3DFGPATTERNSTATS_H_
|
||||
|
||||
#include "V3Dfg.h"
|
||||
#include "V3DfgPasses.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
@@ -195,3 +195,5 @@ public:
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+252
-331
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,126 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Convert DfgGraph to AstModule
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2024 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
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// - Ensures intermediate values (other than simple memory references or
|
||||
// constants) with multiple uses are assigned to variables
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3Dfg.h"
|
||||
#include "V3DfgPasses.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
std::string V3DfgRegularizeContext::tmpNamePrefix(DfgGraph& dfg) {
|
||||
V3Hash hash{dfg.modulep()->name()};
|
||||
hash += m_label;
|
||||
std::string name = hash.toString();
|
||||
const uint32_t sequenceNumber = m_multiplicity[name]++;
|
||||
name += '_' + std::to_string(sequenceNumber);
|
||||
return name;
|
||||
}
|
||||
|
||||
class DfgRegularize final {
|
||||
DfgGraph& m_dfg; // The graph being processed
|
||||
V3DfgRegularizeContext& m_ctx; // The optimization context for stats
|
||||
|
||||
// Prefix of temporary variable names
|
||||
const std::string m_tmpNamePrefix = "__VdfgRegularize_" + m_ctx.tmpNamePrefix(m_dfg) + '_';
|
||||
size_t m_nTmps = 0; // Number of temporaries added to this graph - for variable names only
|
||||
|
||||
// Return canonical variable that can be used to hold the value of this vertex
|
||||
DfgVarPacked* getCanonicalVariable(DfgVertex& vtx) {
|
||||
// First gather all existing variables fully written by this vertex
|
||||
std::vector<DfgVarPacked*> varVtxps;
|
||||
vtx.forEachSink([&](DfgVertex& sink) {
|
||||
if (DfgVarPacked* const varVtxp = sink.cast<DfgVarPacked>()) {
|
||||
if (varVtxp->isDrivenFullyByDfg()) varVtxps.push_back(varVtxp);
|
||||
}
|
||||
});
|
||||
|
||||
if (!varVtxps.empty()) {
|
||||
// There is at least one usable, existing variable. Pick the first one in source
|
||||
// order for deterministic results.
|
||||
std::stable_sort(varVtxps.begin(), varVtxps.end(),
|
||||
[](const DfgVarPacked* ap, const DfgVarPacked* bp) {
|
||||
// Prefer those variables that must be kept anyway
|
||||
const bool keepA = ap->keep() || ap->hasDfgRefs();
|
||||
const bool keepB = bp->keep() || bp->hasDfgRefs();
|
||||
if (keepA != keepB) return keepA;
|
||||
// Prefer those that already have module references, so we don't
|
||||
// have to support recursive substitutions.
|
||||
if (ap->hasModRefs() != bp->hasModRefs()) return ap->hasModRefs();
|
||||
// Otherwise source order
|
||||
const FileLine& aFl = *(ap->fileline());
|
||||
const FileLine& bFl = *(bp->fileline());
|
||||
if (const int cmp = aFl.operatorCompare(bFl)) return cmp < 0;
|
||||
// Fall back on names if all else fails
|
||||
return ap->varp()->name() < bp->varp()->name();
|
||||
});
|
||||
return varVtxps.front();
|
||||
}
|
||||
|
||||
// We need to introduce a temporary
|
||||
++m_ctx.m_temporariesIntroduced;
|
||||
|
||||
// Add temporary AstVar to containing module
|
||||
FileLine* const flp = vtx.fileline();
|
||||
const std::string name = m_tmpNamePrefix + std::to_string(m_nTmps++);
|
||||
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, vtx.dtypep()};
|
||||
m_dfg.modulep()->addStmtsp(varp);
|
||||
|
||||
// Create and return a variable vertex for the temporary
|
||||
return new DfgVarPacked{m_dfg, varp};
|
||||
}
|
||||
|
||||
// Insert intermediate variables for vertices with multiple sinks (or use an existing one)
|
||||
DfgRegularize(DfgGraph& dfg, V3DfgRegularizeContext& ctx)
|
||||
: m_dfg{dfg}
|
||||
, m_ctx{ctx} {
|
||||
|
||||
// Ensure intermediate values used multiple times are written to variables
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
// Operations without multiple sinks need no variables
|
||||
if (!vtx.hasMultipleSinks()) continue;
|
||||
// Array selects need no variables, they are just memory references
|
||||
if (vtx.is<DfgArraySel>()) continue;
|
||||
|
||||
// This is an op which has multiple sinks. Ensure it is assigned to a variable.
|
||||
DfgVarPacked* const varp = getCanonicalVariable(vtx);
|
||||
if (varp->arity()) {
|
||||
// Existing variable
|
||||
FileLine* const flp = varp->driverFileLine(0);
|
||||
varp->sourceEdge(0)->unlinkSource();
|
||||
varp->resetSources();
|
||||
vtx.replaceWith(varp);
|
||||
varp->addDriver(flp, 0, &vtx);
|
||||
} else {
|
||||
// Temporary variable
|
||||
vtx.replaceWith(varp);
|
||||
varp->addDriver(vtx.fileline(), 0, &vtx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
static void apply(DfgGraph& dfg, V3DfgRegularizeContext& ctx) { DfgRegularize{dfg, ctx}; }
|
||||
};
|
||||
|
||||
void V3DfgPasses::regularize(DfgGraph& dfg, V3DfgRegularizeContext& ctx) {
|
||||
DfgRegularize::apply(dfg, ctx);
|
||||
}
|
||||
+7
-12
@@ -40,6 +40,7 @@
|
||||
|
||||
class DfgVertexVar VL_NOT_FINAL : public DfgVertexVariadic {
|
||||
AstVar* const m_varp; // The AstVar associated with this vertex (not owned by this vertex)
|
||||
bool m_hasDfgRefs = false; // This AstVar is referenced in a different DFG of the module
|
||||
bool m_hasModRefs = false; // This AstVar is referenced outside the DFG, but in the module
|
||||
bool m_hasExtRefs = false; // This AstVar is referenced from outside the module
|
||||
|
||||
@@ -52,21 +53,16 @@ public:
|
||||
, m_varp{varp} {}
|
||||
ASTGEN_MEMBERS_DfgVertexVar;
|
||||
|
||||
DfgVertexVar* verticesNext() const {
|
||||
return static_cast<DfgVertexVar*>(DfgVertex::verticesNext());
|
||||
}
|
||||
DfgVertexVar* verticesPrev() const {
|
||||
return static_cast<DfgVertexVar*>(DfgVertex::verticesPrev());
|
||||
}
|
||||
|
||||
bool isDrivenByDfg() const { return arity() > 0; }
|
||||
|
||||
AstVar* varp() const { return m_varp; }
|
||||
bool hasDfgRefs() const { return m_hasDfgRefs; }
|
||||
void setHasDfgRefs() { m_hasDfgRefs = true; }
|
||||
bool hasModRefs() const { return m_hasModRefs; }
|
||||
void setHasModRefs() { m_hasModRefs = true; }
|
||||
bool hasExtRefs() const { return m_hasExtRefs; }
|
||||
void setHasExtRefs() { m_hasExtRefs = true; }
|
||||
bool hasRefs() const { return m_hasModRefs || m_hasExtRefs; }
|
||||
bool hasNonLocalRefs() const { return hasDfgRefs() || hasModRefs() || hasExtRefs(); }
|
||||
|
||||
// Variable cannot be removed, even if redundant in the DfgGraph (might be used externally)
|
||||
bool keep() const {
|
||||
@@ -104,9 +100,6 @@ public:
|
||||
, m_num{flp, static_cast<int>(width), value} {}
|
||||
ASTGEN_MEMBERS_DfgConst;
|
||||
|
||||
DfgConst* verticesNext() const { return static_cast<DfgConst*>(DfgVertex::verticesNext()); }
|
||||
DfgConst* verticesPrev() const { return static_cast<DfgConst*>(DfgVertex::verticesPrev()); }
|
||||
|
||||
V3Number& num() { return m_num; }
|
||||
const V3Number& num() const { return m_num; }
|
||||
|
||||
@@ -245,7 +238,9 @@ public:
|
||||
: DfgVertexVar{dfg, dfgType(), varp, 1u} {}
|
||||
ASTGEN_MEMBERS_DfgVarPacked;
|
||||
|
||||
bool isDrivenFullyByDfg() const { return arity() == 1 && source(0)->dtypep() == dtypep(); }
|
||||
bool isDrivenFullyByDfg() const {
|
||||
return arity() == 1 && source(0)->dtypep() == dtypep() && !varp()->isForceable();
|
||||
}
|
||||
|
||||
void addDriver(FileLine* flp, uint32_t lsb, DfgVertex* vtxp) {
|
||||
m_driverData.emplace_back(flp, lsb);
|
||||
|
||||
+4
-3
@@ -26,7 +26,7 @@
|
||||
EmitCParentModule::EmitCParentModule() {
|
||||
const auto setAll = [](AstNodeModule* modp) -> void {
|
||||
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (VN_IS(nodep, CFunc) || VN_IS(nodep, Var)) { nodep->user4p(modp); }
|
||||
if (VN_IS(nodep, CFunc) || VN_IS(nodep, Var)) nodep->user4p(modp);
|
||||
}
|
||||
};
|
||||
for (AstNode* modp = v3Global.rootp()->modulesp(); modp; modp = modp->nextp()) {
|
||||
@@ -67,6 +67,7 @@ AstCFile* EmitCBaseVisitorConst::createCFile(const string& filename, bool slow,
|
||||
AstCFile* const cfilep = new AstCFile{v3Global.rootp()->fileline(), filename};
|
||||
cfilep->slow(slow);
|
||||
cfilep->source(source);
|
||||
if (source) V3Stats::addStatSum(V3Stats::STAT_CPP_FILES, 1);
|
||||
return cfilep;
|
||||
}
|
||||
|
||||
@@ -172,7 +173,7 @@ void EmitCBaseVisitorConst::emitVarDecl(const AstVar* nodep, bool asRef) {
|
||||
putns(nodep, "&");
|
||||
}
|
||||
putns(nodep, nodep->nameProtect());
|
||||
if (asRef && refNeedParens) { puts(")"); }
|
||||
if (asRef && refNeedParens) puts(")");
|
||||
emitDeclArrayBrackets(nodep);
|
||||
puts(";\n");
|
||||
} else if (nodep->isIO() && basicp && !basicp->isOpaque()) {
|
||||
@@ -202,7 +203,7 @@ void EmitCBaseVisitorConst::emitVarDecl(const AstVar* nodep, bool asRef) {
|
||||
puts("&");
|
||||
}
|
||||
puts(nodep->nameProtect());
|
||||
if (asRef && refNeedParens) { puts(")"); }
|
||||
if (asRef && refNeedParens) puts(")");
|
||||
emitDeclArrayBrackets(nodep);
|
||||
// If it's a packed struct/array then nodep->width is the whole
|
||||
// thing, msb/lsb is just lowest dimension
|
||||
|
||||
@@ -380,6 +380,21 @@ public:
|
||||
puts(")");
|
||||
}
|
||||
|
||||
void visit(AstCvtUnpackArrayToPacked* nodep) override {
|
||||
putns(nodep, "VL_UNPACK_TO_");
|
||||
emitIQW(nodep);
|
||||
const AstNodeDType* const elemDTypep = nodep->fromp()->dtypep()->subDTypep();
|
||||
putns(elemDTypep, "<");
|
||||
putbs(elemDTypep->cType("", false, false));
|
||||
puts(",");
|
||||
puts(cvtToStr(nodep->fromp()->dtypep()->arrayUnpackedElements()));
|
||||
puts(">(");
|
||||
iterateAndNextConstNull(nodep->fromp());
|
||||
puts(", ");
|
||||
putns(elemDTypep, cvtToStr(elemDTypep->widthMin()));
|
||||
puts(")");
|
||||
}
|
||||
|
||||
void visit(AstNodeAssign* nodep) override {
|
||||
bool paren = true;
|
||||
bool decind = false;
|
||||
@@ -447,6 +462,19 @@ public:
|
||||
puts(", ");
|
||||
rhs = false;
|
||||
iterateAndNextConstNull(castp->fromp());
|
||||
} else if (const AstCvtPackedToUnpackArray* const castp
|
||||
= VN_CAST(nodep->rhsp(), CvtPackedToUnpackArray)) {
|
||||
putns(castp, "VL_ASSIGN_UNPACK_Q<");
|
||||
putbs(castp->dtypep()->subDTypep()->cType("", false, false));
|
||||
puts(", ");
|
||||
puts(cvtToStr(castp->dtypep()->arrayUnpackedElements()));
|
||||
puts(">(");
|
||||
iterateAndNextConstNull(nodep->lhsp());
|
||||
puts(", ");
|
||||
putns(castp->dtypep(), cvtToStr(castp->dtypep()->subDTypep()->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) //
|
||||
|
||||
+138
-4
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "V3EmitC.h"
|
||||
#include "V3EmitCConstInit.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
@@ -31,6 +32,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
// Internal EmitC implementation
|
||||
|
||||
class EmitCHeader final : public EmitCConstInit {
|
||||
V3UniqueNames m_names;
|
||||
// METHODS
|
||||
|
||||
void decorateFirst(bool& first, const string& str) {
|
||||
@@ -223,7 +225,7 @@ class EmitCHeader final : public EmitCConstInit {
|
||||
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
AstNodeUOrStructDType* const subp = itemp->getChildStructp();
|
||||
if (subp && !subp->packed()) {
|
||||
if (subp && (!subp->packed() || sdtypep->packed())) {
|
||||
// Recurse if it belongs to the current module
|
||||
if (subp->classOrPackagep() == modp) {
|
||||
emitStructDecl(modp, subp, emitted);
|
||||
@@ -231,6 +233,13 @@ class EmitCHeader final : public EmitCConstInit {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sdtypep->packed()) {
|
||||
emitPackedUOrSBody(sdtypep);
|
||||
} else {
|
||||
emitUnpackedUOrSBody(sdtypep);
|
||||
}
|
||||
}
|
||||
void emitUnpackedUOrSBody(AstNodeUOrStructDType* sdtypep) {
|
||||
putns(sdtypep, sdtypep->verilogKwd()); // "struct"/"union"
|
||||
puts(" " + EmitCBase::prefixNameProtect(sdtypep) + " {\n");
|
||||
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||
@@ -254,8 +263,135 @@ class EmitCHeader final : public EmitCConstInit {
|
||||
puts("return !(*this == rhs);\n}\n");
|
||||
puts("};\n");
|
||||
}
|
||||
|
||||
// getfunc: VL_ASSIGNSEL_XX(rbits, obits, off, lhsdata, rhsdata);
|
||||
// !getfunc: VL_SELASSIGN_XX(rbits, obits, lhsdata, rhsdata, off);
|
||||
void emitVlAssign(const AstNodeDType* const lhstype, const AstNodeDType* rhstype,
|
||||
const std::string& off, const std::string& lhsdata,
|
||||
const std::string& rhsdata, bool getfunc) {
|
||||
puts(getfunc ? "VL_ASSIGNSEL_" : "VL_SELASSIGN_");
|
||||
puts(lhstype->charIQWN());
|
||||
puts(rhstype->charIQWN());
|
||||
puts("(" + std::to_string(lhstype->width()) + ", "); // LHS width
|
||||
if (getfunc) {
|
||||
puts(std::to_string(rhstype->width()) + ", "); // Number of copy bits
|
||||
puts(off + ", "); // LHS offset
|
||||
} else {
|
||||
// Number of copy bits. Use widthTototalBytes to
|
||||
// make VL_SELASSIGN_XX clear upper unused bits for us.
|
||||
// puts(std::to_string(lhstype->width()) + ", ");
|
||||
puts(std::to_string(lhstype->widthTotalBytes() * 8) + ", ");
|
||||
}
|
||||
puts(lhsdata + ", "); // LHS data
|
||||
puts(rhsdata); // RHS data
|
||||
if (!getfunc) {
|
||||
puts(", " + off); // RHS offset
|
||||
}
|
||||
puts(");\n");
|
||||
}
|
||||
|
||||
// `retOrArg` should be prefixed by `&` or suffixed by `.data()` depending on its type
|
||||
void emitPackedMember(const AstNodeDType* parentDtypep, const AstNodeDType* dtypep,
|
||||
const std::string& fieldname, const std::string& offset, bool getfunc,
|
||||
const std::string& retOrArg) {
|
||||
dtypep = dtypep->skipRefp();
|
||||
if (const auto* adtypep = VN_CAST(dtypep, PackArrayDType)) {
|
||||
const std::string index = m_names.get("__Vi");
|
||||
puts("for (int " + index + " = 0; " + index + " < "
|
||||
+ std::to_string(adtypep->elementsConst()) + "; ++" + index + ") {\n");
|
||||
|
||||
const std::string offsetInLoop
|
||||
= offset + " + " + index + " * " + std::to_string(adtypep->subDTypep()->width());
|
||||
const std::string newName = fieldname + "[" + index + "]";
|
||||
emitPackedMember(parentDtypep, adtypep->subDTypep(), newName, offsetInLoop, getfunc,
|
||||
retOrArg);
|
||||
puts("}\n");
|
||||
} else if (VN_IS(dtypep, NodeUOrStructDType)) {
|
||||
const std::string tmp = m_names.get("__Vtmp");
|
||||
const std::string suffixName = dtypep->isWide() ? tmp + ".data()" : tmp;
|
||||
if (getfunc) { // Emit `get` func;
|
||||
// auto __tmp = field.get();
|
||||
puts("auto " + tmp + " = " + fieldname + ".get();\n");
|
||||
// VL_ASSIGNSEL_XX(rbits, obits, lsb, lhsdata, rhsdata);
|
||||
emitVlAssign(parentDtypep, dtypep, offset, retOrArg, suffixName, getfunc);
|
||||
} else { // Emit `set` func
|
||||
const std::string tmptype = AstCDType::typeToHold(dtypep->width());
|
||||
// type tmp;
|
||||
puts(tmptype + " " + tmp + ";\n");
|
||||
// VL_SELASSIGN_XX(rbits, obits, lhsdata, rhsdata, roffset);
|
||||
emitVlAssign(dtypep, parentDtypep, offset, suffixName, retOrArg, getfunc);
|
||||
// field.set(__tmp);
|
||||
puts(fieldname + ".set(" + tmp + ");\n");
|
||||
}
|
||||
} else {
|
||||
UASSERT_OBJ(VN_IS(dtypep, EnumDType) || VN_IS(dtypep, BasicDType), dtypep,
|
||||
"Unsupported type in packed struct or union");
|
||||
const std::string suffixName = dtypep->isWide() ? fieldname + ".data()" : fieldname;
|
||||
if (getfunc) { // Emit `get` func;
|
||||
// VL_ASSIGNSEL_XX(rbits, obits, lsb, lhsdata, rhsdata);
|
||||
emitVlAssign(parentDtypep, dtypep, offset, retOrArg, suffixName, getfunc);
|
||||
} else { // Emit `set` func
|
||||
// VL_SELASSIGN_XX(rbits, obits, lhsdata, rhsdata, roffset);
|
||||
emitVlAssign(dtypep, parentDtypep, offset, suffixName, retOrArg, getfunc);
|
||||
}
|
||||
}
|
||||
}
|
||||
void emitPackedUOrSBody(AstNodeUOrStructDType* sdtypep) {
|
||||
putns(sdtypep, sdtypep->verilogKwd()); // "struct"/"union"
|
||||
puts(" " + EmitCBase::prefixNameProtect(sdtypep) + " {\n");
|
||||
|
||||
AstMemberDType* itemp;
|
||||
AstMemberDType* lastItemp;
|
||||
AstMemberDType* witemp = nullptr;
|
||||
// LSB is first field in C, so loop backwards
|
||||
for (lastItemp = sdtypep->membersp(); lastItemp && lastItemp->nextp();
|
||||
lastItemp = VN_AS(lastItemp->nextp(), MemberDType)) {
|
||||
if (lastItemp->width() == sdtypep->width()) witemp = lastItemp;
|
||||
}
|
||||
for (itemp = lastItemp; itemp; itemp = VN_CAST(itemp->backp(), MemberDType)) {
|
||||
putns(itemp, itemp->dtypep()->cType(itemp->nameProtect(), false, false, true));
|
||||
puts(";\n");
|
||||
}
|
||||
|
||||
const std::string retArgName = m_names.get("__v");
|
||||
const std::string suffixName = sdtypep->isWide() ? retArgName + ".data()" : retArgName;
|
||||
const std::string retArgType = AstCDType::typeToHold(sdtypep->width());
|
||||
|
||||
// Emit `get` member function
|
||||
puts(retArgType + " get() const {\n");
|
||||
puts(retArgType + " " + retArgName + ";\n");
|
||||
if (VN_IS(sdtypep, StructDType)) {
|
||||
for (itemp = lastItemp; itemp; itemp = VN_CAST(itemp->backp(), MemberDType)) {
|
||||
emitPackedMember(sdtypep, itemp->dtypep(), itemp->nameProtect(),
|
||||
std::to_string(itemp->lsb()), /*getfunc=*/true, suffixName);
|
||||
}
|
||||
} else {
|
||||
// We only need to fill the widest field of union
|
||||
emitPackedMember(sdtypep, witemp->dtypep(), witemp->nameProtect(),
|
||||
std::to_string(witemp->lsb()), /*getfunc=*/true, suffixName);
|
||||
}
|
||||
puts("return " + retArgName + ";\n");
|
||||
puts("}\n");
|
||||
|
||||
// Emit `set` member function
|
||||
puts("void set(const " + retArgType + "& " + retArgName + ") {\n");
|
||||
if (VN_IS(sdtypep, StructDType)) {
|
||||
for (itemp = lastItemp; itemp; itemp = VN_CAST(itemp->backp(), MemberDType)) {
|
||||
emitPackedMember(sdtypep, itemp->dtypep(), itemp->nameProtect(),
|
||||
std::to_string(itemp->lsb()), /*getfunc=*/false, suffixName);
|
||||
}
|
||||
} else {
|
||||
// We only need to fill the widest field of union
|
||||
emitPackedMember(sdtypep, witemp->dtypep(), witemp->nameProtect(),
|
||||
std::to_string(witemp->lsb()), /*getfunc=*/false, suffixName);
|
||||
}
|
||||
|
||||
puts("}\n");
|
||||
|
||||
puts("};\n");
|
||||
m_names.reset();
|
||||
}
|
||||
void emitStructs(const AstNodeModule* modp) {
|
||||
bool first = true;
|
||||
// Track structs that've been emitted already
|
||||
std::set<AstNodeUOrStructDType*> emitted;
|
||||
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
@@ -264,8 +400,6 @@ class EmitCHeader final : public EmitCConstInit {
|
||||
AstNodeUOrStructDType* const sdtypep
|
||||
= VN_CAST(tdefp->dtypep()->skipRefToEnump(), NodeUOrStructDType);
|
||||
if (!sdtypep) continue;
|
||||
if (sdtypep->packed()) continue;
|
||||
decorateFirst(first, "\n// UNPACKED STRUCT TYPES\n");
|
||||
emitStructDecl(modp, sdtypep, emitted);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -858,7 +858,7 @@ class EmitCTrace final : EmitCFunc {
|
||||
void emitTraceValue(AstTraceInc* nodep, int arrayindex) {
|
||||
if (AstVarRef* const varrefp = VN_CAST(nodep->valuep(), VarRef)) {
|
||||
AstVar* const varp = varrefp->varp();
|
||||
if (varp->isEvent()) { puts("&"); }
|
||||
if (varp->isEvent()) puts("&");
|
||||
puts("(");
|
||||
if (emitTraceIsScBigUint(nodep)) {
|
||||
puts("(uint32_t*)");
|
||||
|
||||
@@ -110,6 +110,10 @@ private:
|
||||
puts("\n");
|
||||
}
|
||||
|
||||
puts("// Print statistical summary report\n");
|
||||
puts("contextp->statsPrintSummary();\n");
|
||||
puts("\n");
|
||||
|
||||
puts("return 0;\n");
|
||||
puts("}\n");
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user