Merge from master for release.

This commit is contained in:
Wilson Snyder 2021-09-01 21:02:51 -04:00
commit 27c08a65e1
310 changed files with 13751 additions and 9572 deletions

View File

@ -108,4 +108,3 @@ Standard: Cpp11
TabWidth: 8
UseTab: Never
...

View File

@ -29,7 +29,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04]
os: [ubuntu-20.04, ubuntu-18.04]
compiler:
- { cc: clang, cxx: clang++ }
- { cc: gcc, cxx: g++ }
@ -37,11 +37,9 @@ jobs:
exclude:
# Build pull requests only with ubuntu-20.04 and without m32
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-16.04' || 'do-not-exclude' }}
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
# Build -m32 only on ubuntu-20.04
- {os: ubuntu-18.04, m32: 1}
- {os: ubuntu-16.04, m32: 1}
runs-on: ${{ matrix.os }}
name: Build | ${{ matrix.os }} | ${{ matrix.compiler.cc }} ${{ matrix.m32 && '| -m32' || '' }}
env:
@ -92,7 +90,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04]
os: [ubuntu-20.04, ubuntu-18.04]
compiler:
- { cc: clang, cxx: clang++ }
- { cc: gcc, cxx: g++ }
@ -101,11 +99,9 @@ jobs:
exclude:
# Build pull requests only with ubuntu-20.04 and without m32
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-16.04' || 'do-not-exclude' }}
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
# Build -m32 only on ubuntu-20.04
- {os: ubuntu-18.04, m32: 1}
- {os: ubuntu-16.04, m32: 1}
runs-on: ${{ matrix.os }}
name: Test | ${{ matrix.os }} | ${{ matrix.compiler.cc }} | ${{ matrix.suite }} ${{ matrix.m32 && '| -m32' || '' }}
env:

26
Changes
View File

@ -8,6 +8,32 @@ The changes in each Verilator version are described below. The
contributors that suggested a given feature are shown in []. Thanks!
Verilator 4.212 2021-09-01
==========================
**Minor:**
* Fix re-evaluation of logic dependent on state set in DPI exports (#3091). [Geza Lore]
* Support unpacked array localparams in tasks/functions (#3078). [Geza Lore]
* Support timeunit/timeprecision in $unit.
* Support assignment patterns as children of pins (#3041). [Krzysztof Bieganski]
* Add --instr-count-dpi to tune assumed DPI import cost for multithreaded
model scheduling. Default value changed to 200 (#3068). [Yinan Xu]
* Output files are split based on the set of headers required
in order to aid incremental compilation via ccache (#3071). [Geza Lore]
* Parameter values are now emitted as 'static constexpr' instead of enum.
C++ direct references to parameters might require updating (#3077). [Geza Lore]
* Refactored Verilated include files; include verilated.h not verilated_heavy.h.
* Add header guards on Dpi.h generated files (#2979). [Tood Strader]
* Add XML ccall, constpool, initarray, and if/while begins (#3080). [Steven Hugg]
* Add error when constant function under a generate (#3103). [Don Owen]
* Fix -G to treat simple integer literals as signed (#3060). [Anikin1610]
* Fix emitted string array initializers (#2895). [Iztok Jeras]
* Fix bitop tree optimization dropping necessary & operator (#3096). [Flavien Solt]
* Fix internal error on wide -x-initial unique (#3106). [Alexandre Joannou]
* Fix traces to show array instances with brackets (#3092) (#3095). [Pieter Kapsenberg]
Verilator 4.210 2021-07-07
==========================

View File

@ -1,76 +0,0 @@
\.ccache/
\.clang-format
\.clang-tidy
\.git/
\.git$
\.github/
\.svn/
\.(bak|old)/
\.(bak|old)$
\.tar\.
.*\.tgz
.*\.log
\..*\.swp
.*\.tmp
.*\.tidy
.*\.tex
.*\.key
.*\.vcd
.*\.1
\.codacy\.yml
\.travis\.yml
/_build/
/build/
/obj_dbg/
/obj_dir/
/obj_dist/
/obj_iv/
/obj_ms/
/obj_nc/
/obj_opt/
/obj_vcs/
/obj_vlt/
/obj_vltmt/
INCA_libs/
/cov_work/
/logs/
^Makefile$
README.html
bin/verilator_bin.*
bin/verilator_coverage_bin.*
docs/.*\.html$
docs/_build/
docs/clang-format.txt$
docs/doxygen-doc/.*
docs/spelling.txt
examples/xml_py/copied/
examples/xml_py/graph.*
sonar-project.properties
src/Makefile$
src/Makefile_obj$
include/verilated.mk$
test_regress/.gdbinit$
test_regress/transcript
codecov.yml
config.cache$
config.status$
verilator\.log
verilator\.tex
verilator-config.cmake$
verilator-config-version.cmake$
verilator.pc$
verilator_bin.*
verilator_coverage_bin.*
.vcsmx_rebuild$
ncverilog.history
autom4te\.cache/
nodist/
ci/
/simv$
/simv.daidir/
/vc_hdrs.h$
/csrc/
obj_dir.*
TAGS
gmon.out
.*~

View File

@ -118,81 +118,6 @@ INFOS = verilator.html verilator.pdf
INFOS_OLD = README README.html README.pdf
# Files that can be generated, but should be up to date for a distribution.
DISTDEP = info Makefile
DISTFILES1 = $(INFOS) .gitignore \
*.in *.ac \
Artistic \
Changes \
LICENSE \
MANIFEST.SKIP \
README.rst \
verilator-config.cmake.in \
verilator-config-version.cmake.in \
bin/verilator \
bin/verilator_ccache_report \
bin/verilator_coverage \
bin/verilator_difftree \
bin/verilator_gantt \
bin/verilator_includer \
bin/verilator_profcfunc \
docs/.gitignore \
docs/CONTRIBUTING.rst \
docs/CONTRIBUTORS \
docs/Makefile \
docs/_static/*.png \
docs/_static/css/* \
docs/bin/* \
docs/gen/* \
docs/guide/*.py \
docs/guide/*.rst \
docs/guide/figures/* \
docs/install.rst \
docs/internals.rst \
docs/internals.rst \
docs/verilated.dox \
docs/xml.rst \
install-sh configure *.pod \
include/*.[chv]* \
include/*.in \
include/.*ignore \
include/gtkwave/*.[chv]* \
include/vltstd/*.[chv]* \
.*attributes */.*attributes */*/.*attributes \
src/.*ignore src/*.in src/*.cpp src/*.[chly] \
src/astgen src/bisonpre src/*fix src/cppcheck_filtered \
src/config_rev \
src/vlcovgen src/mkinstalldirs \
src/.gdbinit \
src/*.pl src/*.pod \
examples/*/.*ignore examples/*/Makefile* \
examples/*/*.[chv]* examples/*/vl_* \
examples/*/CMakeLists.txt \
DISTFILES2 = \
test_*/.*ignore test_*/Makefile* test_*/*.cpp \
test_*/*.pl test_*/*.v test_*/*.vc test_*/*.vh \
test_regress/*.pl \
test_regress/Makefile \
test_regress/Makefile_obj \
test_regress/input.vc \
test_regress/CMakeLists.txt \
test_regress/t/t*/*.sv* \
test_regress/t/t*/*.v* \
test_regress/t/t*/*/*.sv* \
test_regress/t/t*/*/*.v* \
test_regress/t/t*/*.cpp \
test_regress/t/t*/CMakeLists.txt \
test_regress/t/*.cpp \
test_regress/t/*.h \
test_regress/t/*.dat \
test_regress/t/*.mem \
test_regress/t/*.out \
test_regress/t/*.pl \
test_regress/t/*.pf \
test_regress/t/*.v* \
INST_PROJ_FILES = \
bin/verilator \
bin/verilator_ccache_report \
@ -386,44 +311,7 @@ uninstall:
install: all_nomsg install-all
install-all: installbin installman installdata install-msg
install-here: installman ftp
ifeq ($(VERILATOR_AUTHOR_SITE),1) # Local... Else don't burden users
DISTNAMEREV = $(shell sed -e '/DTVERSION/!d' -e 's/.*verilator_\([^"]*\).*/\1/' -e q ${srcdir}/src/config_rev.h)
VERILATOR_CAD_DIR = $(CAD_DIR)/verilator/$(DISTNAMEREV)/$(DIRPROJECT_ARCH)
INST_PROJ_CVS = cp_if_cvs_diff
install-cadtools: dist
@echo "Install-project to $(CAD_DIR)"
strip bin/verilator_bin*
strip bin/verilator_coverage_bin*
$(MAKE) install-cadtools-quick
$(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/man/man1
for p in $(VL_INST_MAN_FILES) ; do \
$(INSTALL_DATA) $$p $(VERILATOR_CAD_DIR)/man/man1/$$p; \
done
$(INST_PROJ_CVS) $(DISTNAME).tgz $(VERILATOR_CAD_DIR)/verilator.tgz
rm $(DISTNAME).tgz
install-cadtools-quick:
ifeq ($(CFG_WITH_DEFENV),yes)
@echo "%Error: Reconfigure with './configure --disable-defenv' to avoid hardcoded paths."
false
endif
@echo "Install-cadtools-quick (no strip) to $(VERILATOR_CAD_DIR)"
$(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/include/gtkwave
$(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/include/vltstd
$(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/bin
for p in $(INST_PROJ_FILES) ; do \
$(INST_PROJ_CVS) $$p $(VERILATOR_CAD_DIR)/$$p; \
done
for p in $(INST_PROJ_BIN_FILES) ; do \
$(INST_PROJ_CVS) $$p $(VERILATOR_CAD_DIR)/$$p; \
done
# VERILATOR_AUTHOR_SITE
endif
install-here: installman info
# Use --xml flag to see the cppcheck code to use for suppression
CPPCHECK_CPP = $(wildcard \
@ -524,9 +412,6 @@ lint-py:
format-pl-exec:
-chmod a+x test_regress/t/*.pl
ftp: info
install-msg:
@echo
@echo "Installed binaries to $(DESTDIR)$(bindir)/verilator"
@ -597,15 +482,6 @@ TAGS: $(TAGFILES)
doxygen:
$(MAKE) -C docs doxygen
######################################################################
# Test targets
dist-file-list:
@echo "begin-dist-file-list:"; # Scripts look for this
@echo $(wildcard $(DISTFILES1))
@echo $(wildcard $(DISTFILES2))
@echo "end-dist-file-list:"; # Scripts look for this
######################################################################
# Distributions
@ -613,36 +489,16 @@ DISTTITLE := Verilator $(word 1,$(PACKAGE_VERSION))
DISTNAME := verilator-$(word 1,$(PACKAGE_VERSION))
DISTDATEPRE := $(word 2,$(PACKAGE_VERSION))
DISTDATE := $(subst /,-,$(DISTDATEPRE))
DISTTAGNAME := $(subst .,_,$(subst -,_,$(DISTNAME)))
tag:
svnorcvs tag $(DISTTAGNAME)
# Don't depend on DISTFILES because there's no rule for "standards.info*".
dist: $(DISTDEP) maintainer-copy
-rm -fr $(DISTNAME)
for file in $(DISTFILES1); do \
mkdir -p `dirname $(DISTNAME)/$$file` >/dev/null ; \
ln $$file $(DISTNAME)/$$file \
|| { echo copying $$file instead; cp -p $$file $(DISTNAME)/$$file;}; \
done; true;
for file in $(DISTFILES2); do \
mkdir -p `dirname $(DISTNAME)/$$file` >/dev/null ; \
ln $$file $(DISTNAME)/$$file \
|| { echo copying $$file instead; cp -p $$file $(DISTNAME)/$$file;}; \
done; true;
chmod -R a+r $(DISTNAME)
tar chf $(DISTNAME).tar $(DISTNAME)
gzip --force --best $(DISTNAME).tar
mv $(DISTNAME).tar.gz $(DISTNAME).tgz
rm -fr $(DISTNAME)
maintainer-diff:
svnorcvs diff $(DISTTAGNAME)
preexist:
svnorcvs nexists $(DISTTAGNAME)
maintainer-dist: preexist dist tag
svnorcvs release $(DISTNAME).tgz
maintainer-dist: preexist tag
svnorcvs release $(DISTTAGNAME)

View File

@ -5,6 +5,8 @@
:target: https://www.gnu.org/licenses/lgpl-3.0]
.. image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
:target: https://opensource.org/licenses/Artistic-2.0
.. image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
:target: https://repology.org/project/verilator/versions
.. image:: https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f
:target: https://www.codacy.com/gh/verilator/verilator
.. image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg
@ -12,6 +14,7 @@
.. image:: https://github.com/verilator/verilator/workflows/build/badge.svg
:target: https://github.com/verilator/verilator/actions?query=workflow%3Abuild
Welcome to Verilator
====================

View File

@ -333,6 +333,7 @@ detailed descriptions of these arguments.
--if-depth <value> Tune IFDEPTH warning
+incdir+<dir> Directory to search for includes
--inline-mult <value> Tune module inlining
--instr-count-dpi <value> Assumed dynamic instruction count of DPI imports
-LDFLAGS <flags> Linker pre-object arguments for makefile
--l2-name <value> Verilog scope name of the top module
--language <lang> Default language standard to parse

View File

@ -7,7 +7,7 @@
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[4.210 2021-07-07],
AC_INIT([Verilator],[4.212 2021-09-01],
[https://verilator.org],
[verilator],[https://verilator.org])
# When releasing, also update header of Changes file
@ -346,14 +346,9 @@ AC_SUBST(CFG_CXXFLAGS_PROFILE)
# Flag to select newest language standard supported
# Macros work such that first option that passes is the one we take
# Currently enabled gnu++14/c++14 due to packaged SystemC dependency
# gnu++17 is the newest that Verilator supports
# std++03 is the oldest that Verilator supports
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++20)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++14)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++11)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++03)
# Currently enabled c++14 due to packaged SystemC dependency
# c++17 is the newest that Verilator supports
# c++03 is the oldest that Verilator supports
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++20)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++17)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++14)
@ -367,11 +362,6 @@ _MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++11)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++14)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++17)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++20)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++03)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++11)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++14)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++17)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++20)
AC_SUBST(CFG_CXXFLAGS_STD_OLDEST)
# Flags for compiling Verilator internals including parser, and Verilated files

View File

@ -30,6 +30,7 @@ Harald Heckmann
Howard Su
Huang Rui
HyungKi Jeong
Ivan Vnučec
Iztok Jeras
James Hanlon
James Hutchinson
@ -83,6 +84,7 @@ Sebastien Van Cauwenberghe
Sergi Granell
Stefan Wallentowitz
Stephen Henry
Steven Hugg
Tim Snyder
Tobias Rosenkranz
Tobias Wölfel

View File

@ -13,7 +13,8 @@ When possible, please instead report bugs at `Verilator Issues
Primary author is Wilson Snyder <wsnyder@wsnyder.org>.
Major concepts by Paul Wasson, Duane Galbi, John Coiner and Jie Xu.
Major concepts by Paul Wasson, Duane Galbi, John Coiner, Geza Lore, Yutetsu
Takatsukasa, and Jie Xu.
Contributors

View File

@ -11,6 +11,11 @@ C++11 compiler support
require C++14 or newer compilers for both compiling Verilator and
compiling Verilated models no sooner than January 2022.
Verilated_heavy.h
The legacy "verilated_heavy.h" include was replaced with just including
"verilated.h". Verilated_heavy.h is planned for removal no sooner than
July 2022.
Configuration File -msg
The :vlopt:`lint_off` "-msg" option has been replaced with the "-rule"
option. "-msg" is planned for removal no sooner than January 2021.

View File

@ -536,6 +536,14 @@ Summary:
times, but potentially faster simulation speed. This setting is ignored
for very small modules; they will always be inlined, if allowed.
.. option:: --instr-count-dpi <value>
Assumed dynamic instruction count of the average DPI import. This is used
by the partitioning algorithm when creating a multithread model. The
default value is 200. Adjusting this to an appropriate value can yield
performance improvements in multithreaded models. Ignored when creating a
single threaded model.
.. option:: -j [<value>]
Specify the level of parallelism for :vlopt:`--build`. The <value> must
@ -1044,6 +1052,8 @@ Summary:
Verilator assumes DPI pure imports are threadsafe, but non-pure DPI
imports are not.
See also :vlopt:`--instr-count-dpi` option.
.. option:: --threads-max-mtasks <value>
Rarely needed. When using :vlopt:`--threads`, specify the number of

View File

@ -23,6 +23,8 @@ package:
apt-get install verilator # On Ubuntu
For other distributions refer to `Repology Verilator Distro Packages
<https://repology.org/project/verilator>`__.
.. _Git Install:
@ -44,7 +46,6 @@ In brief, to install from git:
#sudo apt-get install zlibc zlib1g zlib1g-dev # Ubuntu only (ignore if gives error)
git clone https://github.com/verilator/verilator # Only first time
## Note the URL above is not a page you can see with a browser, it's for git only
# Every time you need to build:
unsetenv VERILATOR_ROOT # For csh; ignore error if on bash

View File

@ -216,6 +216,12 @@ be done by a "main thread". In most cases the eval thread and main thread
are the same thread (i.e. the user's top C++ testbench runs on a single
thread), but this is not required.
When making frequent use of DPI imported functions in a multi-threaded
model, it may be beneficial to performance to adjust the
:vlopt:`--instr-count-dpi` option based on some experimentation. This
influences the partitioning of the model by adjusting the assumed execution
time of DPI imports.
The :vlopt:`--trace-threads` options can be used to produce trace dumps
using multiple threads. If :vlopt:`--trace-threads` is set without
:vlopt:`--threads`, then :vlopt:`--trace-threads` will imply

View File

@ -4989,9 +4989,7 @@ unsigned int secnum = 0;
int blocks_skipped = 0;
fst_off_t blkpos = 0;
uint64_t seclen, beg_tim;
#ifdef FST_DEBUG
uint64_t end_tim;
#endif
uint64_t frame_uclen, frame_clen, frame_maxhandle, vc_maxhandle;
fst_off_t vc_start;
fst_off_t indx_pntr, indx_pos;
@ -5058,14 +5056,11 @@ for(;;)
if(!seclen) break;
beg_tim = fstReaderUint64(xc->f);
#ifdef FST_DEBUG
end_tim =
#endif
fstReaderUint64(xc->f);
end_tim = fstReaderUint64(xc->f);
if(xc->limit_range_valid)
{
if(beg_tim < xc->limit_range_start)
if(end_tim < xc->limit_range_start)
{
blocks_skipped++;
blkpos += seclen;

View File

@ -173,9 +173,9 @@ void vl_stop_maybe(const char* filename, int linenum, const char* hier, bool may
void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE {
#ifdef VL_THREADED
VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { //
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
vl_finish(filename, linenum, hier);
}));
}});
#else
vl_finish(filename, linenum, hier);
#endif
@ -183,9 +183,9 @@ void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAF
void VL_STOP_MT(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_SAFE {
#ifdef VL_THREADED
VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { //
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
vl_stop_maybe(filename, linenum, hier, maybe);
}));
}});
#else
vl_stop_maybe(filename, linenum, hier, maybe);
#endif
@ -193,9 +193,9 @@ void VL_STOP_MT(const char* filename, int linenum, const char* hier, bool maybe)
void VL_FATAL_MT(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_SAFE {
#ifdef VL_THREADED
VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { //
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
vl_fatal(filename, linenum, hier, msg);
}));
}});
#else
vl_fatal(filename, linenum, hier, msg);
#endif
@ -208,14 +208,14 @@ void VL_FATAL_MT(const char* filename, int linenum, const char* hier, const char
std::string _vl_string_vprintf(const char* formatp, va_list ap) VL_MT_SAFE {
va_list aq;
va_copy(aq, ap);
int len = VL_VSNPRINTF(nullptr, 0, formatp, aq);
size_t len = VL_VSNPRINTF(nullptr, 0, formatp, aq);
va_end(aq);
if (VL_UNLIKELY(len < 1)) return "";
char* bufp = new char[len + 1];
char* const bufp = new char[len + 1];
VL_VSNPRINTF(bufp, len + 1, formatp, ap);
const std::string out = std::string(bufp, len);
std::string out{bufp, len}; // Not const to allow move optimization
delete[] bufp;
return out;
}
@ -263,9 +263,9 @@ void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE {
va_start(ap, formatp);
const std::string out = _vl_string_vprintf(formatp, ap);
va_end(ap);
VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { //
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
VL_PRINTF("%s", out.c_str());
}));
}});
}
#endif
@ -276,7 +276,7 @@ static vluint32_t vl_sys_rand32() VL_MT_UNSAFE {
// Return random 32-bits using system library.
// Used only to construct seed for Verilator's PNRG.
static VerilatedMutex s_mutex;
const VerilatedLockGuard lock(s_mutex); // Otherwise rand is unsafe
const VerilatedLockGuard lock{s_mutex}; // Otherwise rand is unsafe
#if defined(_WIN32) && !defined(__CYGWIN__)
// Windows doesn't have lrand48(), although Cygwin does.
return (std::rand() << 16) ^ std::rand();
@ -742,7 +742,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
} else if (!inPct) { // Normal text
// Fast-forward to next escape and add to output
const char* ep = pos;
while (ep[0] && ep[0] != '%') ep++;
while (ep[0] && ep[0] != '%') ++ep;
if (ep != pos) {
output.append(pos, ep - pos);
pos += ep - pos - 1;
@ -776,7 +776,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
output += '%';
break;
case 'N': { // "C" string with name of module, add . if needed
const char* cstrp = va_arg(ap, const char*);
const char* const cstrp = va_arg(ap, const char*);
if (VL_LIKELY(*cstrp)) {
output += cstrp;
output += '.';
@ -784,13 +784,13 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
break;
}
case 'S': { // "C" string
const char* cstrp = va_arg(ap, const char*);
const char* const cstrp = va_arg(ap, const char*);
output += cstrp;
break;
}
case '@': { // Verilog/C++ string
va_arg(ap, int); // # bits is ignored
const std::string* cstrp = va_arg(ap, const std::string*);
const std::string* const cstrp = va_arg(ap, const std::string*);
std::string padding;
if (width > cstrp->size()) padding.append(width - cstrp->size(), ' ');
output += left ? (*cstrp + padding) : (padding + *cstrp);
@ -808,7 +808,8 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
const int timeunit = va_arg(ap, int);
output += _vl_vsformat_time(t_tmp, d, timeunit, left, width);
} else {
std::string fmts(pctp, pos - pctp + 1);
const size_t len = pos - pctp + 1;
std::string fmts{pctp, len};
VL_SNPRINTF(t_tmp, VL_VALUE_STRING_MAX_WIDTH, fmts.c_str(), d);
output += t_tmp;
}
@ -862,7 +863,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
if (VL_SIGN_E(lbits, lwp[VL_WORDS_I(lbits) - 1])) {
VlWide<VL_VALUE_STRING_MAX_WIDTH / 4 + 2> neg;
VL_NEGATE_W(VL_WORDS_I(lbits), neg, lwp);
append = std::string("-") + VL_DECIMAL_NW(lbits, neg);
append = std::string{"-"} + VL_DECIMAL_NW(lbits, neg);
} else {
append = VL_DECIMAL_NW(lbits, lwp);
}
@ -957,7 +958,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
}
break;
default: { // LCOV_EXCL_START
const std::string msg = std::string("Unknown _vl_vsformat code: ") + pos[0];
const std::string msg = std::string{"Unknown _vl_vsformat code: "} + pos[0];
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
break;
} // LCOV_EXCL_STOP
@ -969,14 +970,14 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
}
static inline bool _vl_vsss_eof(FILE* fp, int floc) VL_MT_SAFE {
if (fp) {
if (VL_LIKELY(fp)) {
return std::feof(fp) ? true : false; // true : false to prevent MSVC++ warning
} else {
return floc < 0;
}
}
static inline void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE {
if (fp) {
if (VL_LIKELY(fp)) {
std::fgetc(fp);
} else {
floc -= 8;
@ -985,7 +986,7 @@ static inline void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE {
static inline int _vl_vsss_peek(FILE* fp, int& floc, const WDataInP fromp,
const std::string& fstr) VL_MT_SAFE {
// Get a character without advancing
if (fp) {
if (VL_LIKELY(fp)) {
const int data = std::fgetc(fp);
if (data == EOF) return EOF;
ungetc(data, fp);
@ -1217,7 +1218,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
case 'u': {
// Read packed 2-value binary data
const int bytes = VL_BYTES_I(obits);
char* out = reinterpret_cast<char*>(owp);
char* const out = reinterpret_cast<char*>(owp);
if (!_vl_vsss_read_bin(fp, floc, fromp, fstr, out, bytes)) goto done;
const int last = bytes % 4;
if (last != 0
@ -1242,7 +1243,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
break;
}
default: { // LCOV_EXCL_START
const std::string msg = std::string("Unknown _vl_vsscanf code: ") + pos[0];
const std::string msg = std::string{"Unknown _vl_vsscanf code: "} + pos[0];
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
break;
} // LCOV_EXCL_STOP
@ -1253,19 +1254,19 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
// Reload data if non-wide (if wide, we put it in the right place directly)
if (obits == 0) { // Due to inIgnore
} else if (obits == -1) { // string
std::string* p = va_arg(ap, std::string*);
std::string* const p = va_arg(ap, std::string*);
*p = t_tmp;
} else if (obits <= VL_BYTESIZE) {
CData* p = va_arg(ap, CData*);
CData* const p = va_arg(ap, CData*);
*p = owp[0];
} else if (obits <= VL_SHORTSIZE) {
SData* p = va_arg(ap, SData*);
SData* const p = va_arg(ap, SData*);
*p = owp[0];
} else if (obits <= VL_IDATASIZE) {
IData* p = va_arg(ap, IData*);
IData* const p = va_arg(ap, IData*);
*p = owp[0];
} else if (obits <= VL_QUADSIZE) {
QData* p = va_arg(ap, QData*);
QData* const p = va_arg(ap, QData*);
*p = VL_SET_QW(owp);
}
}
@ -1337,7 +1338,7 @@ IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE {
if (VL_UNLIKELY(str.empty())) return 0;
// V3Emit has static check that bytes < VL_TO_STRING_MAX_WORDS, but be safe
// V3Emit has static check that bytes < VL_VALUE_STRING_MAX_WORDS, but be safe
if (VL_UNCOVERABLE(bytes < str.size())) {
VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: fgets buffer overrun"); // LCOV_EXCL_LINE
}
@ -1346,7 +1347,6 @@ IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE {
return got;
}
// declared in verilated_heavy.h
IData VL_FGETS_NI(std::string& dest, IData fpi) VL_MT_SAFE {
return getLine(dest, fpi, std::numeric_limits<size_t>::max());
}
@ -1354,7 +1354,7 @@ IData VL_FGETS_NI(std::string& dest, IData fpi) VL_MT_SAFE {
IData VL_FERROR_IN(IData, std::string& outputr) VL_MT_SAFE {
// We ignore lhs/fpi - IEEE says "most recent error" so probably good enough
const IData ret = errno;
outputr = std::string(::std::strerror(ret));
outputr = std::string{::std::strerror(ret)};
return ret;
}
@ -1541,19 +1541,19 @@ IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi
// Shift value in
IData entry = read_elements + start - array_lsb;
if (width <= 8) {
CData* datap = &(reinterpret_cast<CData*>(memp))[entry];
CData* const datap = &(reinterpret_cast<CData*>(memp))[entry];
if (shift == start_shift) *datap = 0;
*datap |= (c << shift) & VL_MASK_I(width);
} else if (width <= 16) {
SData* datap = &(reinterpret_cast<SData*>(memp))[entry];
SData* const datap = &(reinterpret_cast<SData*>(memp))[entry];
if (shift == start_shift) *datap = 0;
*datap |= (c << shift) & VL_MASK_I(width);
} else if (width <= VL_IDATASIZE) {
IData* datap = &(reinterpret_cast<IData*>(memp))[entry];
IData* const datap = &(reinterpret_cast<IData*>(memp))[entry];
if (shift == start_shift) *datap = 0;
*datap |= (c << shift) & VL_MASK_I(width);
} else if (width <= VL_QUADSIZE) {
QData* datap = &(reinterpret_cast<QData*>(memp))[entry];
QData* const datap = &(reinterpret_cast<QData*>(memp))[entry];
if (shift == start_shift) *datap = 0;
*datap |= ((static_cast<QData>(c) << static_cast<QData>(shift)) & VL_MASK_Q(width));
} else {
@ -1579,7 +1579,7 @@ IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE {
return VL_SYSTEM_IW(VL_WQ_WORDS_E, lhsw);
}
IData VL_SYSTEM_IW(int lhswords, const WDataInP lhsp) VL_MT_SAFE {
char filenamez[VL_TO_STRING_MAX_WORDS * VL_EDATASIZE + 1];
char filenamez[VL_VALUE_STRING_MAX_CHARS + 1];
_vl_vint_to_string(lhswords * VL_EDATASIZE, filenamez, lhsp);
const int code = std::system(filenamez); // Yes, std::system() is threadsafe
return code >> 8; // Want exit status
@ -1615,7 +1615,7 @@ IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_M
}
const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefix.c_str());
const char* dp = match.c_str() + 1 /*leading + */ + prefix.length();
const char* const dp = match.c_str() + 1 /*leading + */ + prefix.length();
if (match.empty()) return 0;
VL_ZERO_RESET_W(rbits, rwp);
@ -1684,9 +1684,9 @@ IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_S
}
}
const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefix.c_str());
const char* dp = match.c_str() + 1 /*leading + */ + prefix.length();
const char* const dp = match.c_str() + 1 /*leading + */ + prefix.length();
if (match.empty()) return 0;
rdr = std::string(dp);
rdr = std::string{dp};
return 1;
}
@ -1726,22 +1726,22 @@ std::string VL_TOUPPER_NN(const std::string& ld) VL_MT_SAFE {
std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_MT_SAFE {
// See also _vl_vint_to_string
char destout[VL_TO_STRING_MAX_WORDS * VL_EDATASIZE + 1];
char destout[VL_VALUE_STRING_MAX_CHARS + 1];
int obits = lwords * VL_EDATASIZE;
int lsb = obits - 1;
bool start = true;
char* destp = destout;
int len = 0;
size_t len = 0;
for (; lsb >= 0; --lsb) {
lsb = (lsb / 8) * 8; // Next digit
IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xff;
if (!start || charval) {
*destp++ = (charval == 0) ? ' ' : charval;
len++;
++len;
start = false; // Drop leading 0s
}
}
return std::string(destout, len);
return std::string{destout, len};
}
std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE {
@ -1924,19 +1924,19 @@ void VlReadMem::setData(void* valuep, const std::string& rhs) {
const int value
= (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0'));
if (m_bits <= 8) {
CData* datap = reinterpret_cast<CData*>(valuep);
CData* const datap = reinterpret_cast<CData*>(valuep);
if (!innum) *datap = 0;
*datap = ((*datap << shift) + value) & VL_MASK_I(m_bits);
} else if (m_bits <= 16) {
SData* datap = reinterpret_cast<SData*>(valuep);
SData* const datap = reinterpret_cast<SData*>(valuep);
if (!innum) *datap = 0;
*datap = ((*datap << shift) + value) & VL_MASK_I(m_bits);
} else if (m_bits <= VL_IDATASIZE) {
IData* datap = reinterpret_cast<IData*>(valuep);
IData* const datap = reinterpret_cast<IData*>(valuep);
if (!innum) *datap = 0;
*datap = ((*datap << shift) + value) & VL_MASK_I(m_bits);
} else if (m_bits <= VL_QUADSIZE) {
QData* datap = reinterpret_cast<QData*>(valuep);
QData* const datap = reinterpret_cast<QData*>(valuep);
if (!innum) *datap = 0;
*datap = ((*datap << static_cast<QData>(shift)) + static_cast<QData>(value))
& VL_MASK_Q(m_bits);
@ -1979,7 +1979,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
}
m_addr = addr + 1;
if (m_bits <= 8) {
const CData* datap = reinterpret_cast<const CData*>(valuep);
const CData* const datap = reinterpret_cast<const CData*>(valuep);
if (m_hex) {
fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap);
fprintf(m_fp, "\n");
@ -1987,7 +1987,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap));
}
} else if (m_bits <= 16) {
const SData* datap = reinterpret_cast<const SData*>(valuep);
const SData* const datap = reinterpret_cast<const SData*>(valuep);
if (m_hex) {
fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap);
fprintf(m_fp, "\n");
@ -1995,7 +1995,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap));
}
} else if (m_bits <= 32) {
const IData* datap = reinterpret_cast<const IData*>(valuep);
const IData* const datap = reinterpret_cast<const IData*>(valuep);
if (m_hex) {
fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap);
fprintf(m_fp, "\n");
@ -2003,7 +2003,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap));
}
} else if (m_bits <= 64) {
const QData* datap = reinterpret_cast<const QData*>(valuep);
const QData* const datap = reinterpret_cast<const QData*>(valuep);
const vluint64_t value = VL_MASK_Q(m_bits) & *datap;
const vluint32_t lo = value & 0xffffffff;
const vluint32_t hi = value >> 32;
@ -2038,7 +2038,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
fprintf(m_fp, "%s", formatBinary(32, data));
}
}
word_idx--;
--word_idx;
first = false;
}
fprintf(m_fp, "\n");
@ -2057,7 +2057,7 @@ void VL_READMEM_N(bool hex, // Hex format, else binary
) VL_MT_SAFE {
if (start < static_cast<QData>(array_lsb)) start = array_lsb;
VlReadMem rmem(hex, bits, filename, start, end);
VlReadMem rmem{hex, bits, filename, start, end};
if (VL_UNLIKELY(!rmem.isOpen())) return;
while (true) {
QData addr = 0;
@ -2070,16 +2070,16 @@ void VL_READMEM_N(bool hex, // Hex format, else binary
} else {
QData entry = addr - array_lsb;
if (bits <= 8) {
CData* datap = &(reinterpret_cast<CData*>(memp))[entry];
CData* const datap = &(reinterpret_cast<CData*>(memp))[entry];
rmem.setData(datap, value);
} else if (bits <= 16) {
SData* datap = &(reinterpret_cast<SData*>(memp))[entry];
SData* const datap = &(reinterpret_cast<SData*>(memp))[entry];
rmem.setData(datap, value);
} else if (bits <= VL_IDATASIZE) {
IData* datap = &(reinterpret_cast<IData*>(memp))[entry];
IData* const datap = &(reinterpret_cast<IData*>(memp))[entry];
rmem.setData(datap, value);
} else if (bits <= VL_QUADSIZE) {
QData* datap = &(reinterpret_cast<QData*>(memp))[entry];
QData* const datap = &(reinterpret_cast<QData*>(memp))[entry];
rmem.setData(datap, value);
} else {
WDataOutP datap
@ -2107,22 +2107,22 @@ void VL_WRITEMEM_N(bool hex, // Hex format, else binary
if (start < static_cast<QData>(array_lsb)) start = array_lsb;
if (end > addr_max) end = addr_max;
VlWriteMem wmem(hex, bits, filename, start, end);
VlWriteMem wmem{hex, bits, filename, start, end};
if (VL_UNLIKELY(!wmem.isOpen())) return;
for (QData addr = start; addr <= end; ++addr) {
const QData row_offset = addr - array_lsb;
if (bits <= 8) {
const CData* datap = &(reinterpret_cast<const CData*>(memp))[row_offset];
const CData* const datap = &(reinterpret_cast<const CData*>(memp))[row_offset];
wmem.print(addr, false, datap);
} else if (bits <= 16) {
const SData* datap = &(reinterpret_cast<const SData*>(memp))[row_offset];
const SData* const datap = &(reinterpret_cast<const SData*>(memp))[row_offset];
wmem.print(addr, false, datap);
} else if (bits <= 32) {
const IData* datap = &(reinterpret_cast<const IData*>(memp))[row_offset];
const IData* const datap = &(reinterpret_cast<const IData*>(memp))[row_offset];
wmem.print(addr, false, datap);
} else if (bits <= 64) {
const QData* datap = &(reinterpret_cast<const QData*>(memp))[row_offset];
const QData* const datap = &(reinterpret_cast<const QData*>(memp))[row_offset];
wmem.print(addr, false, datap);
} else {
const WDataInP memDatap = reinterpret_cast<WDataInP>(memp);
@ -2142,8 +2142,8 @@ int VL_TIME_STR_CONVERT(const char* strp) VL_PURE {
if (!strp) return 0;
if (*strp++ != '1') return 0;
while (*strp == '0') {
scale++;
strp++;
++scale;
++strp;
}
switch (*strp++) {
case 's': break;
@ -2259,7 +2259,7 @@ VerilatedContext::VerilatedContext()
Verilated::threadContextp(this);
m_ns.m_profThreadsFilename = "profile_threads.dat";
m_fdps.resize(31);
std::fill(m_fdps.begin(), m_fdps.end(), (FILE*)0);
std::fill(m_fdps.begin(), m_fdps.end(), static_cast<FILE*>(nullptr));
m_fdFreeMct.resize(30);
for (std::size_t i = 0, id = 1; i < m_fdFreeMct.size(); ++i, ++id) m_fdFreeMct[i] = id;
}
@ -2273,19 +2273,19 @@ VerilatedContext::Serialized::Serialized() {
}
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_assertOn = flag;
}
void VerilatedContext::calcUnusedSigs(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_calcUnusedSigs = flag;
}
void VerilatedContext::dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
const VerilatedLockGuard lock(m_timeDumpMutex);
const VerilatedLockGuard lock{m_timeDumpMutex};
m_dumpfile = flag;
}
std::string VerilatedContext::dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
const VerilatedLockGuard lock(m_timeDumpMutex);
const VerilatedLockGuard lock{m_timeDumpMutex};
return m_dumpfile;
}
std::string VerilatedContext::dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
@ -2297,61 +2297,61 @@ std::string VerilatedContext::dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDu
return out;
}
void VerilatedContext::errorCount(int val) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_errorCount = val;
}
void VerilatedContext::errorCountInc() VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
++m_s.m_errorCount;
}
void VerilatedContext::errorLimit(int val) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_errorLimit = val;
}
void VerilatedContext::fatalOnError(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_fatalOnError = flag;
}
void VerilatedContext::fatalOnVpiError(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_fatalOnVpiError = flag;
}
void VerilatedContext::gotError(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_gotError = flag;
}
void VerilatedContext::gotFinish(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_gotFinish = flag;
}
void VerilatedContext::profThreadsStart(vluint64_t flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_ns.m_profThreadsStart = flag;
}
void VerilatedContext::profThreadsWindow(vluint64_t flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_ns.m_profThreadsWindow = flag;
}
void VerilatedContext::profThreadsFilename(const std::string& flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_ns.m_profThreadsFilename = flag;
}
std::string VerilatedContext::profThreadsFilename() const VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
return m_ns.m_profThreadsFilename;
}
void VerilatedContext::randReset(int val) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_randReset = val;
}
void VerilatedContext::timeunit(int value) VL_MT_SAFE {
if (value < 0) value = -value; // Stored as 0..15
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_timeunit = value;
}
void VerilatedContext::timeprecision(int value) VL_MT_SAFE {
if (value < 0) value = -value; // Stored as 0..15
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_s.m_timeprecision = value;
#ifdef SYSTEMC_VERSION
const sc_time sc_res = sc_get_time_resolution();
@ -2387,13 +2387,13 @@ const char* VerilatedContext::timeprecisionString() const VL_MT_SAFE {
}
void VerilatedContext::commandArgs(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex) {
const VerilatedLockGuard lock(m_argMutex);
const VerilatedLockGuard lock{m_argMutex};
m_args.m_argVec.clear(); // Empty first, then add
impp()->commandArgsAddGuts(argc, argv);
}
void VerilatedContext::commandArgsAdd(int argc, const char** argv)
VL_MT_SAFE_EXCLUDES(m_argMutex) {
const VerilatedLockGuard lock(m_argMutex);
const VerilatedLockGuard lock{m_argMutex};
impp()->commandArgsAddGuts(argc, argv);
}
const char* VerilatedContext::commandArgsPlusMatch(const char* prefixp)
@ -2422,20 +2422,20 @@ void VerilatedContext::internalsDump() const VL_MT_SAFE {
void VerilatedContextImp::commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(m_argMutex) {
if (!m_args.m_argVecLoaded) m_args.m_argVec.clear();
for (int i = 0; i < argc; ++i) {
m_args.m_argVec.push_back(argv[i]);
m_args.m_argVec.emplace_back(argv[i]);
commandArgVl(argv[i]);
}
m_args.m_argVecLoaded = true; // Can't just test later for empty vector, no arguments is ok
}
void VerilatedContextImp::commandArgDump() const VL_MT_SAFE_EXCLUDES(m_argMutex) {
const VerilatedLockGuard lock(m_argMutex);
const VerilatedLockGuard lock{m_argMutex};
VL_PRINTF_MT(" Argv:");
for (const auto& i : m_args.m_argVec) VL_PRINTF_MT(" %s", i.c_str());
VL_PRINTF_MT("\n");
}
std::string VerilatedContextImp::argPlusMatch(const char* prefixp)
VL_MT_SAFE_EXCLUDES(m_argMutex) {
const VerilatedLockGuard lock(m_argMutex);
const VerilatedLockGuard lock{m_argMutex};
// Note prefixp does not include the leading "+"
const size_t len = std::strlen(prefixp);
if (VL_UNLIKELY(!m_args.m_argVecLoaded)) {
@ -2454,7 +2454,7 @@ std::string VerilatedContextImp::argPlusMatch(const char* prefixp)
// Return string representing current argv
// Only used by VPI so uses static storage, only supports most recent called context
std::pair<int, char**> VerilatedContextImp::argc_argv() VL_MT_SAFE_EXCLUDES(m_argMutex) {
const VerilatedLockGuard lock(m_argMutex);
const VerilatedLockGuard lock{m_argMutex};
static bool s_loaded = false;
static int s_argc = 0;
static char** s_argvp = nullptr;
@ -2529,7 +2529,7 @@ bool VerilatedContextImp::commandArgVlValue(const std::string& arg, const std::s
void VerilatedContext::randSeed(int val) VL_MT_SAFE {
// As we have per-thread state, the epoch must be static,
// and so the rand seed's mutex must also be static
const VerilatedLockGuard lock(VerilatedContextImp::s().s_randMutex);
const VerilatedLockGuard lock{VerilatedContextImp::s().s_randMutex};
m_s.m_randSeed = val;
const vluint64_t newEpoch = VerilatedContextImp::s().s_randSeedEpoch + 1;
// Obververs must see new epoch AFTER seed updated
@ -2552,10 +2552,10 @@ vluint64_t VerilatedContextImp::randSeedDefault64() const VL_MT_SAFE {
// VerilatedContext:: Methods - scopes
void VerilatedContext::scopesDump() const VL_MT_SAFE {
const VerilatedLockGuard lock(m_impdatap->m_nameMutex);
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
VL_PRINTF_MT(" scopesDump:\n");
for (const auto& i : m_impdatap->m_nameMap) {
const VerilatedScope* scopep = i.second;
const VerilatedScope* const scopep = i.second;
scopep->scopeDump();
}
VL_PRINTF_MT("\n");
@ -2563,20 +2563,20 @@ void VerilatedContext::scopesDump() const VL_MT_SAFE {
void VerilatedContextImp::scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE {
// Slow ok - called once/scope at construction
const VerilatedLockGuard lock(m_impdatap->m_nameMutex);
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
const auto it = m_impdatap->m_nameMap.find(scopep->name());
if (it == m_impdatap->m_nameMap.end()) m_impdatap->m_nameMap.emplace(scopep->name(), scopep);
}
void VerilatedContextImp::scopeErase(const VerilatedScope* scopep) VL_MT_SAFE {
// Slow ok - called once/scope at destruction
const VerilatedLockGuard lock(m_impdatap->m_nameMutex);
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
VerilatedImp::userEraseScope(scopep);
const auto it = m_impdatap->m_nameMap.find(scopep->name());
if (it != m_impdatap->m_nameMap.end()) m_impdatap->m_nameMap.erase(it);
}
const VerilatedScope* VerilatedContext::scopeFind(const char* namep) const VL_MT_SAFE {
// Thread save only assuming this is called only after model construction completed
const VerilatedLockGuard lock(m_impdatap->m_nameMutex);
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
// If too slow, can assume this is only VL_MT_SAFE_POSINIT
const auto& it = m_impdatap->m_nameMap.find(namep);
if (VL_UNLIKELY(it == m_impdatap->m_nameMap.end())) return nullptr;
@ -2670,11 +2670,11 @@ static void runCallbacks(const VoidPCbList& cbs) VL_MT_SAFE {
}
void Verilated::addFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE {
const VerilatedLockGuard lock(VlCbStatic.s_flushMutex);
const VerilatedLockGuard lock{VlCbStatic.s_flushMutex};
addCb(cb, datap, VlCbStatic.s_flushCbs);
}
void Verilated::removeFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE {
const VerilatedLockGuard lock(VlCbStatic.s_flushMutex);
const VerilatedLockGuard lock{VlCbStatic.s_flushMutex};
removeCb(cb, datap, VlCbStatic.s_flushCbs);
}
void Verilated::runFlushCallbacks() VL_MT_SAFE {
@ -2685,7 +2685,7 @@ void Verilated::runFlushCallbacks() VL_MT_SAFE {
static int s_recursing = 0;
#endif
if (!s_recursing++) {
const VerilatedLockGuard lock(VlCbStatic.s_flushMutex);
const VerilatedLockGuard lock{VlCbStatic.s_flushMutex};
runCallbacks(VlCbStatic.s_flushCbs);
}
--s_recursing;
@ -2698,11 +2698,11 @@ void Verilated::runFlushCallbacks() VL_MT_SAFE {
}
void Verilated::addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE {
const VerilatedLockGuard lock(VlCbStatic.s_exitMutex);
const VerilatedLockGuard lock{VlCbStatic.s_exitMutex};
addCb(cb, datap, VlCbStatic.s_exitCbs);
}
void Verilated::removeExitCb(VoidPCb cb, void* datap) VL_MT_SAFE {
const VerilatedLockGuard lock(VlCbStatic.s_exitMutex);
const VerilatedLockGuard lock{VlCbStatic.s_exitMutex};
removeCb(cb, datap, VlCbStatic.s_exitCbs);
}
void Verilated::runExitCallbacks() VL_MT_SAFE {
@ -2712,7 +2712,7 @@ void Verilated::runExitCallbacks() VL_MT_SAFE {
static int s_recursing = 0;
#endif
if (!s_recursing++) {
const VerilatedLockGuard lock(VlCbStatic.s_exitMutex);
const VerilatedLockGuard lock{VlCbStatic.s_exitMutex};
runCallbacks(VlCbStatic.s_exitCbs);
}
--s_recursing;
@ -2729,7 +2729,7 @@ void Verilated::nullPointerError(const char* filename, int linenum) VL_MT_SAFE {
void Verilated::overWidthError(const char* signame) VL_MT_SAFE {
// Slowpath - Called only when signal sets too high of a bit
const std::string msg = (std::string("Testbench C set input '") + signame
const std::string msg = (std::string{"Testbench C set input '"} + signame
+ "' to value that overflows what the signal's width can fit");
VL_FATAL_MT("unknown", 0, "", msg.c_str());
VL_UNREACHABLE
@ -2846,7 +2846,7 @@ void VerilatedScope::configure(VerilatedSyms* symsp, const char* prefixp, const
m_type = type;
m_timeunit = timeunit;
{
char* namep = new char[std::strlen(prefixp) + std::strlen(suffixp) + 2];
char* const namep = new char[std::strlen(prefixp) + std::strlen(suffixp) + 2];
char* dp = namep;
for (const char* sp = prefixp; *sp;) *dp++ = *sp++;
if (*prefixp && *suffixp) *dp++ = '.';
@ -2886,7 +2886,7 @@ void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, boo
// statically construct from that.
if (!finalize) return;
if (!m_varsp) m_varsp = new VerilatedVarNameMap();
if (!m_varsp) m_varsp = new VerilatedVarNameMap;
VerilatedVar var(namep, datap, vltype, static_cast<VerilatedVarFlags>(vlflags), dims, isParam);
va_list ap;
@ -2903,9 +2903,9 @@ void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, boo
} else {
// We could have a linked list of ranges, but really this whole thing needs
// to be generalized to support structs and unions, etc.
VL_FATAL_MT(
__FILE__, __LINE__, "",
(std::string("Unsupported multi-dimensional public varInsert: ") + namep).c_str());
std::string msg
= std::string{"Unsupported multi-dimensional public varInsert: "} + namep;
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
}
}
va_end(ap);
@ -2925,7 +2925,7 @@ VerilatedVar* VerilatedScope::varFind(const char* namep) const VL_MT_SAFE_POSTIN
void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE {
// Slowpath - Called only when find has failed
const std::string msg
= (std::string("Testbench C called '") + VerilatedImp::exportName(funcnum)
= (std::string{"Testbench C called '"} + VerilatedImp::exportName(funcnum)
+ "' but scope wasn't set, perhaps due to dpi import call without "
+ "'context', or missing svSetScope. See IEEE 1800-2017 35.5.3.");
VL_FATAL_MT("unknown", 0, "", msg.c_str());
@ -2935,7 +2935,7 @@ void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE {
void* VerilatedScope::exportFindError(int funcnum) const {
// Slowpath - Called only when find has failed
const std::string msg
= (std::string("Testbench C called '") + VerilatedImp::exportName(funcnum)
= (std::string{"Testbench C called '"} + VerilatedImp::exportName(funcnum)
+ "' but this DPI export function exists only in other scopes, not scope '" + name()
+ "'");
VL_FATAL_MT("unknown", 0, "", msg.c_str());
@ -2950,7 +2950,7 @@ void VerilatedScope::scopeDump() const {
VerilatedImp::exportName(i));
}
}
if (VerilatedVarNameMap* varsp = this->varsp()) {
if (VerilatedVarNameMap* const varsp = this->varsp()) {
for (const auto& i : *varsp) VL_PRINTF_MT(" VAR %p: %s\n", &(i.second), i.first);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -132,7 +132,7 @@ private:
int valueIndex(const std::string& value) VL_REQUIRES(m_mutex) {
const auto iter = m_valueIndexes.find(value);
if (iter != m_valueIndexes.end()) return iter->second;
m_nextIndex++;
++m_nextIndex;
assert(m_nextIndex > 0); // Didn't rollover
m_valueIndexes.emplace(value, m_nextIndex);
m_indexValues.emplace(m_nextIndex, value);
@ -143,8 +143,9 @@ private:
std::string rtn;
for (const char* pos = text.c_str(); *pos; ++pos) {
if (!std::isprint(*pos) || *pos == '%' || *pos == '"') {
char hex[10];
VL_SNPRINTF(hex, 10, "%%%02X", pos[0]);
constexpr size_t LEN_MAX_HEX = 20;
char hex[LEN_MAX_HEX];
VL_SNPRINTF(hex, LEN_MAX_HEX, "%%%02X", pos[0]);
rtn += hex;
} else {
rtn += *pos;
@ -165,11 +166,11 @@ private:
const std::string& value) VL_PURE {
std::string name;
if (key.length() == 1 && std::isalpha(key[0])) {
name += std::string("\001") + key;
name += std::string{"\001"} + key;
} else {
name += std::string("\001") + dequote(key);
name += std::string{"\001"} + dequote(key);
}
name += std::string("\002") + dequote(value);
name += std::string{"\002"} + dequote(value);
return name;
}
static std::string combineHier(const std::string& old, const std::string& add) VL_PURE {
@ -180,31 +181,32 @@ private:
if (old.empty()) return add;
if (add.empty()) return old;
const char* a = old.c_str();
const char* b = add.c_str();
const char* const a = old.c_str();
const char* const b = add.c_str();
// Scan forward to first mismatch
const char* apre = a;
const char* bpre = b;
while (*apre == *bpre) {
apre++;
bpre++;
++apre;
++bpre;
}
// We used to backup and split on only .'s but it seems better to be verbose
// and not assume . is the separator
const std::string prefix = std::string(a, apre - a);
const size_t prefix_len = apre - a;
const std::string prefix = std::string{a, prefix_len};
// Scan backward to last mismatch
const char* apost = a + std::strlen(a) - 1;
const char* bpost = b + std::strlen(b) - 1;
while (*apost == *bpost && apost > apre && bpost > bpre) {
apost--;
bpost--;
--apost;
--bpost;
}
// Forward to . so we have a whole word
std::string suffix = *bpost ? std::string(bpost + 1) : "";
std::string suffix = *bpost ? std::string{bpost + 1} : "";
std::string out = prefix + "*" + suffix;
@ -253,17 +255,17 @@ public:
// PUBLIC METHODS
void forcePerInstance(const bool flag) VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce();
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_forcePerInstance = flag;
}
void clear() VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce();
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
clearGuts();
}
void clearNonMatch(const char* const matchp) VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce();
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
if (matchp && matchp[0]) {
ItemList newlist;
for (const auto& itemp : m_items) {
@ -278,24 +280,24 @@ public:
}
void zero() VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce();
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
for (const auto& itemp : m_items) itemp->zero();
}
// We assume there's always call to i/f/p in that order
void inserti(VerilatedCovImpItem* itemp) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
assert(!m_insertp);
m_insertp = itemp;
}
void insertf(const char* const filenamep, const int lineno) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_insertFilenamep = filenamep;
m_insertLineno = lineno;
}
void insertp(const char* ckeyps[VerilatedCovConst::MAX_KEYS],
const char* valps[VerilatedCovConst::MAX_KEYS]) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
assert(m_insertp);
// First two key/vals are filename
ckeyps[0] = "filename";
@ -308,7 +310,8 @@ public:
while (const char* foundp = std::strchr(fnstartp, '/')) fnstartp = foundp + 1;
const char* fnendp = fnstartp;
for (; *fnendp && *fnendp != '.'; fnendp++) {}
std::string page_default = "sp_user/" + std::string(fnstartp, fnendp - fnstartp);
const size_t page_len = fnendp - fnstartp;
const std::string page_default = "sp_user/" + std::string{fnstartp, page_len};
ckeyps[2] = "page";
valps[2] = page_default.c_str();
@ -337,7 +340,7 @@ public:
// cout<<" "<<__FUNCTION__<<" "<<key<<" = "<<val<<endl;
m_insertp->m_keys[addKeynum] = valueIndex(key);
m_insertp->m_vals[addKeynum] = valueIndex(val);
addKeynum++;
++addKeynum;
if (VL_UNCOVERABLE(!legalKey(key))) {
std::string msg
= ("%Error: Coverage keys of one character, or letter+digit are illegal: "
@ -353,15 +356,15 @@ public:
void write(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce();
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
#ifndef VM_COVERAGE
VL_FATAL_MT("", 0, "", "%Error: Called VerilatedCov::write when VM_COVERAGE disabled\n");
#endif
selftest();
std::ofstream os(filename);
std::ofstream os{filename};
if (os.fail()) {
std::string msg = std::string("%Error: Can't write '") + filename + "'";
std::string msg = std::string{"%Error: Can't write '"} + filename + "'";
VL_FATAL_MT("", 0, "", msg.c_str());
return;
}
@ -435,10 +438,10 @@ void VerilatedCovContext::clearNonMatch(const char* matchp) VL_MT_SAFE {
void VerilatedCovContext::zero() VL_MT_SAFE { impp()->zero(); }
void VerilatedCovContext::write(const char* filenamep) VL_MT_SAFE { impp()->write(filenamep); }
void VerilatedCovContext::_inserti(vluint32_t* itemp) VL_MT_SAFE {
impp()->inserti(new VerilatedCoverItemSpec<vluint32_t>(itemp));
impp()->inserti(new VerilatedCoverItemSpec<vluint32_t>{itemp});
}
void VerilatedCovContext::_inserti(vluint64_t* itemp) VL_MT_SAFE {
impp()->inserti(new VerilatedCoverItemSpec<vluint64_t>(itemp));
impp()->inserti(new VerilatedCoverItemSpec<vluint64_t>{itemp});
}
void VerilatedCovContext::_insertf(const char* filename, int lineno) VL_MT_SAFE {
impp()->insertf(filename, lineno);
@ -511,7 +514,7 @@ VerilatedCovContext* VerilatedCov::threadCovp() VL_MT_SAFE {
VerilatedCovContext* VerilatedContext::coveragep() VL_MT_SAFE {
static VerilatedMutex s_mutex;
if (VL_UNLIKELY(!m_coveragep)) {
const VerilatedLockGuard lock(s_mutex);
const VerilatedLockGuard lock{s_mutex};
if (VL_LIKELY(!m_coveragep)) { // Not redundant, prevents race
m_coveragep.reset(new VerilatedCovImp);
}

View File

@ -184,7 +184,7 @@ static inline const VerilatedDpiOpenVar* _vl_openhandle_varp(const svOpenArrayHa
VL_FATAL_MT(__FILE__, __LINE__, "",
"%%Error: DPI svOpenArrayHandle function called with nullptr handle");
}
const VerilatedDpiOpenVar* varp = reinterpret_cast<const VerilatedDpiOpenVar*>(h);
const VerilatedDpiOpenVar* const varp = reinterpret_cast<const VerilatedDpiOpenVar*>(h);
if (VL_UNLIKELY(!varp->magicOk())) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"%%Error: DPI svOpenArrayHandle function called with non-Verilator handle");
@ -205,13 +205,13 @@ int svDimensions(const svOpenArrayHandle h) { return _vl_openhandle_varp(h)->udi
// Return pointer to open array data, or nullptr if not in IEEE standard C layout
void* svGetArrayPtr(const svOpenArrayHandle h) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h);
if (VL_UNLIKELY(!varp->isDpiStdLayout())) return nullptr;
return varp->datap();
}
// Return size of open array, or 0 if not in IEEE standard C layout
int svSizeOfArray(const svOpenArrayHandle h) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h);
if (VL_UNLIKELY(!varp->isDpiStdLayout())) return 0;
// Truncate 64 bits to int; DPI is limited to 4GB
return static_cast<int>(varp->totalSize());
@ -264,15 +264,15 @@ static void* _vl_svGetArrElemPtr(const svOpenArrayHandle h, int nargs, int indx1
int indx3) VL_MT_SAFE {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
if (VL_UNLIKELY(!varp->isDpiStdLayout())) return nullptr;
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
return datap;
}
// Copy to user bit array from simulator open array
static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int nargs,
int indx1, int indx2, int indx3) VL_MT_SAFE {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
if (VL_UNLIKELY(!datap)) return;
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
case VLVT_UINT8: d[0] = *(reinterpret_cast<CData*>(datap)); return;
@ -299,8 +299,8 @@ static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s,
// Copy to user logic array from simulator open array
static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int nargs,
int indx1, int indx2, int indx3) VL_MT_SAFE {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
if (VL_UNLIKELY(!datap)) return;
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
case VLVT_UINT8:
@ -342,8 +342,8 @@ static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandl
// Copy to simulator open array from from user bit array
static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int nargs,
int indx1, int indx2, int indx3) VL_MT_SAFE {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
if (VL_UNLIKELY(!datap)) return;
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
case VLVT_UINT8: *(reinterpret_cast<CData*>(datap)) = s[0]; return;
@ -364,8 +364,8 @@ static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecV
// Copy to simulator open array from from user logic array
static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s,
int nargs, int indx1, int indx2, int indx3) VL_MT_SAFE {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
if (VL_UNLIKELY(!datap)) return;
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
case VLVT_UINT8: *(reinterpret_cast<CData*>(datap)) = s[0].aval; return;
@ -386,10 +386,10 @@ static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogic
// Return bit from simulator open array
static svBit _vl_svGetBitArrElem(const svOpenArrayHandle s, int nargs, int indx1, int indx2,
int indx3, int indx4) VL_MT_SAFE {
int indx3, int) VL_MT_SAFE {
// One extra index supported, as need bit number
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
if (VL_UNLIKELY(!datap)) return 0;
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
case VLVT_UINT8: return (*(reinterpret_cast<CData*>(datap))) & 1;
@ -401,11 +401,11 @@ static svBit _vl_svGetBitArrElem(const svOpenArrayHandle s, int nargs, int indx1
}
// Update simulator open array from bit
static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int nargs, int indx1,
int indx2, int indx3, int indx4) VL_MT_SAFE {
int indx2, int indx3, int) VL_MT_SAFE {
// One extra index supported, as need bit number
value &= 1; // Make sure clean
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
if (VL_UNLIKELY(!datap)) return;
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
case VLVT_UINT8: *(reinterpret_cast<CData*>(datap)) = value; return;
@ -420,7 +420,7 @@ static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int narg
// DPI accessors that simply call above functions
void* svGetArrElemPtr(const svOpenArrayHandle h, int indx1, ...) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h);
void* datap;
va_list ap;
va_start(ap, indx1);
@ -454,7 +454,7 @@ void* svGetArrElemPtr3(const svOpenArrayHandle h, int indx1, int indx2, int indx
}
void svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int indx1, ...) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
va_list ap;
va_start(ap, indx1);
switch (varp->udims()) {
@ -486,7 +486,7 @@ void svPutBitArrElem3VecVal(const svOpenArrayHandle d, const svBitVecVal* s, int
_vl_svPutBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
}
void svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s, int indx1, ...) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
va_list ap;
va_start(ap, indx1);
switch (varp->udims()) {
@ -522,7 +522,7 @@ void svPutLogicArrElem3VecVal(const svOpenArrayHandle d, const svLogicVecVal* s,
// From simulator storage into user space
void svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1, ...) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
va_list ap;
va_start(ap, indx1);
switch (varp->udims()) {
@ -553,7 +553,7 @@ void svGetBitArrElem3VecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1
_vl_svGetBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
}
void svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int indx1, ...) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
va_list ap;
va_start(ap, indx1);
switch (varp->udims()) {
@ -585,7 +585,7 @@ void svGetLogicArrElem3VecVal(svLogicVecVal* d, const svOpenArrayHandle s, int i
}
svBit svGetBitArrElem(const svOpenArrayHandle s, int indx1, ...) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
svBit out;
va_list ap;
va_start(ap, indx1);
@ -618,7 +618,7 @@ svBit svGetBitArrElem3(const svOpenArrayHandle s, int indx1, int indx2, int indx
}
svLogic svGetLogicArrElem(const svOpenArrayHandle s, int indx1, ...) {
// Verilator doesn't support X/Z so can just call Bit version
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
svBit out;
va_list ap;
va_start(ap, indx1);
@ -654,7 +654,7 @@ svLogic svGetLogicArrElem3(const svOpenArrayHandle s, int indx1, int indx2, int
}
void svPutBitArrElem(const svOpenArrayHandle d, svBit value, int indx1, ...) {
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
va_list ap;
va_start(ap, indx1);
switch (varp->udims()) {
@ -685,7 +685,7 @@ void svPutBitArrElem3(const svOpenArrayHandle d, svBit value, int indx1, int ind
}
void svPutLogicArrElem(const svOpenArrayHandle d, svLogic value, int indx1, ...) {
// Verilator doesn't support X/Z so can just call Bit version
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
va_list ap;
va_start(ap, indx1);
switch (varp->udims()) {
@ -732,15 +732,15 @@ svScope svGetScope() {
}
svScope svSetScope(const svScope scope) {
const VerilatedScope* prevScopep = Verilated::dpiScope();
const VerilatedScope* vscopep = reinterpret_cast<const VerilatedScope*>(scope);
const VerilatedScope* const prevScopep = Verilated::dpiScope();
const VerilatedScope* const vscopep = reinterpret_cast<const VerilatedScope*>(scope);
Verilated::dpiScope(vscopep);
// NOLINTNEXTLINE(google-readability-casting)
return (svScope)(prevScopep);
}
const char* svGetNameFromScope(const svScope scope) {
const VerilatedScope* vscopep = reinterpret_cast<const VerilatedScope*>(scope);
const VerilatedScope* const vscopep = reinterpret_cast<const VerilatedScope*>(scope);
return vscopep->name();
}

View File

@ -28,7 +28,6 @@
#include "verilatedos.h"
#include "verilated.h" // Also presumably included by caller
#include "verilated_heavy.h" // Also presumably included by caller
#include "verilated_sym_props.h"
#include "svdpi.h"

View File

@ -53,29 +53,31 @@
//=============================================================================
// Check that vltscope_t matches fstScopeType
static_assert((int)FST_ST_VCD_MODULE == (int)VLT_TRACE_SCOPE_MODULE,
static_assert(static_cast<int>(FST_ST_VCD_MODULE) == static_cast<int>(VLT_TRACE_SCOPE_MODULE),
"VLT_TRACE_SCOPE_MODULE mismatches");
static_assert((int)FST_ST_VCD_TASK == (int)VLT_TRACE_SCOPE_TASK,
static_assert(static_cast<int>(FST_ST_VCD_TASK) == static_cast<int>(VLT_TRACE_SCOPE_TASK),
"VLT_TRACE_SCOPE_TASK mismatches");
static_assert((int)FST_ST_VCD_FUNCTION == (int)VLT_TRACE_SCOPE_FUNCTION,
static_assert(static_cast<int>(FST_ST_VCD_FUNCTION) == static_cast<int>(VLT_TRACE_SCOPE_FUNCTION),
"VLT_TRACE_SCOPE_FUNCTION mismatches");
static_assert((int)FST_ST_VCD_BEGIN == (int)VLT_TRACE_SCOPE_BEGIN,
static_assert(static_cast<int>(FST_ST_VCD_BEGIN) == static_cast<int>(VLT_TRACE_SCOPE_BEGIN),
"VLT_TRACE_SCOPE_BEGIN mismatches");
static_assert((int)FST_ST_VCD_FORK == (int)VLT_TRACE_SCOPE_FORK,
static_assert(static_cast<int>(FST_ST_VCD_FORK) == static_cast<int>(VLT_TRACE_SCOPE_FORK),
"VLT_TRACE_SCOPE_FORK mismatches");
static_assert((int)FST_ST_VCD_GENERATE == (int)VLT_TRACE_SCOPE_GENERATE,
static_assert(static_cast<int>(FST_ST_VCD_GENERATE) == static_cast<int>(VLT_TRACE_SCOPE_GENERATE),
"VLT_TRACE_SCOPE_GENERATE mismatches");
static_assert((int)FST_ST_VCD_STRUCT == (int)VLT_TRACE_SCOPE_STRUCT,
static_assert(static_cast<int>(FST_ST_VCD_STRUCT) == static_cast<int>(VLT_TRACE_SCOPE_STRUCT),
"VLT_TRACE_SCOPE_STRUCT mismatches");
static_assert((int)FST_ST_VCD_UNION == (int)VLT_TRACE_SCOPE_UNION,
static_assert(static_cast<int>(FST_ST_VCD_UNION) == static_cast<int>(VLT_TRACE_SCOPE_UNION),
"VLT_TRACE_SCOPE_UNION mismatches");
static_assert((int)FST_ST_VCD_CLASS == (int)VLT_TRACE_SCOPE_CLASS,
static_assert(static_cast<int>(FST_ST_VCD_CLASS) == static_cast<int>(VLT_TRACE_SCOPE_CLASS),
"VLT_TRACE_SCOPE_CLASS mismatches");
static_assert((int)FST_ST_VCD_INTERFACE == (int)VLT_TRACE_SCOPE_INTERFACE,
static_assert(static_cast<int>(FST_ST_VCD_INTERFACE)
== static_cast<int>(VLT_TRACE_SCOPE_INTERFACE),
"VLT_TRACE_SCOPE_INTERFACE mismatches");
static_assert((int)FST_ST_VCD_PACKAGE == (int)VLT_TRACE_SCOPE_PACKAGE,
static_assert(static_cast<int>(FST_ST_VCD_PACKAGE) == static_cast<int>(VLT_TRACE_SCOPE_PACKAGE),
"VLT_TRACE_SCOPE_PACKAGE mismatches");
static_assert((int)FST_ST_VCD_PROGRAM == (int)VLT_TRACE_SCOPE_PROGRAM,
static_assert(static_cast<int>(FST_ST_VCD_PROGRAM) == static_cast<int>(VLT_TRACE_SCOPE_PROGRAM),
"VLT_TRACE_SCOPE_PROGRAM mismatches");
//=============================================================================
@ -98,7 +100,7 @@ VerilatedFst::~VerilatedFst() {
}
void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_fst = fstWriterCreate(filename, 1);
fstWriterSetPackType(m_fst, FST_WR_PT_LZ4);
fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str()); // lintok-begin-on-ref
@ -130,14 +132,14 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
}
void VerilatedFst::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
VerilatedTrace<VerilatedFst>::closeBase();
fstWriterClose(m_fst);
m_fst = nullptr;
}
void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
VerilatedTrace<VerilatedFst>::flushBase();
fstWriterFlushContext(m_fst);
}
@ -161,11 +163,11 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
VerilatedTrace<VerilatedFst>::declCode(code, bits, false);
std::istringstream nameiss(name);
std::istringstream nameiss{name};
std::istream_iterator<std::string> beg(nameiss);
std::istream_iterator<std::string> end;
std::list<std::string> tokens(beg, end); // Split name
std::string symbol_name(tokens.back());
std::string symbol_name{tokens.back()};
tokens.pop_back(); // Remove symbol name from hierarchy
tokens.insert(tokens.begin(), moduleName()); // Add current module to the hierarchy
std::string tmpModName;
@ -191,8 +193,8 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
tmpModName = *new_it;
tmpModName.pop_back();
// If the scope ends with a non-ascii character, it will be 0x80 + fstScopeType
fstWriterSetScope(m_fst, (fstScopeType)(new_it->back() & 0x7f), tmpModName.c_str(),
nullptr);
fstWriterSetScope(m_fst, static_cast<fstScopeType>(new_it->back() & 0x7f),
tmpModName.c_str(), nullptr);
} else
fstWriterSetScope(m_fst, FST_ST_VCD_SCOPE, new_it->c_str(), nullptr);
@ -202,7 +204,7 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
std::stringstream name_ss;
name_ss << symbol_name;
if (array) name_ss << "(" << arraynum << ")";
if (array) name_ss << "[" << arraynum << "]";
std::string name_str = name_ss.str();
if (dtypenum > 0) {

2252
include/verilated_funcs.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -12,969 +12,20 @@
//*************************************************************************
///
/// \file
/// \brief Verilated string and data-type header
/// \brief Verilated old string and data-type header
///
/// This file is included automatically by Verilator at the top of
/// all C++ files it generates. It is used when strings or other
/// heavyweight types are required; these contents are not part of
/// verilated.h to save compile time when such types aren't used.
/// This file is deprecated, and provided for backwards compatibility.
/// Include verilated.h instead.
///
//*************************************************************************
#ifndef VERILATOR_VERILATED_HEAVY_H_
#define VERILATOR_VERILATED_HEAVY_H_
#ifdef VL_NO_LEGACY
#error "Include <verilated.h> instead of <verilated_heavy.h>"
#endif
#include "verilated.h"
#include <algorithm>
#include <array>
#include <deque>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <unordered_set>
//===================================================================
// String formatters (required by below containers)
extern std::string VL_TO_STRING(CData lhs);
extern std::string VL_TO_STRING(SData lhs);
extern std::string VL_TO_STRING(IData lhs);
extern std::string VL_TO_STRING(QData lhs);
inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; }
extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
//===================================================================
// Shuffle RNG
class VlURNG final {
public:
using result_type = size_t;
static constexpr size_t min() { return 0; }
static constexpr size_t max() { return 1ULL << 31; }
size_t operator()() { return VL_MASK_I(31) & VL_RANDOM_I(32); }
};
//===================================================================
// Readmem/Writemem operation classes
class VlReadMem final {
bool m_hex; // Hex format
int m_bits; // Bit width of values
const std::string& m_filename; // Filename
QData m_end; // End address (as specified by user)
FILE* m_fp; // File handle for filename
QData m_addr; // Next address to read
int m_linenum; // Line number last read from file
public:
VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end);
~VlReadMem();
bool isOpen() const { return m_fp != nullptr; }
int linenum() const { return m_linenum; }
bool get(QData& addrr, std::string& valuer);
void setData(void* valuep, const std::string& rhs);
};
class VlWriteMem final {
bool m_hex; // Hex format
int m_bits; // Bit width of values
FILE* m_fp; // File handle for filename
QData m_addr; // Next address to write
public:
VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end);
~VlWriteMem();
bool isOpen() const { return m_fp != nullptr; }
void print(QData addr, bool addrstamp, const void* valuep);
};
//===================================================================
/// Verilog wide packed bit container.
/// Similar to std::array<WData, N>, but lighter weight, only methods needed
/// by Verilator, to help compile time.
///
/// A 'struct' as we want this to be an aggregate type that allows
/// static aggregate initialization. Consider data members private.
///
/// For example a Verilog "bit [94:0]" will become a VlWide<3> because 3*32
/// bits are needed to hold the 95 bits. The MSB (bit 96) must always be
/// zero in memory, but during intermediate operations in the Verilated
/// internals is unpredictable.
template <std::size_t T_Words> struct VlWide final {
// MEMBERS
// This should be the only data member, otherwise generated static initializers need updating
EData m_storage[T_Words]; // Contents of the packed array
// CONSTRUCTORS
// cppcheck-suppress uninitVar
VlWide() = default;
~VlWide() = default;
VlWide(const VlWide&) = default;
VlWide(VlWide&&) = default;
// OPERATOR METHODS
VlWide& operator=(const VlWide&) = default;
VlWide& operator=(VlWide&&) = default;
operator WDataOutP() { return &m_storage[0]; } // This also allows []
operator WDataInP() const { return &m_storage[0]; } // This also allows []
// METHODS
const EData& at(size_t index) const { return m_storage[index]; }
EData& at(size_t index) { return m_storage[index]; }
WData* data() { return &m_storage[0]; }
const WData* data() const { return &m_storage[0]; }
bool operator<(const VlWide<T_Words>& rhs) const {
return VL_LT_W(T_Words, data(), rhs.data());
}
};
// Convert a C array to std::array reference by pointer magic, without copy.
// Data type (second argument) is so the function template can automatically generate.
template <std::size_t T_Words>
VlWide<T_Words>& VL_CVT_W_A(const WDataInP inp, const VlWide<T_Words>&) {
return *((VlWide<T_Words>*)inp);
}
template <std::size_t T_Words> std::string VL_TO_STRING(const VlWide<T_Words>& obj) {
return VL_TO_STRING_W(T_Words, obj.data());
}
//===================================================================
// Verilog queue and dynamic array container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
//
// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound
// For dynamic arrays it is always zero
template <class T_Value, size_t T_MaxSize = 0> class VlQueue final {
private:
// TYPES
using Deque = std::deque<T_Value>;
public:
using const_iterator = typename Deque::const_iterator;
private:
// MEMBERS
Deque m_deque; // State of the assoc array
T_Value m_defaultValue; // Default value
public:
// CONSTRUCTORS
// m_defaultValue isn't defaulted. Caller's constructor must do it.
VlQueue() = default;
~VlQueue() = default;
VlQueue(const VlQueue&) = default;
VlQueue(VlQueue&&) = default;
VlQueue& operator=(const VlQueue&) = default;
VlQueue& operator=(VlQueue&&) = default;
// Standard copy constructor works. Verilog: assoca = assocb
// Also must allow conversion from a different T_MaxSize queue
template <size_t U_MaxSize = 0> VlQueue operator=(const VlQueue<T_Value, U_MaxSize>& rhs) {
m_deque = rhs.privateDeque();
if (VL_UNLIKELY(T_MaxSize && T_MaxSize < m_deque.size())) m_deque.resize(T_MaxSize - 1);
return *this;
}
static VlQueue cons(const T_Value& lhs) {
VlQueue out;
out.push_back(lhs);
return out;
}
static VlQueue cons(const T_Value& lhs, const T_Value& rhs) {
VlQueue out;
out.push_back(rhs);
out.push_back(lhs);
return out;
}
static VlQueue cons(const VlQueue& lhs, const T_Value& rhs) {
VlQueue out = lhs;
out.push_front(rhs);
return out;
}
static VlQueue cons(const T_Value& lhs, const VlQueue& rhs) {
VlQueue out = rhs;
out.push_back(lhs);
return out;
}
static VlQueue cons(const VlQueue& lhs, const VlQueue& rhs) {
VlQueue out = rhs;
for (const auto& i : lhs.m_deque) out.push_back(i);
return out;
}
// METHODS
T_Value& atDefault() { return m_defaultValue; }
const T_Value& atDefault() const { return m_defaultValue; }
const Deque& privateDeque() const { return m_deque; }
// Size. Verilog: function int size(), or int num()
int size() const { return m_deque.size(); }
// Clear array. Verilog: function void delete([input index])
void clear() { m_deque.clear(); }
void erase(vlsint32_t index) {
if (VL_LIKELY(index >= 0 && index < m_deque.size()))
m_deque.erase(m_deque.begin() + index);
}
// Dynamic array new[] becomes a renew()
void renew(size_t size) {
clear();
m_deque.resize(size, atDefault());
}
// Dynamic array new[]() becomes a renew_copy()
void renew_copy(size_t size, const VlQueue<T_Value, T_MaxSize>& rhs) {
if (size == 0) {
clear();
} else {
*this = rhs;
m_deque.resize(size, atDefault());
}
}
// function void q.push_front(value)
void push_front(const T_Value& value) {
m_deque.push_front(value);
if (VL_UNLIKELY(T_MaxSize != 0 && m_deque.size() > T_MaxSize)) m_deque.pop_back();
}
// function void q.push_back(value)
void push_back(const T_Value& value) {
if (VL_LIKELY(T_MaxSize == 0 || m_deque.size() < T_MaxSize)) m_deque.push_back(value);
}
// function value_t q.pop_front();
T_Value pop_front() {
if (m_deque.empty()) return m_defaultValue;
T_Value v = m_deque.front();
m_deque.pop_front();
return v;
}
// function value_t q.pop_back();
T_Value pop_back() {
if (m_deque.empty()) return m_defaultValue;
T_Value v = m_deque.back();
m_deque.pop_back();
return v;
}
// Setting. Verilog: assoc[index] = v
// Can't just overload operator[] or provide a "at" reference to set,
// because we need to be able to insert only when the value is set
T_Value& at(vlsint32_t index) {
static T_Value s_throwAway;
// Needs to work for dynamic arrays, so does not use T_MaxSize
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
s_throwAway = atDefault();
return s_throwAway;
} else {
return m_deque[index];
}
}
// Accessing. Verilog: v = assoc[index]
const T_Value& at(vlsint32_t index) const {
static T_Value s_throwAway;
// Needs to work for dynamic arrays, so does not use T_MaxSize
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
return atDefault();
} else {
return m_deque[index];
}
}
// function void q.insert(index, value);
void insert(vlsint32_t index, const T_Value& value) {
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return;
m_deque.insert(m_deque.begin() + index, value);
}
// Return slice q[lsb:msb]
VlQueue slice(vlsint32_t lsb, vlsint32_t msb) const {
VlQueue out;
if (VL_UNLIKELY(lsb < 0)) lsb = 0;
if (VL_UNLIKELY(lsb >= m_deque.size())) lsb = m_deque.size() - 1;
if (VL_UNLIKELY(msb >= m_deque.size())) msb = m_deque.size() - 1;
for (vlsint32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]);
return out;
}
// For save/restore
const_iterator begin() const { return m_deque.begin(); }
const_iterator end() const { return m_deque.end(); }
// Methods
void sort() { std::sort(m_deque.begin(), m_deque.end()); }
template <typename Func> void sort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
std::sort(m_deque.begin(), m_deque.end(), [=](const T_Value& a, const T_Value& b) {
// index number is meaninless with sort, as it changes
return with_func(0, a) < with_func(0, b);
});
}
void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); }
template <typename Func> void rsort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
std::sort(m_deque.rbegin(), m_deque.rend(), [=](const T_Value& a, const T_Value& b) {
// index number is meaninless with sort, as it changes
return with_func(0, a) < with_func(0, b);
});
}
void reverse() { std::reverse(m_deque.begin(), m_deque.end()); }
void shuffle() { std::shuffle(m_deque.begin(), m_deque.end(), VlURNG()); }
VlQueue unique() const {
VlQueue out;
std::unordered_set<T_Value> saw;
for (const auto& i : m_deque) {
auto it = saw.find(i);
if (it == saw.end()) {
saw.insert(it, i);
out.push_back(i);
}
}
return out;
}
VlQueue<IData> unique_index() const {
VlQueue<IData> out;
IData index = 0;
std::unordered_set<T_Value> saw;
for (const auto& i : m_deque) {
auto it = saw.find(i);
if (it == saw.end()) {
saw.insert(it, i);
out.push_back(index);
}
++index;
}
return out;
}
template <typename Func> VlQueue find(Func with_func) const {
VlQueue out;
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) out.push_back(i);
++index;
}
return out;
}
template <typename Func> VlQueue<IData> find_index(Func with_func) const {
VlQueue<IData> out;
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) out.push_back(index);
++index;
}
return out;
}
template <typename Func> VlQueue find_first(Func with_func) const {
// Can't use std::find_if as need index number
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) return VlQueue::cons(i);
++index;
}
return VlQueue{};
}
template <typename Func> VlQueue<IData> find_first_index(Func with_func) const {
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) return VlQueue<IData>::cons(index);
++index;
}
return VlQueue<IData>{};
}
template <typename Func> VlQueue find_last(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
if (with_func(index, *it)) return VlQueue::cons(*it);
--index;
}
return VlQueue{};
}
template <typename Func> VlQueue<IData> find_last_index(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
if (with_func(index, *it)) return VlQueue<IData>::cons(index);
--index;
}
return VlQueue<IData>{};
}
// Reduction operators
VlQueue min() const {
if (m_deque.empty()) return VlQueue();
const auto it = std::min_element(m_deque.begin(), m_deque.end());
return VlQueue::cons(*it);
}
VlQueue max() const {
if (m_deque.empty()) return VlQueue();
const auto it = std::max_element(m_deque.begin(), m_deque.end());
return VlQueue::cons(*it);
}
T_Value r_sum() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_deque) out += i;
return out;
}
template <typename Func> T_Value r_sum(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out += with_func(index++, i);
return out;
}
T_Value r_product() const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
T_Value out{*it};
++it;
for (; it != m_deque.end(); ++it) out *= *it;
return out;
}
template <typename Func> T_Value r_product(Func with_func) const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
IData index = 0;
T_Value out{with_func(index, *it)};
++it;
++index;
for (; it != m_deque.end(); ++it) out *= with_func(index++, *it);
return out;
}
T_Value r_and() const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
T_Value out{*it};
++it;
for (; it != m_deque.end(); ++it) out &= *it;
return out;
}
template <typename Func> T_Value r_and(Func with_func) const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
IData index = 0;
T_Value out{with_func(index, *it)};
++it;
++index;
for (; it != m_deque.end(); ++it) out &= with_func(index, *it);
return out;
}
T_Value r_or() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_deque) out |= i;
return out;
}
template <typename Func> T_Value r_or(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out |= with_func(index++, i);
return out;
}
T_Value r_xor() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_deque) out ^= i;
return out;
}
template <typename Func> T_Value r_xor(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out ^= with_func(index++, i);
return out;
}
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
if (m_deque.empty()) return "'{}"; // No trailing space
std::string out = "'{";
std::string comma;
for (const auto& i : m_deque) {
out += comma + VL_TO_STRING(i);
comma = ", ";
}
return out + "} ";
}
};
template <class T_Value> std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
return obj.to_string();
}
//===================================================================
// Verilog associative array container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
//
template <class T_Key, class T_Value> class VlAssocArray final {
private:
// TYPES
using Map = std::map<T_Key, T_Value>;
public:
using const_iterator = typename Map::const_iterator;
private:
// MEMBERS
Map m_map; // State of the assoc array
T_Value m_defaultValue; // Default value
public:
// CONSTRUCTORS
// m_defaultValue isn't defaulted. Caller's constructor must do it.
VlAssocArray() = default;
~VlAssocArray() = default;
VlAssocArray(const VlAssocArray&) = default;
VlAssocArray(VlAssocArray&&) = default;
VlAssocArray& operator=(const VlAssocArray&) = default;
VlAssocArray& operator=(VlAssocArray&&) = default;
// METHODS
T_Value& atDefault() { return m_defaultValue; }
const T_Value& atDefault() const { return m_defaultValue; }
// Size of array. Verilog: function int size(), or int num()
int size() const { return m_map.size(); }
// Clear array. Verilog: function void delete([input index])
void clear() { m_map.clear(); }
void erase(const T_Key& index) { m_map.erase(index); }
// Return 0/1 if element exists. Verilog: function int exists(input index)
int exists(const T_Key& index) const { return m_map.find(index) != m_map.end(); }
// Return first element. Verilog: function int first(ref index);
int first(T_Key& indexr) const {
const auto it = m_map.cbegin();
if (it == m_map.end()) return 0;
indexr = it->first;
return 1;
}
// Return last element. Verilog: function int last(ref index)
int last(T_Key& indexr) const {
const auto it = m_map.crbegin();
if (it == m_map.rend()) return 0;
indexr = it->first;
return 1;
}
// Return next element. Verilog: function int next(ref index)
int next(T_Key& indexr) const {
auto it = m_map.find(indexr);
if (VL_UNLIKELY(it == m_map.end())) return 0;
++it;
if (VL_UNLIKELY(it == m_map.end())) return 0;
indexr = it->first;
return 1;
}
// Return prev element. Verilog: function int prev(ref index)
int prev(T_Key& indexr) const {
auto it = m_map.find(indexr);
if (VL_UNLIKELY(it == m_map.end())) return 0;
if (VL_UNLIKELY(it == m_map.begin())) return 0;
--it;
indexr = it->first;
return 1;
}
// Setting. Verilog: assoc[index] = v
// Can't just overload operator[] or provide a "at" reference to set,
// because we need to be able to insert only when the value is set
T_Value& at(const T_Key& index) {
const auto it = m_map.find(index);
if (it == m_map.end()) {
std::pair<typename Map::iterator, bool> pit = m_map.emplace(index, m_defaultValue);
return pit.first->second;
}
return it->second;
}
// Accessing. Verilog: v = assoc[index]
const T_Value& at(const T_Key& index) const {
const auto it = m_map.find(index);
if (it == m_map.end()) {
return m_defaultValue;
} else {
return it->second;
}
}
// Setting as a chained operation
VlAssocArray& set(const T_Key& index, const T_Value& value) {
at(index) = value;
return *this;
}
VlAssocArray& setDefault(const T_Value& value) {
atDefault() = value;
return *this;
}
// For save/restore
const_iterator begin() const { return m_map.begin(); }
const_iterator end() const { return m_map.end(); }
// Methods
VlQueue<T_Value> unique() const {
VlQueue<T_Value> out;
std::set<T_Value> saw;
for (const auto& i : m_map) {
auto it = saw.find(i.second);
if (it == saw.end()) {
saw.insert(it, i.second);
out.push_back(i.second);
}
}
return out;
}
VlQueue<T_Key> unique_index() const {
VlQueue<T_Key> out;
std::set<T_Key> saw;
for (const auto& i : m_map) {
auto it = saw.find(i.second);
if (it == saw.end()) {
saw.insert(it, i.second);
out.push_back(i.first);
}
}
return out;
}
template <typename Func> VlQueue<T_Value> find(Func with_func) const {
VlQueue<T_Value> out;
for (const auto& i : m_map)
if (with_func(i.first, i.second)) out.push_back(i.second);
return out;
}
template <typename Func> VlQueue<T_Key> find_index(Func with_func) const {
VlQueue<T_Key> out;
for (const auto& i : m_map)
if (with_func(i.first, i.second)) out.push_back(i.first);
return out;
}
template <typename Func> VlQueue<T_Value> find_first(Func with_func) const {
const auto it
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.end()) return VlQueue<T_Value>{};
return VlQueue<T_Value>::cons(it->second);
}
template <typename Func> VlQueue<T_Key> find_first_index(Func with_func) const {
const auto it
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.end()) return VlQueue<T_Value>{};
return VlQueue<T_Key>::cons(it->first);
}
template <typename Func> VlQueue<T_Value> find_last(Func with_func) const {
const auto it
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.rend()) return VlQueue<T_Value>{};
return VlQueue<T_Value>::cons(it->second);
}
template <typename Func> VlQueue<T_Key> find_last_index(Func with_func) const {
const auto it
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.rend()) return VlQueue<T_Value>{};
return VlQueue<T_Key>::cons(it->first);
}
// Reduction operators
VlQueue<T_Value> min() const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::min_element(
m_map.begin(), m_map.end(),
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return a.second < b.second;
});
return VlQueue<T_Value>::cons(it->second);
}
VlQueue<T_Value> max() const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::max_element(
m_map.begin(), m_map.end(),
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return a.second < b.second;
});
return VlQueue<T_Value>::cons(it->second);
}
T_Value r_sum() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out += i.second;
return out;
}
template <typename Func> T_Value r_sum(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out += with_func(i.first, i.second);
return out;
}
T_Value r_product() const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{it->second};
++it;
for (; it != m_map.end(); ++it) out *= it->second;
return out;
}
template <typename Func> T_Value r_product(Func with_func) const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{with_func(it->first, it->second)};
++it;
for (; it != m_map.end(); ++it) out *= with_func(it->first, it->second);
return out;
}
T_Value r_and() const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{it->second};
++it;
for (; it != m_map.end(); ++it) out &= it->second;
return out;
}
template <typename Func> T_Value r_and(Func with_func) const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{with_func(it->first, it->second)};
++it;
for (; it != m_map.end(); ++it) out &= with_func(it->first, it->second);
return out;
}
T_Value r_or() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out |= i.second;
return out;
}
template <typename Func> T_Value r_or(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out |= with_func(i.first, i.second);
return out;
}
T_Value r_xor() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out ^= i.second;
return out;
}
template <typename Func> T_Value r_xor(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out ^= with_func(i.first, i.second);
return out;
}
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
if (m_map.empty()) return "'{}"; // No trailing space
std::string out = "'{";
std::string comma;
for (const auto& i : m_map) {
out += comma + VL_TO_STRING(i.first) + ":" + VL_TO_STRING(i.second);
comma = ", ";
}
// Default not printed - maybe random init data
return out + "} ";
}
};
template <class T_Key, class T_Value>
std::string VL_TO_STRING(const VlAssocArray<T_Key, T_Value>& obj) {
return obj.to_string();
}
template <class T_Key, class T_Value>
void VL_READMEM_N(bool hex, int bits, const std::string& filename,
VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
VlReadMem rmem(hex, bits, filename, start, end);
if (VL_UNLIKELY(!rmem.isOpen())) return;
while (true) {
QData addr;
std::string data;
if (rmem.get(addr /*ref*/, data /*ref*/)) {
rmem.setData(&(obj.at(addr)), data);
} else {
break;
}
}
}
template <class T_Key, class T_Value>
void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
const VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
VlWriteMem wmem(hex, bits, filename, start, end);
if (VL_UNLIKELY(!wmem.isOpen())) return;
for (const auto& i : obj) {
const QData addr = i.first;
if (addr >= start && addr <= end) wmem.print(addr, true, &(i.second));
}
}
//===================================================================
/// Verilog unpacked array container
/// For when a standard C++[] array is not sufficient, e.g. an
/// array under a queue, or methods operating on the array.
///
/// A 'struct' as we want this to be an aggregate type that allows
/// static aggregate initialization. Consider data members private.
///
/// This class may get exposed to a Verilated Model's top I/O, if the top
/// IO has an unpacked array.
template <class T_Value, std::size_t T_Depth> struct VlUnpacked final {
// MEMBERS
// This should be the only data member, otherwise generated static initializers need updating
T_Value m_storage[T_Depth]; // Contents of the unpacked array
// CONSTRUCTORS
VlUnpacked() = default;
~VlUnpacked() = default;
VlUnpacked(const VlUnpacked&) = default;
VlUnpacked(VlUnpacked&&) = default;
VlUnpacked& operator=(const VlUnpacked&) = default;
VlUnpacked& operator=(VlUnpacked&&) = default;
// METHODS
// Raw access
WData* data() { return &m_storage[0]; }
const WData* data() const { return &m_storage[0]; }
T_Value& operator[](size_t index) { return m_storage[index]; };
const T_Value& operator[](size_t index) const { return m_storage[index]; };
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
std::string out = "'{";
std::string comma;
for (int i = 0; i < T_Depth; ++i) {
out += comma + VL_TO_STRING(m_storage[i]);
comma = ", ";
}
return out + "} ";
}
};
template <class T_Value, std::size_t T_Depth>
std::string VL_TO_STRING(const VlUnpacked<T_Value, T_Depth>& obj) {
return obj.to_string();
}
//===================================================================
// Verilog class reference container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
#define VlClassRef std::shared_ptr
template <class T> // T typically of type VlClassRef<x>
inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
if (VL_UNLIKELY(!t)) Verilated::nullPointerError(filename, linenum);
return t;
}
template <typename T, typename U>
static inline bool VL_CAST_DYNAMIC(VlClassRef<T> in, VlClassRef<U>& outr) {
VlClassRef<U> casted = std::dynamic_pointer_cast<U>(in);
if (VL_LIKELY(casted)) {
outr = casted;
return true;
} else {
return false;
}
}
//======================================================================
// Conversion functions
extern std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_MT_SAFE;
inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE {
VlWide<VL_WQ_WORDS_E> lw;
VL_SET_WQ(lw, lhs);
return VL_CVT_PACK_STR_NW(VL_WQ_WORDS_E, lw);
}
inline std::string VL_CVT_PACK_STR_NN(const std::string& lhs) VL_PURE { return lhs; }
inline std::string& VL_CVT_PACK_STR_NN(std::string& lhs) VL_PURE { return lhs; }
inline std::string VL_CVT_PACK_STR_NI(IData lhs) VL_PURE {
VlWide<VL_WQ_WORDS_E> lw;
VL_SET_WI(lw, lhs);
return VL_CVT_PACK_STR_NW(1, lw);
}
inline std::string VL_CONCATN_NNN(const std::string& lhs, const std::string& rhs) VL_PURE {
return lhs + rhs;
}
inline std::string VL_REPLICATEN_NNQ(int, int, int, const std::string& lhs, IData rep) VL_PURE {
std::string out;
out.reserve(lhs.length() * rep);
for (unsigned times = 0; times < rep; ++times) out += lhs;
return out;
}
inline std::string VL_REPLICATEN_NNI(int obits, int lbits, int rbits, const std::string& lhs,
IData rep) VL_PURE {
return VL_REPLICATEN_NNQ(obits, lbits, rbits, lhs, rep);
}
inline IData VL_LEN_IN(const std::string& ld) { return ld.length(); }
extern std::string VL_TOLOWER_NN(const std::string& ld);
extern std::string VL_TOUPPER_NN(const std::string& ld);
extern IData VL_FERROR_IN(IData fpi, std::string& outputr) VL_MT_SAFE;
extern IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) VL_MT_SAFE;
extern IData VL_FOPEN_MCD_N(const std::string& filename) VL_MT_SAFE;
extern void VL_READMEM_N(bool hex, int bits, QData depth, int array_lsb,
const std::string& filename, void* memp, QData start,
QData end) VL_MT_SAFE;
extern void VL_WRITEMEM_N(bool hex, int bits, QData depth, int array_lsb,
const std::string& filename, const void* memp, QData start,
QData end) VL_MT_SAFE;
extern IData VL_SSCANF_INX(int lbits, const std::string& ld, const char* formatp, ...) VL_MT_SAFE;
extern void VL_SFORMAT_X(int obits_ignored, std::string& output, const char* formatp,
...) VL_MT_SAFE;
extern std::string VL_SFORMATF_NX(const char* formatp, ...) VL_MT_SAFE;
extern void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix, int width,
VerilatedContext* contextp) VL_MT_SAFE;
extern IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE;
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, CData& rdr) VL_MT_SAFE {
VlWide<2> rwp; // WData must always be at least 2
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
if (got) rdr = rwp[0];
return got;
}
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, SData& rdr) VL_MT_SAFE {
VlWide<2> rwp; // WData must always be at least 2
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
if (got) rdr = rwp[0];
return got;
}
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, IData& rdr) VL_MT_SAFE {
VlWide<2> rwp;
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
if (got) rdr = rwp[0];
return got;
}
inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, QData& rdr) VL_MT_SAFE {
VlWide<2> rwp;
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
if (got) rdr = VL_SET_QW(rwp);
return got;
}
inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, double& rdr) VL_MT_SAFE {
VlWide<2> rwp;
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
if (got) rdr = VL_CVT_D_Q(VL_SET_QW(rwp));
return got;
}
extern IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_SAFE;
//======================================================================
// Strings
extern std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE;
extern CData VL_GETC_N(const std::string& lhs, IData rhs) VL_PURE;
extern std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_PURE;
inline IData VL_CMP_NN(const std::string& lhs, const std::string& rhs, bool ignoreCase) VL_PURE {
// SystemVerilog does not allow a string variable to contain '\0'.
// So C functions such as strcmp() can correctly compare strings.
if (ignoreCase) {
return VL_STRCASECMP(lhs.c_str(), rhs.c_str());
} else {
return std::strcmp(lhs.c_str(), rhs.c_str());
}
}
extern IData VL_ATOI_N(const std::string& str, int base) VL_PURE;
extern IData VL_FGETS_NI(std::string& dest, IData fpi);
#endif // Guard

View File

@ -30,7 +30,6 @@
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_heavy.h"
#include "verilated_syms.h"
#include <deque>
@ -104,7 +103,7 @@ public:
// METHODS
// Add message to queue (called by producer)
void post(const VerilatedMsg& msg) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_queue.insert(msg); // Pass by value to copy the message into queue
++m_depth;
}
@ -124,7 +123,7 @@ public:
const VerilatedMsg msg = *(it);
m_queue.erase(it);
m_mutex.unlock();
m_depth--; // Ok if outside critical section as only this code checks the value
--m_depth; // Ok if outside critical section as only this code checks the value
{
VL_DEBUG_IF(VL_DBG_MSGF("Executing callback from mtaskId=%d\n", msg.mtaskId()););
msg.run();
@ -179,7 +178,7 @@ public:
// FILE* list constructed from a file-descriptor
class VerilatedFpList final {
FILE* m_fp[31];
FILE* m_fp[31] = {};
std::size_t m_sz = 0;
public:
@ -256,11 +255,11 @@ public: // But only for verilated*.cpp
int timeFormatWidth() const VL_MT_SAFE { return m_s.m_timeFormatWidth; }
void timeFormatWidth(int value) VL_MT_SAFE { m_s.m_timeFormatWidth = value; }
std::string timeFormatSuffix() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
const VerilatedLockGuard lock(m_timeDumpMutex);
const VerilatedLockGuard lock{m_timeDumpMutex};
return m_timeFormatSuffix;
}
void timeFormatSuffix(const std::string& value) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
const VerilatedLockGuard lock(m_timeDumpMutex);
const VerilatedLockGuard lock{m_timeDumpMutex};
m_timeFormatSuffix = value;
}
@ -276,7 +275,7 @@ public: // But only for verilated*.cpp
public: // But only for verilated*.cpp
// METHODS - file IO
IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
if (m_fdFreeMct.empty()) return 0;
IData idx = m_fdFreeMct.back();
m_fdFreeMct.pop_back();
@ -285,10 +284,10 @@ public: // But only for verilated*.cpp
return (1 << idx);
}
IData fdNew(const char* filenamep, const char* modep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
FILE* fp = std::fopen(filenamep, modep);
FILE* const fp = std::fopen(filenamep, modep);
if (VL_UNLIKELY(!fp)) return 0;
// Bit 31 indicates it's a descriptor not a MCD
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
if (m_fdFree.empty()) {
// Need to create more space in m_fdps and m_fdFree
const std::size_t start = std::max<std::size_t>(31UL + 1UL + 3UL, m_fdps.size());
@ -306,25 +305,25 @@ public: // But only for verilated*.cpp
return (idx | (1UL << 31)); // bit 31 indicates not MCD
}
void fdFlush(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
for (const auto& i : fdlist) std::fflush(i);
}
IData fdSeek(IData fdi, IData offset, IData origin) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
if (VL_UNLIKELY(fdlist.size() != 1)) return 0;
return static_cast<IData>(
std::fseek(*fdlist.begin(), static_cast<long>(offset), static_cast<int>(origin)));
}
IData fdTell(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
if (VL_UNLIKELY(fdlist.size() != 1)) return 0;
return static_cast<IData>(std::ftell(*fdlist.begin()));
}
void fdWrite(IData fdi, const std::string& output) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
for (const auto& i : fdlist) {
if (VL_UNLIKELY(!i)) continue;
@ -332,7 +331,7 @@ public: // But only for verilated*.cpp
}
}
void fdClose(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
if (VL_BITISSET_I(fdi, 31)) {
// Non-MCD case
IData idx = VL_MASK_I(31) & fdi;
@ -356,7 +355,7 @@ public: // But only for verilated*.cpp
}
}
inline FILE* fdToFp(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
if (VL_UNLIKELY(fdlist.size() != 1)) return nullptr;
return *fdlist.begin();
@ -463,7 +462,7 @@ public:
// per map overhead * N scopes would take much more space and cache thrashing.
// As scopep's are pointers, this implicitly handles multiple Context's
static inline void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE {
const VerilatedLockGuard lock(s().m_userMapMutex);
const VerilatedLockGuard lock{s().m_userMapMutex};
const auto it = s().m_userMap.find(std::make_pair(scopep, userKey));
if (it != s().m_userMap.end()) {
it->second = userData;
@ -472,7 +471,7 @@ public:
}
}
static inline void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
const VerilatedLockGuard lock(s().m_userMapMutex);
const VerilatedLockGuard lock{s().m_userMapMutex};
const auto& it = vlstd::as_const(s().m_userMap).find(std::make_pair(scopep, userKey));
if (VL_UNLIKELY(it == s().m_userMap.end())) return nullptr;
return it->second;
@ -482,7 +481,7 @@ public: // But only for verilated.cpp
// Symbol table destruction cleans up the entries for each scope.
static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE {
// Slow ok - called once/scope on destruction, so we simply iterate.
const VerilatedLockGuard lock(s().m_userMapMutex);
const VerilatedLockGuard lock{s().m_userMapMutex};
for (auto it = s().m_userMap.begin(); it != s().m_userMap.end();) {
if (it->first.first == scopep) {
s().m_userMap.erase(it++);
@ -492,7 +491,7 @@ public: // But only for verilated.cpp
}
}
static void userDump() VL_MT_SAFE {
const VerilatedLockGuard lock(s().m_userMapMutex); // Avoid it changing in middle of dump
const VerilatedLockGuard lock{s().m_userMapMutex}; // Avoid it changing in middle of dump
bool first = true;
for (const auto& i : s().m_userMap) {
if (first) {
@ -508,13 +507,13 @@ public: // But only for verilated*.cpp
// METHODS - hierarchy
static void hierarchyAdd(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE {
// Slow ok - called at construction for VPI accessible elements
const VerilatedLockGuard lock(s().m_hierMapMutex);
const VerilatedLockGuard lock{s().m_hierMapMutex};
s().m_hierMap[fromp].push_back(top);
}
static void hierarchyRemove(const VerilatedScope* fromp,
const VerilatedScope* top) VL_MT_SAFE {
// Slow ok - called at destruction for VPI accessible elements
const VerilatedLockGuard lock(s().m_hierMapMutex);
const VerilatedLockGuard lock{s().m_hierMapMutex};
VerilatedHierarchyMap& map = s().m_hierMap;
if (map.find(fromp) == map.end()) return;
auto& scopes = map[fromp];
@ -537,7 +536,7 @@ public: // But only for verilated*.cpp
// miss at the cost of a multiply, and all lookups move to slowpath.
static int exportInsert(const char* namep) VL_MT_SAFE {
// Slow ok - called once/function at creation
const VerilatedLockGuard lock(s().m_exportMutex);
const VerilatedLockGuard lock{s().m_exportMutex};
const auto it = s().m_exportMap.find(namep);
if (it == s().m_exportMap.end()) {
s().m_exportMap.emplace(namep, s().m_exportNext++);
@ -547,24 +546,24 @@ public: // But only for verilated*.cpp
}
}
static int exportFind(const char* namep) VL_MT_SAFE {
const VerilatedLockGuard lock(s().m_exportMutex);
const VerilatedLockGuard lock{s().m_exportMutex};
const auto& it = s().m_exportMap.find(namep);
if (VL_LIKELY(it != s().m_exportMap.end())) return it->second;
std::string msg = (std::string("%Error: Testbench C called ") + namep
std::string msg = (std::string{"%Error: Testbench C called "} + namep
+ " but no such DPI export function name exists in ANY model");
VL_FATAL_MT("unknown", 0, "", msg.c_str());
return -1;
}
static const char* exportName(int funcnum) VL_MT_SAFE {
// Slowpath; find name for given export; errors only so no map to reverse-map it
const VerilatedLockGuard lock(s().m_exportMutex);
const VerilatedLockGuard lock{s().m_exportMutex};
for (const auto& i : s().m_exportMap) {
if (i.second == funcnum) return i.first;
}
return "*UNKNOWN*";
}
static void exportsDump() VL_MT_SAFE {
const VerilatedLockGuard lock(s().m_exportMutex);
const VerilatedLockGuard lock{s().m_exportMutex};
bool first = true;
for (const auto& i : s().m_exportMap) {
if (first) {

View File

@ -93,8 +93,8 @@ void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE {
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, std::strlen(VLTSAVE_HEADER_STR)))) {
const std::string fn = filename();
const std::string msg
= std::string(
"Can't deserialize; file has wrong header signature, or file not found: ")
= std::
string{"Can't deserialize; file has wrong header signature, or file not found: "}
+ filename();
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
// Die before we close() as close would infinite loop
@ -112,7 +112,7 @@ void VerilatedDeserialize::trailer() VL_MT_UNSAFE_ONE {
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_TRAILER_STR, std::strlen(VLTSAVE_TRAILER_STR)))) {
const std::string fn = filename();
const std::string msg
= std::string("Can't deserialize; file has wrong end-of-file signature: ")
= std::string{"Can't deserialize; file has wrong end-of-file signature: "}
+ filename();
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
// Die before we close() as close would infinite loop
@ -204,7 +204,7 @@ void VerilatedSave::flush() VL_MT_UNSAFE_ONE {
if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) {
// LCOV_EXCL_START
// write failed, presume error (perhaps out of disk space)
std::string msg = std::string(__FUNCTION__) + ": " + std::strerror(errno);
std::string msg = std::string{__FUNCTION__} + ": " + std::strerror(errno);
VL_FATAL_MT("", 0, "", msg.c_str());
close();
break;
@ -235,7 +235,7 @@ void VerilatedRestore::fill() VL_MT_UNSAFE_ONE {
if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) {
// LCOV_EXCL_START
// write failed, presume error (perhaps out of disk space)
const std::string msg = std::string(__FUNCTION__) + ": " + std::strerror(errno);
const std::string msg = std::string{__FUNCTION__} + ": " + std::strerror(errno);
VL_FATAL_MT("", 0, "", msg.c_str());
close();
break;

View File

@ -23,7 +23,7 @@
#define VERILATOR_VERILATED_SAVE_C_H_
#include "verilatedos.h"
#include "verilated_heavy.h"
#include "verilated.h"
#include <string>

View File

@ -30,7 +30,7 @@
#define VERILATOR_VERILATED_SYMS_H_
#include "verilatedos.h"
#include "verilated_heavy.h"
#include "verilated.h"
#include "verilated_sym_props.h"
#include <map>

View File

@ -48,7 +48,8 @@ VlMTaskVertex::VlMTaskVertex(vluint32_t upstreamDepCount)
// VlWorkerThread
VlWorkerThread::VlWorkerThread(VlThreadPool* poolp, VerilatedContext* contextp, bool profiling)
: m_poolp{poolp}
: m_ready_size{0}
, m_poolp{poolp}
, m_profiling{profiling} // Must init this last -- after setting up fields that it might read:
, m_exiting{false}
, m_cthread{startWorker, this}
@ -104,7 +105,7 @@ VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads, bool profil
}
// Create'em
for (int i = 0; i < nThreads; ++i) {
m_workers.push_back(new VlWorkerThread(this, contextp, profiling));
m_workers.push_back(new VlWorkerThread{this, contextp, profiling});
}
// Set up a profile buffer for the current thread too -- on the
// assumption that it's the same thread that calls eval and may be
@ -131,13 +132,13 @@ void VlThreadPool::setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex) {
// try not to malloc while collecting profiling.
t_profilep->reserve(4096);
{
const VerilatedLockGuard lk(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_allProfiles.insert(t_profilep);
}
}
void VlThreadPool::profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lk(m_mutex);
const VerilatedLockGuard lock{m_mutex};
for (const auto& profilep : m_allProfiles) {
// Every thread's profile trace gets a copy of rec.
profilep->emplace_back(rec);
@ -146,7 +147,7 @@ void VlThreadPool::profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES
void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lk(m_mutex);
const VerilatedLockGuard lock{m_mutex};
VL_DEBUG_IF(VL_DBG_MSGF("+prof+threads writing to '%s'\n", filenamep););
FILE* const fp = std::fopen(filenamep, "w");

View File

@ -225,10 +225,10 @@ public:
}
VL_CPU_RELAX();
}
VerilatedLockGuard lk(m_mutex);
VerilatedLockGuard lock{m_mutex};
while (m_ready.empty()) {
m_waiting = true;
m_cv.wait(lk);
m_cv.wait(lock);
}
m_waiting = false;
// As noted above this is inefficient if our ready list is ever
@ -242,7 +242,7 @@ public:
VL_MT_SAFE_EXCLUDES(m_mutex) {
bool notify;
{
const VerilatedLockGuard lk(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_ready.emplace_back(fnp, selfp, evenCycle);
m_ready_size.fetch_add(1, std::memory_order_relaxed);
notify = m_waiting;

View File

@ -52,21 +52,21 @@ private:
public:
// Put an element at the back of the queue
void put(T value) VL_MT_SAFE_EXCLUDES(m_mutex) {
VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_queue.push_back(value);
m_cv.notify_one();
}
// Put an element at the front of the queue
void put_front(T value) VL_MT_SAFE_EXCLUDES(m_mutex) {
VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
m_queue.push_front(value);
m_cv.notify_one();
}
// Get an element from the front of the queue. Blocks if none available
T get() VL_MT_SAFE_EXCLUDES(m_mutex) {
VerilatedLockGuard lock(m_mutex);
VerilatedLockGuard lock{m_mutex};
m_cv.wait(lock, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); });
assert(!m_queue.empty());
T value = m_queue.front();
@ -76,7 +76,7 @@ public:
// Non blocking get
bool tryGet(T& result) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lockGuard(m_mutex);
const VerilatedLockGuard lockGuard{m_mutex};
if (m_queue.empty()) return false;
result = m_queue.front();
m_queue.pop_front();

View File

@ -246,7 +246,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::closeBase() {
shutdownWorker();
while (m_numTraceBuffers) {
delete[] m_buffersFromWorker.get();
m_numTraceBuffers--;
--m_numTraceBuffers;
}
#endif
}
@ -351,7 +351,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
m_traceBufferSize = nextCode() + numSignals() * 2 + 4;
// Start the worker thread
m_workerThread.reset(new std::thread(&VerilatedTrace<VL_DERIVED_T>::workerThreadMain, this));
m_workerThread.reset(new std::thread{&VerilatedTrace<VL_DERIVED_T>::workerThreadMain, this});
#endif
}
@ -398,7 +398,7 @@ void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m
// Not really VL_MT_SAFE but more VL_MT_UNSAFE_ONE.
// This does get the mutex, but if multiple threads are trying to dump
// chances are the data being dumped will have other problems
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
if (VL_UNCOVERABLE(m_timeLastDump && timeui <= m_timeLastDump)) { // LCOV_EXCL_START
VL_PRINTF_MT("%%Warning: previous dump at t=%" VL_PRI64 "u, requesting t=%" VL_PRI64
"u, dump call ignored\n",
@ -479,9 +479,9 @@ template <>
void VerilatedTrace<VL_DERIVED_T>::addCallbackRecord(std::vector<CallbackRecord>& cbVec,
CallbackRecord& cbRec)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
if (VL_UNCOVERABLE(timeLastDump() != 0)) { // LCOV_EXCL_START
std::string msg = (std::string("Internal: ") + __FILE__ + "::" + __FUNCTION__
std::string msg = (std::string{"Internal: "} + __FILE__ + "::" + __FUNCTION__
+ " called with already open file");
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
} // LCOV_EXCL_STOP
@ -489,19 +489,19 @@ void VerilatedTrace<VL_DERIVED_T>::addCallbackRecord(std::vector<CallbackRecord>
}
template <> void VerilatedTrace<VL_DERIVED_T>::addInitCb(initCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr(cb, userp);
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_initCbs, cbr);
}
template <> void VerilatedTrace<VL_DERIVED_T>::addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr(cb, userp);
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_fullCbs, cbr);
}
template <> void VerilatedTrace<VL_DERIVED_T>::addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr(cb, userp);
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_chgCbs, cbr);
}
template <> void VerilatedTrace<VL_DERIVED_T>::addCleanupCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr(cb, userp);
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_cleanupCbs, cbr);
}
template <> void VerilatedTrace<VL_DERIVED_T>::module(const std::string& name) VL_MT_UNSAFE {

897
include/verilated_types.h Normal file
View File

@ -0,0 +1,897 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Code available from: https://verilator.org
//
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
///
/// \file
/// \brief Verilated common data type containers
///
/// verilated.h should be included instead of this file.
///
/// Those macro/function/variable starting or ending in _ are internal,
/// however many of the other function/macros here are also internal.
///
//*************************************************************************
#ifndef VERILATOR_VERILATED_TYPES_H_
#define VERILATOR_VERILATED_TYPES_H_
#ifndef VERILATOR_VERILATED_H_INTERNAL_
#error "verilated_types.h should only be included by verilated.h"
#endif
//===================================================================
// String formatters (required by below containers)
extern std::string VL_TO_STRING(CData lhs);
extern std::string VL_TO_STRING(SData lhs);
extern std::string VL_TO_STRING(IData lhs);
extern std::string VL_TO_STRING(QData lhs);
inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; }
extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
//=========================================================================
// Declare net data types
#define VL_SIG8(name, msb, lsb) CData name ///< Declare signal, 1-8 bits
#define VL_SIG16(name, msb, lsb) SData name ///< Declare signal, 9-16 bits
#define VL_SIG64(name, msb, lsb) QData name ///< Declare signal, 33-64 bits
#define VL_SIG(name, msb, lsb) IData name ///< Declare signal, 17-32 bits
#define VL_SIGW(name, msb, lsb, words) WData name[words] ///< Declare signal, 65+ bits
#define VL_IN8(name, msb, lsb) CData name ///< Declare input signal, 1-8 bits
#define VL_IN16(name, msb, lsb) SData name ///< Declare input signal, 9-16 bits
#define VL_IN64(name, msb, lsb) QData name ///< Declare input signal, 33-64 bits
#define VL_IN(name, msb, lsb) IData name ///< Declare input signal, 17-32 bits
#define VL_INW(name, msb, lsb, words) WData name[words] ///< Declare input signal, 65+ bits
#define VL_INOUT8(name, msb, lsb) CData name ///< Declare bidir signal, 1-8 bits
#define VL_INOUT16(name, msb, lsb) SData name ///< Declare bidir signal, 9-16 bits
#define VL_INOUT64(name, msb, lsb) QData name ///< Declare bidir signal, 33-64 bits
#define VL_INOUT(name, msb, lsb) IData name ///< Declare bidir signal, 17-32 bits
#define VL_INOUTW(name, msb, lsb, words) WData name[words] ///< Declare bidir signal, 65+ bits
#define VL_OUT8(name, msb, lsb) CData name ///< Declare output signal, 1-8 bits
#define VL_OUT16(name, msb, lsb) SData name ///< Declare output signal, 9-16 bits
#define VL_OUT64(name, msb, lsb) QData name ///< Declare output signal, 33-64bits
#define VL_OUT(name, msb, lsb) IData name ///< Declare output signal, 17-32 bits
#define VL_OUTW(name, msb, lsb, words) WData name[words] ///< Declare output signal, 65+ bits
//===================================================================
// Shuffle RNG
extern vluint64_t vl_rand64() VL_MT_SAFE;
class VlURNG final {
public:
using result_type = size_t;
static constexpr size_t min() { return 0; }
static constexpr size_t max() { return 1ULL << 31; }
size_t operator()() { return VL_MASK_I(31) & vl_rand64(); }
};
//===================================================================
// Readmem/Writemem operation classes
class VlReadMem final {
bool m_hex; // Hex format
int m_bits; // Bit width of values
const std::string& m_filename; // Filename
QData m_end; // End address (as specified by user)
FILE* m_fp; // File handle for filename
QData m_addr; // Next address to read
int m_linenum; // Line number last read from file
public:
VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end);
~VlReadMem();
bool isOpen() const { return m_fp != nullptr; }
int linenum() const { return m_linenum; }
bool get(QData& addrr, std::string& valuer);
void setData(void* valuep, const std::string& rhs);
};
class VlWriteMem final {
bool m_hex; // Hex format
int m_bits; // Bit width of values
FILE* m_fp; // File handle for filename
QData m_addr; // Next address to write
public:
VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end);
~VlWriteMem();
bool isOpen() const { return m_fp != nullptr; }
void print(QData addr, bool addrstamp, const void* valuep);
};
//===================================================================
/// Verilog wide packed bit container.
/// Similar to std::array<WData, N>, but lighter weight, only methods needed
/// by Verilator, to help compile time.
///
/// A 'struct' as we want this to be an aggregate type that allows
/// static aggregate initialization. Consider data members private.
///
/// For example a Verilog "bit [94:0]" will become a VlWide<3> because 3*32
/// bits are needed to hold the 95 bits. The MSB (bit 96) must always be
/// zero in memory, but during intermediate operations in the Verilated
/// internals is unpredictable.
static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE;
template <std::size_t T_Words> struct VlWide final {
// MEMBERS
// This should be the only data member, otherwise generated static initializers need updating
EData m_storage[T_Words]; // Contents of the packed array
// CONSTRUCTORS
// Default constructors and destructor are used. Note however that C++20 requires that
// aggregate types do not have a user declared constructor, not even an explicitly defaulted
// one.
// OPERATOR METHODS
// Default copy assignment operators are used.
operator WDataOutP() { return &m_storage[0]; } // This also allows []
operator WDataInP() const { return &m_storage[0]; } // This also allows []
// METHODS
const EData& at(size_t index) const { return m_storage[index]; }
EData& at(size_t index) { return m_storage[index]; }
WData* data() { return &m_storage[0]; }
const WData* data() const { return &m_storage[0]; }
bool operator<(const VlWide<T_Words>& rhs) const {
return _vl_cmp_w(T_Words, data(), rhs.data()) < 0;
}
};
// Convert a C array to std::array reference by pointer magic, without copy.
// Data type (second argument) is so the function template can automatically generate.
template <std::size_t T_Words>
VlWide<T_Words>& VL_CVT_W_A(const WDataInP inp, const VlWide<T_Words>&) {
return *((VlWide<T_Words>*)inp);
}
template <std::size_t T_Words> std::string VL_TO_STRING(const VlWide<T_Words>& obj) {
return VL_TO_STRING_W(T_Words, obj.data());
}
//===================================================================
// Verilog queue and dynamic array container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
//
// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound
// For dynamic arrays it is always zero
template <class T_Value, size_t T_MaxSize = 0> class VlQueue final {
private:
// TYPES
using Deque = std::deque<T_Value>;
public:
using const_iterator = typename Deque::const_iterator;
private:
// MEMBERS
Deque m_deque; // State of the assoc array
T_Value m_defaultValue; // Default value
public:
// CONSTRUCTORS
// m_defaultValue isn't defaulted. Caller's constructor must do it.
VlQueue() = default;
~VlQueue() = default;
VlQueue(const VlQueue&) = default;
VlQueue(VlQueue&&) = default;
VlQueue& operator=(const VlQueue&) = default;
VlQueue& operator=(VlQueue&&) = default;
// Standard copy constructor works. Verilog: assoca = assocb
// Also must allow conversion from a different T_MaxSize queue
template <size_t U_MaxSize = 0> VlQueue operator=(const VlQueue<T_Value, U_MaxSize>& rhs) {
m_deque = rhs.privateDeque();
if (VL_UNLIKELY(T_MaxSize && T_MaxSize < m_deque.size())) m_deque.resize(T_MaxSize - 1);
return *this;
}
static VlQueue cons(const T_Value& lhs) {
VlQueue out;
out.push_back(lhs);
return out;
}
static VlQueue cons(const T_Value& lhs, const T_Value& rhs) {
VlQueue out;
out.push_back(rhs);
out.push_back(lhs);
return out;
}
static VlQueue cons(const VlQueue& lhs, const T_Value& rhs) {
VlQueue out = lhs;
out.push_front(rhs);
return out;
}
static VlQueue cons(const T_Value& lhs, const VlQueue& rhs) {
VlQueue out = rhs;
out.push_back(lhs);
return out;
}
static VlQueue cons(const VlQueue& lhs, const VlQueue& rhs) {
VlQueue out = rhs;
for (const auto& i : lhs.m_deque) out.push_back(i);
return out;
}
// METHODS
T_Value& atDefault() { return m_defaultValue; }
const T_Value& atDefault() const { return m_defaultValue; }
const Deque& privateDeque() const { return m_deque; }
// Size. Verilog: function int size(), or int num()
int size() const { return m_deque.size(); }
// Clear array. Verilog: function void delete([input index])
void clear() { m_deque.clear(); }
void erase(vlsint32_t index) {
if (VL_LIKELY(index >= 0 && index < m_deque.size()))
m_deque.erase(m_deque.begin() + index);
}
// Dynamic array new[] becomes a renew()
void renew(size_t size) {
clear();
m_deque.resize(size, atDefault());
}
// Dynamic array new[]() becomes a renew_copy()
void renew_copy(size_t size, const VlQueue<T_Value, T_MaxSize>& rhs) {
if (size == 0) {
clear();
} else {
*this = rhs;
m_deque.resize(size, atDefault());
}
}
// function void q.push_front(value)
void push_front(const T_Value& value) {
m_deque.push_front(value);
if (VL_UNLIKELY(T_MaxSize != 0 && m_deque.size() > T_MaxSize)) m_deque.pop_back();
}
// function void q.push_back(value)
void push_back(const T_Value& value) {
if (VL_LIKELY(T_MaxSize == 0 || m_deque.size() < T_MaxSize)) m_deque.push_back(value);
}
// function value_t q.pop_front();
T_Value pop_front() {
if (m_deque.empty()) return m_defaultValue;
T_Value v = m_deque.front();
m_deque.pop_front();
return v;
}
// function value_t q.pop_back();
T_Value pop_back() {
if (m_deque.empty()) return m_defaultValue;
T_Value v = m_deque.back();
m_deque.pop_back();
return v;
}
// Setting. Verilog: assoc[index] = v
// Can't just overload operator[] or provide a "at" reference to set,
// because we need to be able to insert only when the value is set
T_Value& at(vlsint32_t index) {
static T_Value s_throwAway;
// Needs to work for dynamic arrays, so does not use T_MaxSize
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
s_throwAway = atDefault();
return s_throwAway;
} else {
return m_deque[index];
}
}
// Accessing. Verilog: v = assoc[index]
const T_Value& at(vlsint32_t index) const {
static T_Value s_throwAway;
// Needs to work for dynamic arrays, so does not use T_MaxSize
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
return atDefault();
} else {
return m_deque[index];
}
}
// function void q.insert(index, value);
void insert(vlsint32_t index, const T_Value& value) {
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return;
m_deque.insert(m_deque.begin() + index, value);
}
// Return slice q[lsb:msb]
VlQueue slice(vlsint32_t lsb, vlsint32_t msb) const {
VlQueue out;
if (VL_UNLIKELY(lsb < 0)) lsb = 0;
if (VL_UNLIKELY(lsb >= m_deque.size())) lsb = m_deque.size() - 1;
if (VL_UNLIKELY(msb >= m_deque.size())) msb = m_deque.size() - 1;
for (vlsint32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]);
return out;
}
// For save/restore
const_iterator begin() const { return m_deque.begin(); }
const_iterator end() const { return m_deque.end(); }
// Methods
void sort() { std::sort(m_deque.begin(), m_deque.end()); }
template <typename Func> void sort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
std::sort(m_deque.begin(), m_deque.end(), [=](const T_Value& a, const T_Value& b) {
// index number is meaninless with sort, as it changes
return with_func(0, a) < with_func(0, b);
});
}
void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); }
template <typename Func> void rsort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
std::sort(m_deque.rbegin(), m_deque.rend(), [=](const T_Value& a, const T_Value& b) {
// index number is meaninless with sort, as it changes
return with_func(0, a) < with_func(0, b);
});
}
void reverse() { std::reverse(m_deque.begin(), m_deque.end()); }
void shuffle() { std::shuffle(m_deque.begin(), m_deque.end(), VlURNG{}); }
VlQueue unique() const {
VlQueue out;
std::unordered_set<T_Value> saw;
for (const auto& i : m_deque) {
auto it = saw.find(i);
if (it == saw.end()) {
saw.insert(it, i);
out.push_back(i);
}
}
return out;
}
VlQueue<IData> unique_index() const {
VlQueue<IData> out;
IData index = 0;
std::unordered_set<T_Value> saw;
for (const auto& i : m_deque) {
auto it = saw.find(i);
if (it == saw.end()) {
saw.insert(it, i);
out.push_back(index);
}
++index;
}
return out;
}
template <typename Func> VlQueue find(Func with_func) const {
VlQueue out;
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) out.push_back(i);
++index;
}
return out;
}
template <typename Func> VlQueue<IData> find_index(Func with_func) const {
VlQueue<IData> out;
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) out.push_back(index);
++index;
}
return out;
}
template <typename Func> VlQueue find_first(Func with_func) const {
// Can't use std::find_if as need index number
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) return VlQueue::cons(i);
++index;
}
return VlQueue{};
}
template <typename Func> VlQueue<IData> find_first_index(Func with_func) const {
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) return VlQueue<IData>::cons(index);
++index;
}
return VlQueue<IData>{};
}
template <typename Func> VlQueue find_last(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
if (with_func(index, *it)) return VlQueue::cons(*it);
--index;
}
return VlQueue{};
}
template <typename Func> VlQueue<IData> find_last_index(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
if (with_func(index, *it)) return VlQueue<IData>::cons(index);
--index;
}
return VlQueue<IData>{};
}
// Reduction operators
VlQueue min() const {
if (m_deque.empty()) return VlQueue{};
const auto it = std::min_element(m_deque.begin(), m_deque.end());
return VlQueue::cons(*it);
}
VlQueue max() const {
if (m_deque.empty()) return VlQueue{};
const auto it = std::max_element(m_deque.begin(), m_deque.end());
return VlQueue::cons(*it);
}
T_Value r_sum() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_deque) out += i;
return out;
}
template <typename Func> T_Value r_sum(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out += with_func(index++, i);
return out;
}
T_Value r_product() const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
T_Value out{*it};
++it;
for (; it != m_deque.end(); ++it) out *= *it;
return out;
}
template <typename Func> T_Value r_product(Func with_func) const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
IData index = 0;
T_Value out{with_func(index, *it)};
++it;
++index;
for (; it != m_deque.end(); ++it) out *= with_func(index++, *it);
return out;
}
T_Value r_and() const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
T_Value out{*it};
++it;
for (; it != m_deque.end(); ++it) out &= *it;
return out;
}
template <typename Func> T_Value r_and(Func with_func) const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
IData index = 0;
T_Value out{with_func(index, *it)};
++it;
++index;
for (; it != m_deque.end(); ++it) out &= with_func(index, *it);
return out;
}
T_Value r_or() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_deque) out |= i;
return out;
}
template <typename Func> T_Value r_or(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out |= with_func(index++, i);
return out;
}
T_Value r_xor() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_deque) out ^= i;
return out;
}
template <typename Func> T_Value r_xor(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out ^= with_func(index++, i);
return out;
}
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
if (m_deque.empty()) return "'{}"; // No trailing space
std::string out = "'{";
std::string comma;
for (const auto& i : m_deque) {
out += comma + VL_TO_STRING(i);
comma = ", ";
}
return out + "} ";
}
};
template <class T_Value> std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
return obj.to_string();
}
//===================================================================
// Verilog associative array container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
//
template <class T_Key, class T_Value> class VlAssocArray final {
private:
// TYPES
using Map = std::map<T_Key, T_Value>;
public:
using const_iterator = typename Map::const_iterator;
private:
// MEMBERS
Map m_map; // State of the assoc array
T_Value m_defaultValue; // Default value
public:
// CONSTRUCTORS
// m_defaultValue isn't defaulted. Caller's constructor must do it.
VlAssocArray() = default;
~VlAssocArray() = default;
VlAssocArray(const VlAssocArray&) = default;
VlAssocArray(VlAssocArray&&) = default;
VlAssocArray& operator=(const VlAssocArray&) = default;
VlAssocArray& operator=(VlAssocArray&&) = default;
// METHODS
T_Value& atDefault() { return m_defaultValue; }
const T_Value& atDefault() const { return m_defaultValue; }
// Size of array. Verilog: function int size(), or int num()
int size() const { return m_map.size(); }
// Clear array. Verilog: function void delete([input index])
void clear() { m_map.clear(); }
void erase(const T_Key& index) { m_map.erase(index); }
// Return 0/1 if element exists. Verilog: function int exists(input index)
int exists(const T_Key& index) const { return m_map.find(index) != m_map.end(); }
// Return first element. Verilog: function int first(ref index);
int first(T_Key& indexr) const {
const auto it = m_map.cbegin();
if (it == m_map.end()) return 0;
indexr = it->first;
return 1;
}
// Return last element. Verilog: function int last(ref index)
int last(T_Key& indexr) const {
const auto it = m_map.crbegin();
if (it == m_map.rend()) return 0;
indexr = it->first;
return 1;
}
// Return next element. Verilog: function int next(ref index)
int next(T_Key& indexr) const {
auto it = m_map.find(indexr);
if (VL_UNLIKELY(it == m_map.end())) return 0;
++it;
if (VL_UNLIKELY(it == m_map.end())) return 0;
indexr = it->first;
return 1;
}
// Return prev element. Verilog: function int prev(ref index)
int prev(T_Key& indexr) const {
auto it = m_map.find(indexr);
if (VL_UNLIKELY(it == m_map.end())) return 0;
if (VL_UNLIKELY(it == m_map.begin())) return 0;
--it;
indexr = it->first;
return 1;
}
// Setting. Verilog: assoc[index] = v
// Can't just overload operator[] or provide a "at" reference to set,
// because we need to be able to insert only when the value is set
T_Value& at(const T_Key& index) {
const auto it = m_map.find(index);
if (it == m_map.end()) {
std::pair<typename Map::iterator, bool> pit = m_map.emplace(index, m_defaultValue);
return pit.first->second;
}
return it->second;
}
// Accessing. Verilog: v = assoc[index]
const T_Value& at(const T_Key& index) const {
const auto it = m_map.find(index);
if (it == m_map.end()) {
return m_defaultValue;
} else {
return it->second;
}
}
// Setting as a chained operation
VlAssocArray& set(const T_Key& index, const T_Value& value) {
at(index) = value;
return *this;
}
VlAssocArray& setDefault(const T_Value& value) {
atDefault() = value;
return *this;
}
// For save/restore
const_iterator begin() const { return m_map.begin(); }
const_iterator end() const { return m_map.end(); }
// Methods
VlQueue<T_Value> unique() const {
VlQueue<T_Value> out;
std::set<T_Value> saw;
for (const auto& i : m_map) {
auto it = saw.find(i.second);
if (it == saw.end()) {
saw.insert(it, i.second);
out.push_back(i.second);
}
}
return out;
}
VlQueue<T_Key> unique_index() const {
VlQueue<T_Key> out;
std::set<T_Key> saw;
for (const auto& i : m_map) {
auto it = saw.find(i.second);
if (it == saw.end()) {
saw.insert(it, i.second);
out.push_back(i.first);
}
}
return out;
}
template <typename Func> VlQueue<T_Value> find(Func with_func) const {
VlQueue<T_Value> out;
for (const auto& i : m_map)
if (with_func(i.first, i.second)) out.push_back(i.second);
return out;
}
template <typename Func> VlQueue<T_Key> find_index(Func with_func) const {
VlQueue<T_Key> out;
for (const auto& i : m_map)
if (with_func(i.first, i.second)) out.push_back(i.first);
return out;
}
template <typename Func> VlQueue<T_Value> find_first(Func with_func) const {
const auto it
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.end()) return VlQueue<T_Value>{};
return VlQueue<T_Value>::cons(it->second);
}
template <typename Func> VlQueue<T_Key> find_first_index(Func with_func) const {
const auto it
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.end()) return VlQueue<T_Value>{};
return VlQueue<T_Key>::cons(it->first);
}
template <typename Func> VlQueue<T_Value> find_last(Func with_func) const {
const auto it
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.rend()) return VlQueue<T_Value>{};
return VlQueue<T_Value>::cons(it->second);
}
template <typename Func> VlQueue<T_Key> find_last_index(Func with_func) const {
const auto it
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.rend()) return VlQueue<T_Value>{};
return VlQueue<T_Key>::cons(it->first);
}
// Reduction operators
VlQueue<T_Value> min() const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::min_element(
m_map.begin(), m_map.end(),
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return a.second < b.second;
});
return VlQueue<T_Value>::cons(it->second);
}
VlQueue<T_Value> max() const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::max_element(
m_map.begin(), m_map.end(),
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return a.second < b.second;
});
return VlQueue<T_Value>::cons(it->second);
}
T_Value r_sum() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out += i.second;
return out;
}
template <typename Func> T_Value r_sum(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out += with_func(i.first, i.second);
return out;
}
T_Value r_product() const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{it->second};
++it;
for (; it != m_map.end(); ++it) out *= it->second;
return out;
}
template <typename Func> T_Value r_product(Func with_func) const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{with_func(it->first, it->second)};
++it;
for (; it != m_map.end(); ++it) out *= with_func(it->first, it->second);
return out;
}
T_Value r_and() const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{it->second};
++it;
for (; it != m_map.end(); ++it) out &= it->second;
return out;
}
template <typename Func> T_Value r_and(Func with_func) const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{with_func(it->first, it->second)};
++it;
for (; it != m_map.end(); ++it) out &= with_func(it->first, it->second);
return out;
}
T_Value r_or() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out |= i.second;
return out;
}
template <typename Func> T_Value r_or(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out |= with_func(i.first, i.second);
return out;
}
T_Value r_xor() const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out ^= i.second;
return out;
}
template <typename Func> T_Value r_xor(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out ^= with_func(i.first, i.second);
return out;
}
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
if (m_map.empty()) return "'{}"; // No trailing space
std::string out = "'{";
std::string comma;
for (const auto& i : m_map) {
out += comma + VL_TO_STRING(i.first) + ":" + VL_TO_STRING(i.second);
comma = ", ";
}
// Default not printed - maybe random init data
return out + "} ";
}
};
template <class T_Key, class T_Value>
std::string VL_TO_STRING(const VlAssocArray<T_Key, T_Value>& obj) {
return obj.to_string();
}
template <class T_Key, class T_Value>
void VL_READMEM_N(bool hex, int bits, const std::string& filename,
VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
VlReadMem rmem{hex, bits, filename, start, end};
if (VL_UNLIKELY(!rmem.isOpen())) return;
while (true) {
QData addr;
std::string data;
if (rmem.get(addr /*ref*/, data /*ref*/)) {
rmem.setData(&(obj.at(addr)), data);
} else {
break;
}
}
}
template <class T_Key, class T_Value>
void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
const VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
VlWriteMem wmem{hex, bits, filename, start, end};
if (VL_UNLIKELY(!wmem.isOpen())) return;
for (const auto& i : obj) {
const QData addr = i.first;
if (addr >= start && addr <= end) wmem.print(addr, true, &(i.second));
}
}
//===================================================================
/// Verilog unpacked array container
/// For when a standard C++[] array is not sufficient, e.g. an
/// array under a queue, or methods operating on the array.
///
/// A 'struct' as we want this to be an aggregate type that allows
/// static aggregate initialization. Consider data members private.
///
/// This class may get exposed to a Verilated Model's top I/O, if the top
/// IO has an unpacked array.
template <class T_Value, std::size_t T_Depth> struct VlUnpacked final {
// MEMBERS
// This should be the only data member, otherwise generated static initializers need updating
T_Value m_storage[T_Depth]; // Contents of the unpacked array
// CONSTRUCTORS
// Default constructors and destructor are used. Note however that C++20 requires that
// aggregate types do not have a user declared constructor, not even an explicitly defaulted
// one.
// OPERATOR METHODS
// Default copy assignment operators are used.
// METHODS
// Raw access
WData* data() { return &m_storage[0]; }
const WData* data() const { return &m_storage[0]; }
T_Value& operator[](size_t index) { return m_storage[index]; };
const T_Value& operator[](size_t index) const { return m_storage[index]; };
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
std::string out = "'{";
std::string comma;
for (int i = 0; i < T_Depth; ++i) {
out += comma + VL_TO_STRING(m_storage[i]);
comma = ", ";
}
return out + "} ";
}
};
template <class T_Value, std::size_t T_Depth>
std::string VL_TO_STRING(const VlUnpacked<T_Value, T_Depth>& obj) {
return obj.to_string();
}
//===================================================================
// Verilog class reference container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
#define VlClassRef std::shared_ptr
template <class T> // T typically of type VlClassRef<x>
inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
if (VL_UNLIKELY(!t)) Verilated::nullPointerError(filename, linenum);
return t;
}
template <typename T, typename U>
static inline bool VL_CAST_DYNAMIC(VlClassRef<T> in, VlClassRef<U>& outr) {
VlClassRef<U> casted = std::dynamic_pointer_cast<U>(in);
if (VL_LIKELY(casted)) {
outr = casted;
return true;
} else {
return false;
}
}
//======================================================================
#endif // Guard

View File

@ -102,7 +102,7 @@ VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep) {
}
void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
if (isOpen()) return;
// Set member variables
@ -120,7 +120,7 @@ void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
void VerilatedVcd::openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex) {
// Open next filename in concat sequence, mangle filename if
// incFilename is true.
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
openNextImp(incFilename);
}
@ -195,11 +195,11 @@ void VerilatedVcd::makeNameMap() {
if (!hiername.empty() && hiername[0] == '\t') nullScope = true;
}
if (nullScope) {
NameMap* newmapp = new NameMap;
NameMap* const newmapp = new NameMap;
for (const auto& i : *m_namemapp) {
const std::string& hiername = i.first;
const std::string& decl = i.second;
std::string newname = std::string("top");
std::string newname{"top"};
if (hiername[0] != '\t') newname += ' ';
newname += hiername;
newmapp->emplace(newname, decl);
@ -243,7 +243,7 @@ void VerilatedVcd::closeErr() {
void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
// This function is on the flush() call path
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
if (!isOpen()) return;
if (m_evcd) {
printStr("$vcdclose ");
@ -257,7 +257,7 @@ void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
}
void VerilatedVcd::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
const VerilatedLockGuard lock{m_mutex};
VerilatedTrace<VerilatedVcd>::flushBase();
bufferFlush();
}
@ -271,8 +271,9 @@ void VerilatedVcd::printStr(const char* str) {
}
void VerilatedVcd::printQuad(vluint64_t n) {
char buf[100];
VL_SNPRINTF(buf, 100, "%" VL_PRI64 "u", n);
constexpr size_t LEN_STR_QUAD = 40;
char buf[LEN_STR_QUAD];
VL_SNPRINTF(buf, LEN_STR_QUAD, "%" VL_PRI64 "u", n);
printStr(buf);
}
@ -311,7 +312,7 @@ void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
// LCOV_EXCL_START
// write failed, presume error (perhaps out of disk space)
std::string msg
= std::string("VerilatedVcd::bufferFlush: ") + std::strerror(errno);
= std::string{"VerilatedVcd::bufferFlush: "} + std::strerror(errno);
VL_FATAL_MT("", 0, "", msg.c_str());
closeErr();
break;
@ -331,7 +332,7 @@ char* VerilatedVcd::writeCode(char* writep, vluint32_t code) {
*writep++ = static_cast<char>('!' + code % 94);
code /= 94;
while (code) {
code--;
--code;
*writep++ = static_cast<char>('!' + code % 94);
code /= 94;
}
@ -355,9 +356,9 @@ void VerilatedVcd::dumpHeader() {
const time_t tick = time(nullptr);
tm ticktm;
VL_LOCALTIME_R(&tick, &ticktm);
constexpr int bufsize = 50;
char buf[bufsize];
std::strftime(buf, bufsize, "%c", &ticktm);
constexpr size_t LEN_BUF = 50;
char buf[LEN_BUF];
std::strftime(buf, LEN_BUF, "%c", &ticktm);
printStr(buf);
}
printStr(" $end\n");
@ -393,8 +394,8 @@ void VerilatedVcd::dumpHeader() {
// Skip common prefix, it must break at a space or tab
for (; *np && (*np == *lp); np++, lp++) {}
while (np != hiername && *np && *np != ' ' && *np != '\t') {
np--;
lp--;
--np;
--lp;
}
// printf("hier %s\n lp=%s\n np=%s\n",hiername,lp,np);
@ -430,9 +431,9 @@ void VerilatedVcd::dumpHeader() {
for (; *np && *np != ' ' && *np != '\t'; np++) {
if (*np == '[') {
printStr("(");
printStr("[");
} else if (*np == ']') {
printStr(")");
printStr("]");
} else if (!(*np & '\x80')) {
*m_writep++ = *np;
}
@ -531,7 +532,7 @@ void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep,
decl += " ";
decl += basename;
if (array) {
VL_SNPRINTF(buf, bufsize, "(%d)", arraynum);
VL_SNPRINTF(buf, bufsize, "[%d]", arraynum);
decl += buf;
hiername += buf;
}
@ -896,9 +897,9 @@ void vcdTestMain(const char* filenamep) {
VerilatedVcdC* vcdp = new VerilatedVcdC;
vcdp->evcd(true);
vcdp->set_time_unit("1ms");
vcdp->set_time_unit(std::string("1ms"));
vcdp->set_time_unit(std::string{"1ms"});
vcdp->set_time_resolution("1ns");
vcdp->set_time_resolution(std::string("1ns"));
vcdp->set_time_resolution(std::string{"1ns"});
vcdp->spTrace()->addInitCb(&vcdInit, 0);
vcdp->spTrace()->addFullCb(&vcdFull, 0);
vcdp->spTrace()->addChgCb(&vcdChange, 0);
@ -927,7 +928,7 @@ void vcdTestMain(const char* filenamep) {
vcdp->dump(++timestamp);
vcdp->dump(++timestamp);
# ifdef VERILATED_VCD_TEST_64BIT
vluint64_t bytesPerDump = 15ULL;
const vluint64_t bytesPerDump = 15ULL;
for (vluint64_t i = 0; i < ((1ULL << 32) / bytesPerDump); i++) {
v1 = i;
vcdp->dump(++timestamp);

View File

@ -44,9 +44,9 @@ public:
// We want to avoid a depreciated warning, but still be back compatible.
// Turning off the message just for this still results in an
// annoying "to turn off" message.
const sc_time t1sec(1, SC_SEC);
const sc_time t1sec{1, SC_SEC};
if (t1sec.to_default_time_units() != 0) {
const sc_time tunits(1.0 / t1sec.to_default_time_units(), SC_SEC);
const sc_time tunits{1.0 / t1sec.to_default_time_units(), SC_SEC};
spTrace()->set_time_unit(tunits.to_string());
}
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());

View File

@ -80,7 +80,7 @@ public:
static const size_t chunk = 96;
if (VL_UNCOVERABLE(size > chunk)) VL_FATAL_MT(__FILE__, __LINE__, "", "increase chunk");
if (VL_LIKELY(t_freeHead)) {
vluint8_t* newp = t_freeHead;
vluint8_t* const newp = t_freeHead;
t_freeHead = *(reinterpret_cast<vluint8_t**>(newp));
*(reinterpret_cast<vluint32_t*>(newp)) = activeMagic();
return newp + 8;
@ -91,7 +91,7 @@ public:
return newp + 8;
}
static void operator delete(void* obj, size_t /*size*/)VL_MT_SAFE {
vluint8_t* oldp = (static_cast<vluint8_t*>(obj)) - 8;
vluint8_t* const oldp = (static_cast<vluint8_t*>(obj)) - 8;
if (VL_UNLIKELY(*(reinterpret_cast<vluint32_t*>(oldp)) != activeMagic())) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"vpi_release_handle() called on same object twice, or on non-Verilator "
@ -128,7 +128,7 @@ class VerilatedVpioTimedCb final : public VerilatedVpio {
public:
VerilatedVpioTimedCb(vluint64_t id, QData time)
: m_id(id)
: m_id{id}
, m_time{time} {}
virtual ~VerilatedVpioTimedCb() override = default;
static VerilatedVpioTimedCb* castp(vpiHandle h) {
@ -147,7 +147,7 @@ class VerilatedVpioReasonCb final : public VerilatedVpio {
public:
// cppcheck-suppress uninitVar // m_value
VerilatedVpioReasonCb(vluint64_t id, PLI_INT32 reason)
: m_id(id)
: m_id{id}
, m_reason{reason} {}
virtual ~VerilatedVpioReasonCb() override = default;
static VerilatedVpioReasonCb* castp(vpiHandle h) {
@ -200,7 +200,7 @@ public:
virtual const char* name() const override { return m_varp->name(); }
virtual const char* fullname() const override {
static VL_THREAD_LOCAL std::string t_out;
t_out = std::string(m_scopep->name()) + "." + name();
t_out = std::string{m_scopep->name()} + "." + name();
return t_out.c_str();
}
};
@ -208,7 +208,7 @@ public:
class VerilatedVpioParam final : public VerilatedVpioVarBase {
public:
VerilatedVpioParam(const VerilatedVar* varp, const VerilatedScope* scopep)
: VerilatedVpioVarBase(varp, scopep) {}
: VerilatedVpioVarBase{varp, scopep} {}
virtual ~VerilatedVpioParam() override = default;
static VerilatedVpioParam* castp(vpiHandle h) {
@ -252,7 +252,7 @@ public:
return nullptr;
}
m_done = true;
return ((new VerilatedVpioRange(m_range))->castVpiHandle());
return ((new VerilatedVpioRange{m_range})->castVpiHandle());
}
};
@ -286,13 +286,13 @@ protected:
public:
VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep)
: VerilatedVpioVarBase(varp, scopep) {
: VerilatedVpioVarBase{varp, scopep} {
m_mask.u32 = VL_MASK_I(varp->packed().elements());
m_entSize = varp->entSize();
m_varDatap = varp->datap();
}
explicit VerilatedVpioVar(const VerilatedVpioVar* varp)
: VerilatedVpioVarBase(varp) {
: VerilatedVpioVarBase{varp} {
if (varp) {
m_mask.u32 = varp->m_mask.u32;
m_entSize = varp->m_entSize;
@ -343,9 +343,10 @@ public:
virtual const VerilatedRange* rangep() const override { return &(varp()->packed()); }
virtual const char* fullname() const override {
static VL_THREAD_LOCAL std::string t_out;
char num[25];
VL_SNPRINTF(num, 25, "%d", m_index);
t_out = std::string(scopep()->name()) + "." + name() + "[" + num + "]";
constexpr size_t LEN_MAX_INDEX = 25;
char num[LEN_MAX_INDEX];
VL_SNPRINTF(num, LEN_MAX_INDEX, "%d", m_index);
t_out = std::string{scopep()->name()} + "." + name() + "[" + num + "]";
return t_out.c_str();
}
};
@ -379,7 +380,7 @@ public:
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
}
return ((new VerilatedVpioVar(&(m_it->second), m_scopep))->castVpiHandle());
return ((new VerilatedVpioVar{&(m_it->second), m_scopep})->castVpiHandle());
}
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr; // End of list - only one deep
@ -457,7 +458,7 @@ public:
return nullptr;
}
const VerilatedScope* modp = *m_it++;
return (new VerilatedVpioModule(modp))->castVpiHandle();
return (new VerilatedVpioModule{modp})->castVpiHandle();
}
};
@ -475,16 +476,16 @@ class VerilatedVpiCbHolder final {
public:
// cppcheck-suppress uninitVar // m_value
VerilatedVpiCbHolder(vluint64_t id, const s_cb_data* cbDatap, const VerilatedVpioVar* varop)
: m_id(id)
, m_cbData(*cbDatap)
, m_varo(varop) {
: m_id{id}
, m_cbData{*cbDatap}
, m_varo{varop} {
m_value.format = cbDatap->value ? cbDatap->value->format : vpiSuppressVal;
m_cbData.value = &m_value;
if (varop) {
m_cbData.obj = m_varo.castVpiHandle();
m_varo.createPrevDatap();
} else {
m_cbData.obj = NULL;
m_cbData.obj = nullptr;
}
}
~VerilatedVpiCbHolder() = default;
@ -626,16 +627,18 @@ public:
continue;
}
VerilatedVpiCbHolder& ho = *it++;
if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(ho.cb_datap()->obj)) {
void* newDatap = varop->varDatap();
void* prevDatap = varop->prevDatap(); // Was malloced when we added the callback
if (VerilatedVpioVar* const varop = VerilatedVpioVar::castp(ho.cb_datap()->obj)) {
void* const newDatap = varop->varDatap();
void* const prevDatap
= varop->prevDatap(); // Was malloced when we added the callback
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
varop->fullname(), *((CData*)newDatap),
*((CData*)prevDatap), newDatap, prevDatap););
varop->fullname(), *(static_cast<CData*>(newDatap)),
*(static_cast<CData*>(prevDatap)), newDatap,
prevDatap););
if (std::memcmp(prevDatap, newDatap, varop->entSize()) != 0) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" VL_PRI64
"d %s v[0]=%d\n",
ho.id(), varop->fullname(), *((CData*)newDatap)););
VL_DEBUG_IF_PLI(
VL_DBG_MSGF("- vpi: value_callback %" VL_PRI64 "d %s v[0]=%d\n", ho.id(),
varop->fullname(), *(static_cast<CData*>(newDatap))););
update.insert(varop);
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
(ho.cb_rtnp())(ho.cb_datap());
@ -706,7 +709,8 @@ public:
va_end(args);
m_errorInfo.state = vpiPLI;
t_filehold = file;
setError((PLI_BYTE8*)m_buff, nullptr, const_cast<PLI_BYTE8*>(t_filehold.c_str()), line);
setError(static_cast<PLI_BYTE8*>(m_buff), nullptr,
const_cast<PLI_BYTE8*>(t_filehold.c_str()), line);
}
p_vpi_error_info getError() {
if (m_flag) return &m_errorInfo;
@ -758,7 +762,7 @@ PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() {
VerilatedVpiError* VerilatedVpiImp::error_info() VL_MT_UNSAFE_ONE {
VerilatedVpiImp::assertOneCheck();
if (VL_UNLIKELY(!s().m_errorInfop)) s().m_errorInfop = new VerilatedVpiError();
if (VL_UNLIKELY(!s().m_errorInfop)) s().m_errorInfop = new VerilatedVpiError;
return s().m_errorInfop;
}
@ -1108,13 +1112,13 @@ const char* VerilatedVpiError::strFromVpiProp(PLI_INT32 vpiVal) VL_MT_SAFE {
#define SELF_CHECK_RESULT_CSTR(got, exp) \
if (0 != std::strcmp((got), (exp))) { \
std::string msg \
= std::string("%Error: ") + "GOT = '" + got + "'" + " EXP = '" + exp + "'"; \
= std::string{"%Error: "} + "GOT = '" + (got) + "'" + " EXP = '" + (exp) + "'"; \
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); \
}
#define SELF_CHECK_ENUM_STR(fn, enumn) \
do { \
const char* strVal = VerilatedVpiError::fn(enumn); \
const char* const strVal = VerilatedVpiError::fn(enumn); \
SELF_CHECK_RESULT_CSTR(strVal, #enumn); \
} while (0)
@ -1358,7 +1362,7 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope);
std::string scopeAndName = namep;
if (voScopep) {
scopeAndName = std::string(voScopep->fullname()) + "." + namep;
scopeAndName = std::string{voScopep->fullname()} + "." + namep;
namep = const_cast<PLI_BYTE8*>(scopeAndName.c_str());
}
{
@ -1366,9 +1370,9 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
scopep = Verilated::threadContextp()->scopeFind(namep);
if (scopep) { // Whole thing found as a scope
if (scopep->type() == VerilatedScope::SCOPE_MODULE) {
return (new VerilatedVpioModule(scopep))->castVpiHandle();
return (new VerilatedVpioModule{scopep})->castVpiHandle();
} else {
return (new VerilatedVpioScope(scopep))->castVpiHandle();
return (new VerilatedVpioScope{scopep})->castVpiHandle();
}
}
const char* baseNamep = scopeAndName.c_str();
@ -1376,7 +1380,8 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
const char* const dotp = std::strrchr(namep, '.');
if (VL_LIKELY(dotp)) {
baseNamep = dotp + 1;
scopename = std::string(namep, dotp - namep);
const size_t len = dotp - namep;
scopename = std::string{namep, len};
}
if (scopename.find('.') == std::string::npos) {
@ -1393,9 +1398,9 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
if (!varp) return nullptr;
if (varp->isParam()) {
return (new VerilatedVpioParam(varp, scopep))->castVpiHandle();
return (new VerilatedVpioParam{varp, scopep})->castVpiHandle();
} else {
return (new VerilatedVpioVar(varp, scopep))->castVpiHandle();
return (new VerilatedVpioVar{varp, scopep})->castVpiHandle();
}
}
@ -1405,7 +1410,7 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) {
VerilatedVpiImp::assertOneCheck();
VL_VPI_ERROR_RESET_();
// Memory words are not indexable
VerilatedVpioMemoryWord* vop = VerilatedVpioMemoryWord::castp(object);
VerilatedVpioMemoryWord* const vop = VerilatedVpioMemoryWord::castp(object);
if (VL_UNLIKELY(vop)) return nullptr;
const VerilatedVpioVar* const varop = VerilatedVpioVar::castp(object);
if (VL_LIKELY(varop)) {
@ -1414,15 +1419,15 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) {
if (VL_UNLIKELY(indx > varop->varp()->unpacked().left()
|| indx < varop->varp()->unpacked().right()))
return nullptr;
return (new VerilatedVpioMemoryWord(varop->varp(), varop->scopep(), indx,
indx - varop->varp()->unpacked().right()))
return (new VerilatedVpioMemoryWord{varop->varp(), varop->scopep(), indx,
indx - varop->varp()->unpacked().right()})
->castVpiHandle();
}
if (VL_UNLIKELY(indx < varop->varp()->unpacked().left()
|| indx > varop->varp()->unpacked().right()))
return nullptr;
return (new VerilatedVpioMemoryWord(varop->varp(), varop->scopep(), indx,
indx - varop->varp()->unpacked().left()))
return (new VerilatedVpioMemoryWord{varop->varp(), varop->scopep(), indx,
indx - varop->varp()->unpacked().left()})
->castVpiHandle();
}
VL_VPI_INTERNAL_(__FILE__, __LINE__, "%s : can't resolve handle", __func__);
@ -1437,12 +1442,12 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
VL_VPI_ERROR_RESET_();
switch (type) {
case vpiLeftRange: {
if (VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object)) {
if (VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object)) {
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
return (new VerilatedVpioConst(vop->rangep()->left()))->castVpiHandle();
} else if (VerilatedVpioRange* vop = VerilatedVpioRange::castp(object)) {
return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle();
} else if (VerilatedVpioRange* const vop = VerilatedVpioRange::castp(object)) {
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
return (new VerilatedVpioConst(vop->rangep()->left()))->castVpiHandle();
return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle();
}
VL_VPI_WARNING_(__FILE__, __LINE__,
"%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
@ -1450,12 +1455,12 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
return nullptr;
}
case vpiRightRange: {
if (VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object)) {
if (VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object)) {
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
return (new VerilatedVpioConst(vop->rangep()->right()))->castVpiHandle();
} else if (VerilatedVpioRange* vop = VerilatedVpioRange::castp(object)) {
return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle();
} else if (VerilatedVpioRange* const vop = VerilatedVpioRange::castp(object)) {
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
return (new VerilatedVpioConst(vop->rangep()->right()))->castVpiHandle();
return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle();
}
VL_VPI_WARNING_(__FILE__, __LINE__,
"%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
@ -1463,19 +1468,20 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
return nullptr;
}
case vpiIndex: {
VerilatedVpioVar* vop = VerilatedVpioVar::castp(object);
VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
return (new VerilatedVpioConst(vop->index()))->castVpiHandle();
const vlsint32_t val = vop->index();
return (new VerilatedVpioConst{val})->castVpiHandle();
}
case vpiScope: {
VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object);
VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
return (new VerilatedVpioScope(vop->scopep()))->castVpiHandle();
return (new VerilatedVpioScope{vop->scopep()})->castVpiHandle();
}
case vpiParent: {
VerilatedVpioMemoryWord* vop = VerilatedVpioMemoryWord::castp(object);
VerilatedVpioMemoryWord* const vop = VerilatedVpioMemoryWord::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
return (new VerilatedVpioVar(vop->varp(), vop->scopep()))->castVpiHandle();
return (new VerilatedVpioVar{vop->varp(), vop->scopep()})->castVpiHandle();
}
default:
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
@ -1505,7 +1511,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
VerilatedVpiError::strFromVpiMethod(type), vop->fullname(),
vop->varp()->dims());
}
return (new VerilatedVpioMemoryWordIter(object, vop->varp()))->castVpiHandle();
return (new VerilatedVpioMemoryWordIter{object, vop->varp()})->castVpiHandle();
}
case vpiRange: {
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
@ -1518,12 +1524,12 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
VerilatedVpiError::strFromVpiMethod(type), vop->fullname(),
vop->varp()->dims());
}
return ((new VerilatedVpioRangeIter(vop->rangep()))->castVpiHandle());
return ((new VerilatedVpioRangeIter{vop->rangep()})->castVpiHandle());
}
case vpiReg: {
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
return ((new VerilatedVpioVarIter(vop->scopep()))->castVpiHandle());
return ((new VerilatedVpioVarIter{vop->scopep()})->castVpiHandle());
}
case vpiModule: {
const VerilatedVpioModule* const vop = VerilatedVpioModule::castp(object);
@ -1531,7 +1537,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
const VerilatedScope* const modp = vop ? vop->scopep() : nullptr;
const auto it = vlstd::as_const(map)->find(const_cast<VerilatedScope*>(modp));
if (it == map->end()) return nullptr;
return ((new VerilatedVpioModuleIter(it->second))->castVpiHandle());
return ((new VerilatedVpioModuleIter{it->second})->castVpiHandle());
}
default:
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
@ -1543,7 +1549,7 @@ vpiHandle vpi_scan(vpiHandle object) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_scan %p\n", object););
VerilatedVpiImp::assertOneCheck();
VL_VPI_ERROR_RESET_();
VerilatedVpio* vop = VerilatedVpio::castp(object);
VerilatedVpio* const vop = VerilatedVpio::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
return vop->dovpi_scan();
}
@ -1689,7 +1695,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
const char* fullname) {
if (!vl_check_format(varp, valuep, fullname, true)) return;
// Maximum required size is for binary string, one byte per bit plus null termination
static VL_THREAD_LOCAL char t_outStr[1 + VL_MULS_MAX_WORDS * 32];
static VL_THREAD_LOCAL char t_outStr[VL_VALUE_STRING_MAX_WORDS * VL_EDATASIZE + 1];
// cppcheck-suppress variableScope
static VL_THREAD_LOCAL int t_outStrSz = sizeof(t_outStr) - 1;
// We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal
@ -1697,7 +1703,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
if (valuep->format == vpiVectorVal) {
// Vector pointer must come from our memory pool
// It only needs to persist until the next vpi_get_value
static VL_THREAD_LOCAL t_vpi_vecval t_out[VL_MULS_MAX_WORDS * 2];
static VL_THREAD_LOCAL t_vpi_vecval t_out[VL_VALUE_STRING_MAX_WORDS * 2];
valuep->value.vector = t_out;
if (varp->vltype() == VLVT_UINT8) {
t_out[0].aval = *(reinterpret_cast<CData*>(varDatap));
@ -1720,10 +1726,10 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
return;
} else if (varp->vltype() == VLVT_WDATA) {
int words = VL_WORDS_I(varp->packed().elements());
if (VL_UNCOVERABLE(words >= VL_MULS_MAX_WORDS)) {
VL_FATAL_MT(
__FILE__, __LINE__, "",
"vpi_get_value with more than VL_MULS_MAX_WORDS; increase and recompile");
if (VL_UNCOVERABLE(words >= VL_VALUE_STRING_MAX_WORDS)) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"vpi_get_value with more than VL_VALUE_STRING_MAX_WORDS; increase and "
"recompile");
}
const WDataInP datap = (reinterpret_cast<EData*>(varDatap));
for (int i = 0; i < words; ++i) {
@ -1743,9 +1749,9 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)",
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
VL_MULS_MAX_WORDS, bits);
VL_VALUE_STRING_MAX_WORDS, bits);
}
for (i = 0; i < bits; ++i) {
char val = (datap[i >> 3] >> (i & 7)) & 1;
@ -1764,9 +1770,9 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)",
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
VL_MULS_MAX_WORDS, chars);
VL_VALUE_STRING_MAX_WORDS, chars);
chars = t_outStrSz;
}
for (i = 0; i < chars; ++i) {
@ -1822,9 +1828,9 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)",
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
VL_MULS_MAX_WORDS, chars);
VL_VALUE_STRING_MAX_WORDS, chars);
chars = t_outStrSz;
}
for (i = 0; i < chars; ++i) {
@ -1853,12 +1859,12 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
int i;
if (bytes > t_outStrSz) {
// limit maximum size of output to size of buffer to prevent overrun.
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname,
t_outStrSz, VL_MULS_MAX_WORDS, bytes);
VL_VPI_WARNING_(__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than "
"required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format),
fullname, t_outStrSz, VL_VALUE_STRING_MAX_WORDS, bytes);
bytes = t_outStrSz;
}
for (i = 0; i < bytes; ++i) {
@ -1893,13 +1899,13 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) {
VL_VPI_ERROR_RESET_();
if (VL_UNLIKELY(!valuep)) return;
if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) {
if (VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) {
vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname());
return;
} else if (const VerilatedVpioParam* const vop = VerilatedVpioParam::castp(object)) {
vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname());
return;
} else if (VerilatedVpioConst* vop = VerilatedVpioConst::castp(object)) {
} else if (VerilatedVpioConst* const vop = VerilatedVpioConst::castp(object)) {
if (valuep->format == vpiIntVal) {
valuep->value.integer = vop->num();
return;
@ -2161,7 +2167,7 @@ void vpi_get_time(vpiHandle object, p_vpi_time time_p) {
return;
} else if (time_p->type == vpiScaledRealTime) {
double dtime = VL_TIME_D();
if (VerilatedVpioScope* vop = VerilatedVpioScope::castp(object)) {
if (VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object)) {
const int scalePow10
= Verilated::threadContextp()->timeprecision() - vop->scopep()->timeunit();
const double scale = vl_time_multiplier(scalePow10); // e.g. 0.0001
@ -2271,7 +2277,7 @@ PLI_INT32 vpi_free_object(vpiHandle object) {
PLI_INT32 vpi_release_handle(vpiHandle object) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_release_handle %p\n", object););
VerilatedVpiImp::assertOneCheck();
VerilatedVpio* vop = VerilatedVpio::castp(object);
VerilatedVpio* const vop = VerilatedVpio::castp(object);
VL_VPI_ERROR_RESET_();
if (VL_UNLIKELY(!vop)) return 0;
VL_DO_DANGLING(delete vop, vop);

View File

@ -421,7 +421,8 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
// Verilated function size macros
#define VL_MULS_MAX_WORDS 16 ///< Max size in words of MULS operation
#define VL_TO_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation
#define VL_VALUE_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation
#define VL_VALUE_STRING_MAX_CHARS (VL_VALUE_STRING_MAX_WORDS * VL_EDATASIZE / VL_BYTESIZE)
//=========================================================================
// Base macros

View File

@ -66,11 +66,11 @@ def cwrite(filename):
fh.write(" auto* gp = &m_graph;\n")
for ver in sorted(Vertexes, key=lambda ver: ver['num']):
fh.write(
" auto* %s = new V3GraphTestVertex(gp, \"%s\"); if (%s) {}\n"
" auto* %s = new V3GraphTestVertex{gp, \"%s\"}; if (%s) {}\n"
% (ver['name'], ver['name'], ver['name']))
fh.write("\n")
for edge in Edges:
fh.write(" new V3GraphEdge(gp, %s, %s, %s, %s);\n" %
fh.write(" new V3GraphEdge{gp, %s, %s, %s, %s};\n" %
(edge['from'], edge['to'], edge['weight'],
"true" if edge['cutable'] else "false"))
fh.write("}\n")

View File

@ -169,6 +169,7 @@ RAW_OBJS = \
V3Clean.o \
V3Clock.o \
V3Combine.o \
V3Common.o \
V3Config.o \
V3Const__gen.o \
V3Coverage.o \
@ -179,10 +180,11 @@ RAW_OBJS = \
V3DepthBlock.o \
V3Descope.o \
V3DupFinder.o \
V3EmitC.o \
V3EmitCBase.o \
V3EmitCConstPool.o \
V3EmitCFunc.o \
V3EmitCHeaders.o \
V3EmitCImp.o \
V3EmitCInlines.o \
V3EmitCMain.o \
V3EmitCMake.o \
@ -254,6 +256,7 @@ RAW_OBJS = \
V3Undriven.o \
V3Unknown.o \
V3Unroll.o \
V3VariableOrder.o \
V3Waiver.o \
V3Width.o \
V3WidthSel.o \

View File

@ -64,11 +64,11 @@ private:
public:
LatchDetectGraphVertex(V3Graph* graphp, const string& name, VertexType type = VT_BLOCK)
: V3GraphVertex(graphp)
, m_name(name)
, m_type(type) {}
virtual string name() const { return m_name + " " + typestr(); }
virtual string dotColor() const { return user() ? "green" : "black"; }
: V3GraphVertex{graphp}
, m_name{name}
, m_type{type} {}
virtual string name() const override { return m_name + " " + typestr(); }
virtual string dotColor() const override { return user() ? "green" : "black"; }
virtual int type() const { return m_type; }
};
@ -120,7 +120,7 @@ protected:
public:
LatchDetectGraph() { clear(); }
~LatchDetectGraph() { clear(); }
virtual ~LatchDetectGraph() override { clear(); }
// ACCESSORS
LatchDetectGraphVertex* currentp() { return m_curVertexp; }
void currentp(LatchDetectGraphVertex* vertex) { m_curVertexp = vertex; }
@ -142,17 +142,17 @@ public:
// Add a new control path and connect it to its parent
LatchDetectGraphVertex* addPathVertex(LatchDetectGraphVertex* parent, const string& name,
bool branch = false) {
m_curVertexp = new LatchDetectGraphVertex(this, name,
m_curVertexp = new LatchDetectGraphVertex{this, name,
branch ? LatchDetectGraphVertex::VT_BRANCH
: LatchDetectGraphVertex::VT_BLOCK);
new V3GraphEdge(this, parent, m_curVertexp, 1);
: LatchDetectGraphVertex::VT_BLOCK};
new V3GraphEdge{this, parent, m_curVertexp, 1};
return m_curVertexp;
}
// Add a new output variable vertex and store a pointer to it in the user1 field of the
// variables AstNode
LatchDetectGraphVertex* addOutputVertex(AstVarRef* nodep) {
LatchDetectGraphVertex* outVertexp
= new LatchDetectGraphVertex(this, nodep->name(), LatchDetectGraphVertex::VT_OUTPUT);
= new LatchDetectGraphVertex{this, nodep->name(), LatchDetectGraphVertex::VT_OUTPUT};
nodep->varp()->user1p(outVertexp);
m_outputs.push_back(nodep);
return outVertexp;
@ -437,7 +437,7 @@ private:
virtual void visit(AstInitial* nodep) override {
// Relink to IACTIVE, unless already under it
UINFO(4, " INITIAL " << nodep << endl);
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_INITIAL);
ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
AstActive* wantactivep = m_namer.getIActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
@ -470,7 +470,7 @@ private:
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_INITIAL);
ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
if (!m_scopeFinalp) {
m_scopeFinalp = new AstCFunc(
nodep->fileline(), "_final_" + m_namer.scopep()->nameDotless(), m_namer.scopep());
@ -540,14 +540,14 @@ private:
// Warn and/or convert any delayed assignments
if (combo && !sequent) {
ActiveLatchCheckVisitor latchvisitor(nodep, kwd);
ActiveLatchCheckVisitor latchvisitor{nodep, kwd};
if (kwd == VAlwaysKwd::ALWAYS_LATCH) {
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_LATCH);
ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_LATCH};
} else {
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_COMBO);
ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_COMBO};
}
} else if (!combo && sequent) {
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_SEQ);
ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_SEQ};
}
}
virtual void visit(AstAlways* nodep) override {
@ -620,6 +620,6 @@ public:
void V3Active::activeAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ActiveVisitor visitor(nodep); } // Destruct before checking
{ ActiveVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("active", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

View File

@ -136,6 +136,6 @@ public:
void V3ActiveTop::activeTopAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ActiveTopVisitor visitor(nodep); } // Destruct before checking
{ ActiveTopVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("activetop", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

View File

@ -474,6 +474,6 @@ public:
void V3Assert::assertAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ AssertVisitor visitor(nodep); } // Destruct before checking
{ AssertVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("assert", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

View File

@ -205,6 +205,6 @@ public:
void V3AssertPre::assertPreAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ AssertPreVisitor visitor(nodep); } // Destruct before checking
{ AssertPreVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("assertpre", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

View File

@ -74,9 +74,10 @@ AstNode::AstNode(AstType t, FileLine* fl)
m_clonep = nullptr;
m_cloneCnt = 0;
// Attributes
m_didWidth = false;
m_doingWidth = false;
m_protect = true;
m_flags.didWidth = false;
m_flags.doingWidth = false;
m_flags.protect = true;
m_flags.unused = 0; // Initializing this avoids a read-modify-write on construction
m_user1u = VNUser(0);
m_user1Cnt = 0;
m_user2u = VNUser(0);
@ -1123,7 +1124,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo
if (doDump) {
{ // Write log & close
UINFO(2, "Dumping " << filename << endl);
const std::unique_ptr<std::ofstream> logsp(V3File::new_ofstream(filename, append));
const std::unique_ptr<std::ofstream> logsp{V3File::new_ofstream(filename, append)};
if (logsp->fail()) v3fatal("Can't write " << filename);
*logsp << "Verilator Tree Dump (format 0x3900) from <e" << std::dec << editCountLast();
*logsp << "> to <e" << std::dec << editCountGbl() << ">\n";
@ -1263,12 +1264,15 @@ AstNodeDType* AstNode::findBitRangeDType(const VNumRange& range, int widthMin,
AstBasicDType* AstNode::findInsertSameDType(AstBasicDType* nodep) {
return v3Global.rootp()->typeTablep()->findInsertSameDType(nodep);
}
AstNodeDType* AstNode::findVoidDType() const {
return v3Global.rootp()->typeTablep()->findVoidDType(fileline());
AstNodeDType* AstNode::findEmptyQueueDType() const {
return v3Global.rootp()->typeTablep()->findEmptyQueueDType(fileline());
}
AstNodeDType* AstNode::findQueueIndexDType() const {
return v3Global.rootp()->typeTablep()->findQueueIndexDType(fileline());
}
AstNodeDType* AstNode::findVoidDType() const {
return v3Global.rootp()->typeTablep()->findVoidDType(fileline());
}
//######################################################################
// AstNVisitor

View File

@ -24,6 +24,7 @@
#include "V3FileLine.h"
#include "V3Number.h"
#include "V3Global.h"
#include "V3Broken.h"
#include <cmath>
#include <type_traits>
@ -50,7 +51,7 @@ using MTaskIdSet = std::set<int>; // Set of mtaskIds for Var sorting
// For broken() function, return error string if have a match
#define BROKEN_RTN(test) \
do { \
if (VL_UNCOVERABLE(test)) return #test; \
if (VL_UNCOVERABLE(test)) return "'" #test "' @ " __FILE__ ":" VL_STRINGIFY(__LINE__); \
} while (false)
// For broken() function, return error string if a base of this class has a match
#define BROKEN_BASE_RTN(test) \
@ -238,39 +239,6 @@ inline bool operator==(AstPragmaType::en lhs, const AstPragmaType& rhs) { return
//######################################################################
class AstCFuncType final {
public:
enum en : uint8_t {
FT_NORMAL,
TRACE_REGISTER,
TRACE_INIT,
TRACE_INIT_SUB,
TRACE_FULL,
TRACE_FULL_SUB,
TRACE_CHANGE,
TRACE_CHANGE_SUB,
TRACE_CLEANUP
};
enum en m_e;
inline AstCFuncType()
: m_e{FT_NORMAL} {}
// cppcheck-suppress noExplicitConstructor
inline AstCFuncType(en _e)
: m_e{_e} {}
explicit inline AstCFuncType(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
operator en() const { return m_e; }
// METHODS
bool isTrace() const { return m_e != FT_NORMAL; }
};
inline bool operator==(const AstCFuncType& lhs, const AstCFuncType& rhs) {
return lhs.m_e == rhs.m_e;
}
inline bool operator==(const AstCFuncType& lhs, AstCFuncType::en rhs) { return lhs.m_e == rhs; }
inline bool operator==(AstCFuncType::en lhs, const AstCFuncType& rhs) { return lhs == rhs.m_e; }
//######################################################################
class VEdgeType final {
public:
// REMEMBER to edit the strings below too
@ -561,6 +529,25 @@ public:
bool isEventValue() const { return m_e == EVENTVALUE; }
bool isString() const { return m_e == STRING; }
bool isMTaskState() const { return m_e == MTASKSTATE; }
// Does this represent a C++ LiteralType? (can be constexpr)
bool isLiteralType() const {
switch (m_e) {
case BIT:
case BYTE:
case CHANDLE:
case INT:
case INTEGER:
case LOGIC:
case LONGINT:
case DOUBLE:
case SHORTINT:
case SCOPEPTR:
case CHARPTR:
case UINT32:
case UINT64: return true;
default: return false;
}
}
};
inline bool operator==(const AstBasicDTypeKwd& lhs, const AstBasicDTypeKwd& rhs) {
return lhs.m_e == rhs.m_e;
@ -1367,8 +1354,32 @@ class AstNode VL_NOT_FINAL {
const AstType m_type; // Node sub-type identifier
// ^ ASTNODE_PREFETCH depends on above ordering of members
// padding - 2 extra bytes here after m_type
int m_cloneCnt; // Mark of when userp was set
// AstType is 2 bytes, so we can stick another 6 bytes after it to utilize what would
// otherwise be padding (on a 64-bit system). We stick the attribute flags, broken state,
// and the clone count here.
struct {
bool didWidth : 1; // Did V3Width computation
bool doingWidth : 1; // Inside V3Width
bool protect : 1; // Protect name if protection is on
// Space for more flags here (there must be 8 bits in total)
uint8_t unused : 5;
} m_flags; // Attribute flags
// State variable used by V3Broken for consistency checking. The top bit of this is byte is a
// flag, representing V3Broken is currently proceeding under this node. The bottom 7 bits are
// a generation number. This is hot when --debug-checks so we access as a whole to avoid bit
// field masking resulting in unnecessary read-modify-write ops.
uint8_t m_brokenState = 0;
int m_cloneCnt; // Sequence number for when last clone was made
#if defined(__x86_64__) && defined(__gnu_linux__)
// Only assert this on known platforms, as it only affects performance, not correctness
static_assert(sizeof(m_type) + sizeof(m_flags) + sizeof(m_brokenState) + sizeof(m_cloneCnt)
<= sizeof(void*),
"packing requires padding");
#endif
AstNodeDType* m_dtypep; // Data type of output or assignment (etc)
AstNode* m_headtailp; // When at begin/end of list, the opposite end of the list
@ -1381,12 +1392,6 @@ class AstNode VL_NOT_FINAL {
AstNode* m_clonep; // Pointer to clone of/ source of this module (for *LAST* cloneTree() ONLY)
static int s_cloneCntGbl; // Count of which userp is set
// Attributes
bool m_didWidth : 1; // Did V3Width computation
bool m_doingWidth : 1; // Inside V3Width
bool m_protect : 1; // Protect name if protection is on
// // Space for more bools here
// This member ordering both allows 64 bit alignment and puts associated data together
VNUser m_user1u; // Contains any information the user iteration routine wants
uint32_t m_user1Cnt; // Mark of when userp was set
@ -1489,9 +1494,14 @@ public:
AstNode* firstAbovep() const { // Returns nullptr when second or later in list
return ((backp() && backp()->nextp() != this) ? backp() : nullptr);
}
bool brokeExists() const;
bool brokeExistsAbove() const;
bool brokeExistsBelow() const;
uint8_t brokenState() const { return m_brokenState; }
void brokenState(uint8_t value) { m_brokenState = value; }
// Used by AstNode::broken()
bool brokeExists() const { return V3Broken::isLinkable(this); }
bool brokeExistsAbove() const { return brokeExists() && (m_brokenState >> 7); }
bool brokeExistsBelow() const { return brokeExists() && !(m_brokenState >> 7); }
// Note: brokeExistsBelow is not quite precise, as it is true for sibling nodes as well
// CONSTRUCTORS
virtual ~AstNode() = default;
@ -1500,21 +1510,20 @@ public:
static void operator delete(void* obj, size_t size);
#endif
// CONSTANT ACCESSORS
static int instrCountBranch() { return 4; } ///< Instruction cycles to branch
static int instrCountDiv() { return 10; } ///< Instruction cycles to divide
static int instrCountDpi() { return 1000; } ///< Instruction cycles to call user function
static int instrCountLd() { return 2; } ///< Instruction cycles to load memory
static int instrCountMul() { return 3; } ///< Instruction cycles to multiply integers
static int instrCountPli() { return 20; } ///< Instruction cycles to call pli routines
static int instrCountDouble() { return 8; } ///< Instruction cycles to convert or do floats
static int instrCountDoubleDiv() { return 40; } ///< Instruction cycles to divide floats
static int instrCountDoubleTrig() { return 200; } ///< Instruction cycles to do trigonomics
static int instrCountString() { return 100; } ///< Instruction cycles to do string ops
/// Instruction cycles to call subroutine
static int instrCountCall() { return instrCountBranch() + 10; }
/// Instruction cycles to determine simulation time
static int instrCountTime() { return instrCountCall() + 5; }
// CONSTANTS
// The following are relative dynamic costs (~ execution cycle count) of various operations.
// They are used by V3InstCount to estimate the relative execution time of code fragments.
static constexpr int INSTR_COUNT_BRANCH = 4; // Branch
static constexpr int INSTR_COUNT_CALL = INSTR_COUNT_BRANCH + 10; // Subroutine call
static constexpr int INSTR_COUNT_LD = 2; // Load memory
static constexpr int INSTR_COUNT_INT_MUL = 3; // Integer multiply
static constexpr int INSTR_COUNT_INT_DIV = 10; // Integer divide
static constexpr int INSTR_COUNT_DBL = 8; // Convert or do float ops
static constexpr int INSTR_COUNT_DBL_DIV = 40; // Double divide
static constexpr int INSTR_COUNT_DBL_TRIG = 200; // Double trigonometric ops
static constexpr int INSTR_COUNT_STR = 100; // String ops
static constexpr int INSTR_COUNT_TIME = INSTR_COUNT_CALL + 5; // Determine simulation time
static constexpr int INSTR_COUNT_PLI = 20; // PLI routines
// ACCESSORS
virtual string name() const { return ""; }
@ -1545,17 +1554,17 @@ public:
void fileline(FileLine* fl) { m_fileline = fl; }
bool width1() const;
int widthInstrs() const;
void didWidth(bool flag) { m_didWidth = flag; }
bool didWidth() const { return m_didWidth; }
void didWidth(bool flag) { m_flags.didWidth = flag; }
bool didWidth() const { return m_flags.didWidth; }
bool didWidthAndSet() {
if (didWidth()) return true;
didWidth(true);
return false;
}
bool doingWidth() const { return m_doingWidth; }
void doingWidth(bool flag) { m_doingWidth = flag; }
bool protect() const { return m_protect; }
void protect(bool flag) { m_protect = flag; }
bool doingWidth() const { return m_flags.doingWidth; }
void doingWidth(bool flag) { m_flags.doingWidth = flag; }
bool protect() const { return m_flags.protect; }
void protect(bool flag) { m_flags.protect = flag; }
// TODO stomp these width functions out, and call via dtypep() instead
int width() const;
@ -1689,6 +1698,7 @@ public:
void dtypeSetSigned32() { dtypep(findSigned32DType()); }
void dtypeSetUInt32() { dtypep(findUInt32DType()); } // Twostate
void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate
void dtypeSetEmptyQueue() { dtypep(findEmptyQueueDType()); }
void dtypeSetVoid() { dtypep(findVoidDType()); }
// Data type locators
@ -1699,6 +1709,7 @@ public:
AstNodeDType* findUInt32DType() { return findBasicDType(AstBasicDTypeKwd::UINT32); }
AstNodeDType* findUInt64DType() { return findBasicDType(AstBasicDTypeKwd::UINT64); }
AstNodeDType* findCHandleDType() { return findBasicDType(AstBasicDTypeKwd::CHANDLE); }
AstNodeDType* findEmptyQueueDType() const;
AstNodeDType* findVoidDType() const;
AstNodeDType* findQueueIndexDType() const;
AstNodeDType* findBitDType(int width, int widthMin, VSigning numeric) const;
@ -1707,7 +1718,7 @@ public:
VSigning numeric) const;
AstNodeDType* findBitRangeDType(const VNumRange& range, int widthMin, VSigning numeric) const;
AstNodeDType* findBasicDType(AstBasicDTypeKwd kwd) const;
AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
static AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
// METHODS - dump and error
void v3errorEnd(std::ostringstream& str) const;
@ -1782,8 +1793,6 @@ public:
virtual bool isGateOptimizable() const { return true; }
// GateDedupable is a slightly larger superset of GateOptimzable (eg, AstNodeIf)
virtual bool isGateDedupable() const { return isGateOptimizable(); }
// Needs verilated_heavy.h (uses std::string or some others)
virtual bool isHeavy() const { return false; }
// Else creates output or exits, etc, not unconsumed
virtual bool isOutputter() const { return false; }
// Else a AstTime etc which output can't be predicted from input
@ -2086,7 +2095,7 @@ public:
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual bool sizeMattersThs() const override { return false; }
virtual int instrCount() const override { return instrCountBranch(); }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual AstNode* cloneType(AstNode* condp, AstNode* expr1p, AstNode* expr2p) = 0;
};
@ -2221,7 +2230,7 @@ public:
AstNode* incsp() const { return op3p(); } // op3 = increment statements
AstNode* bodysp() const { return op4p(); } // op4 = body of loop
virtual bool isGateOptimizable() const override { return false; }
virtual int instrCount() const override { return instrCountBranch(); }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual bool same(const AstNode* samep) const override { return true; }
};
@ -2248,7 +2257,7 @@ public:
void addElsesp(AstNode* newp) { addOp3p(newp); }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isGateDedupable() const override { return true; }
virtual int instrCount() const override { return instrCountBranch(); }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual bool same(const AstNode* samep) const override { return true; }
void branchPred(VBranchPred flag) { m_branchPred = flag; }
VBranchPred branchPred() const { return m_branchPred; }
@ -2266,7 +2275,7 @@ protected:
public:
ASTNODE_BASE_FUNCS(NodeCase)
virtual int instrCount() const override { return instrCountBranch(); }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
AstNode* exprp() const { return op1p(); } // op1 = case condition <expression>
AstCaseItem* itemsp() const {
return VN_CAST(op2p(), CaseItem);
@ -2445,6 +2454,7 @@ public:
return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I");
}
string cType(const string& name, bool forFunc, bool isRef) const;
bool isLiteralType() const; // Does this represent a C++ LiteralType? (can be constexpr)
private:
class CTypeRecursed;
@ -2621,7 +2631,6 @@ class AstNodeCCall VL_NOT_FINAL : public AstNodeStmt {
// A call of a C++ function, perhaps a AstCFunc or perhaps globally named
// Functions are not statements, while tasks are. AstNodeStmt needs isStatement() to deal.
AstCFunc* m_funcp;
string m_selfPointer; // Output code object pointer (e.g.: 'this')
string m_argTypes;
protected:
@ -2636,7 +2645,7 @@ public:
virtual void dump(std::ostream& str = std::cout) const override;
virtual void cloneRelink() override;
virtual const char* broken() const override;
virtual int instrCount() const override { return instrCountCall(); }
virtual int instrCount() const override { return INSTR_COUNT_CALL; }
virtual bool same(const AstNode* samep) const override {
const AstNodeCCall* asamep = static_cast<const AstNodeCCall*>(samep);
return (funcp() == asamep->funcp() && argTypes() == asamep->argTypes());
@ -2647,9 +2656,6 @@ public:
virtual bool isPure() const override;
virtual bool isOutputter() const override { return !isPure(); }
AstCFunc* funcp() const { return m_funcp; }
string selfPointer() const { return m_selfPointer; }
void selfPointer(const string& value) { m_selfPointer = value; }
string selfPointerProtect(bool useSelfForThis) const;
void argTypes(const string& str) { m_argTypes = str; }
string argTypes() const { return m_argTypes; }
// op1p reserved for AstCMethodCall
@ -2678,6 +2684,7 @@ private:
bool m_isHideProtected : 1; // Verilog protected
bool m_pure : 1; // DPI import pure (vs. virtual pure)
bool m_pureVirtual : 1; // Pure virtual
bool m_underGenerate : 1; // Under generate (for warning)
bool m_virtual : 1; // Virtual method in class
VLifetime m_lifetime; // Lifetime
protected:
@ -2700,6 +2707,7 @@ protected:
, m_isHideProtected{false}
, m_pure{false}
, m_pureVirtual{false}
, m_underGenerate{false}
, m_virtual{false} {
addNOp3p(stmtsp);
cname(name); // Might be overridden by dpi import/export
@ -2766,6 +2774,8 @@ public:
bool pure() const { return m_pure; }
void pureVirtual(bool flag) { m_pureVirtual = flag; }
bool pureVirtual() const { return m_pureVirtual; }
void underGenerate(bool flag) { m_underGenerate = flag; }
bool underGenerate() const { return m_underGenerate; }
void isVirtual(bool flag) { m_virtual = flag; }
bool isVirtual() const { return m_virtual; }
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
@ -2845,7 +2855,6 @@ private:
bool m_recursive : 1; // Recursive module
bool m_recursiveClone : 1; // If recursive, what module it clones, otherwise nullptr
int m_level = 0; // 1=top module, 2=cell off top module, ...
int m_varNum = 0; // Incrementing variable number
int m_typeNum = 0; // Incrementing implicit type number
VLifetime m_lifetime; // Lifetime
VTimescale m_timeunit; // Global time unit
@ -2886,7 +2895,6 @@ public:
void level(int level) { m_level = level; }
int level() const { return m_level; }
bool isTop() const { return level() == 1; }
int varNumGetInc() { return ++m_varNum; }
int typeNumGetInc() { return ++m_typeNum; }
void modPublic(bool flag) { m_modPublic = flag; }
bool modPublic() const { return m_modPublic; }

View File

@ -121,7 +121,8 @@ const char* AstNodeCCall::broken() const {
return nullptr;
}
bool AstNodeCCall::isPure() const { return funcp()->pure(); }
string AstNodeCCall::selfPointerProtect(bool useSelfForThis) const {
string AstCCall::selfPointerProtect(bool useSelfForThis) const {
const string& sp
= useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer();
return VIdProtect::protectWordsIf(sp, protect());
@ -267,7 +268,7 @@ AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) {
char* endp;
int v = strtol(literal.c_str(), &endp, 0);
if ((v != 0) && (endp[0] == 0)) { // C literal
return new AstConst(fl, AstConst::WidthedValue(), 32, v);
return new AstConst(fl, AstConst::Signed32(), v);
} else { // Try a Verilog literal (fatals if not)
return new AstConst(fl, AstConst::StringToParse(), literal.c_str());
}
@ -775,6 +776,20 @@ int AstNodeDType::widthPow2() const {
return 1;
}
bool AstNodeDType::isLiteralType() const {
if (auto* const dtypep = VN_CAST_CONST(skipRefp(), BasicDType)) {
return dtypep->keyword().isLiteralType();
} else if (auto* const dtypep = VN_CAST_CONST(skipRefp(), UnpackArrayDType)) {
return dtypep->basicp()->isLiteralType();
} else if (auto* const dtypep = VN_CAST_CONST(skipRefp(), StructDType)) {
// Currently all structs are packed, later this can be expanded to
// 'forall members _.isLiteralType()'
return dtypep->packed();
} else {
return false;
}
}
/// What is the base variable (or const) this dereferences?
AstNode* AstArraySel::baseFromp(AstNode* nodep, bool overMembers) {
// Else AstArraySel etc; search for the base
@ -917,9 +932,18 @@ void AstTypeTable::repairCache() {
}
}
AstEmptyQueueDType* AstTypeTable::findEmptyQueueDType(FileLine* fl) {
if (VL_UNLIKELY(!m_emptyQueuep)) {
AstEmptyQueueDType* newp = new AstEmptyQueueDType{fl};
addTypesp(newp);
m_emptyQueuep = newp;
}
return m_emptyQueuep;
}
AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) {
if (VL_UNLIKELY(!m_voidp)) {
AstVoidDType* newp = new AstVoidDType(fl);
AstVoidDType* newp = new AstVoidDType{fl};
addTypesp(newp);
m_voidp = newp;
}
@ -1050,8 +1074,8 @@ AstVarScope* AstConstPool::findTable(AstInitArray* initp) {
UASSERT_OBJ(VN_IS(valuep, Const), valuep, "Const pool table entry must be Const");
}
// Try to find an existing table with the same content
const uint32_t hash = V3Hasher::uncachedHash(initp).value();
const auto& er = m_tables.equal_range(hash);
const V3Hash hash = V3Hasher::uncachedHash(initp);
const auto& er = m_tables.equal_range(hash.value());
for (auto it = er.first; it != er.second; ++it) {
AstVarScope* const varScopep = it->second;
const AstInitArray* const init2p = VN_CAST(varScopep->varp()->valuep(), InitArray);
@ -1061,11 +1085,11 @@ AstVarScope* AstConstPool::findTable(AstInitArray* initp) {
}
// No such table yet, create it.
string name = "TABLE_";
name += cvtToHex(hash);
name += hash.toString();
name += "_";
name += cvtToStr(std::distance(er.first, er.second));
AstVarScope* const varScopep = createNewEntry(name, initp);
m_tables.emplace(hash, varScopep);
m_tables.emplace(hash.value(), varScopep);
return varScopep;
}
@ -1079,8 +1103,8 @@ static bool sameInit(const AstConst* ap, const AstConst* bp) {
AstVarScope* AstConstPool::findConst(AstConst* initp, bool mergeDType) {
// Try to find an existing constant with the same value
const uint32_t hash = initp->num().toHash().value();
const auto& er = m_consts.equal_range(hash);
const V3Hash hash = initp->num().toHash();
const auto& er = m_consts.equal_range(hash.value());
for (auto it = er.first; it != er.second; ++it) {
AstVarScope* const varScopep = it->second;
const AstConst* const init2p = VN_CAST(varScopep->varp()->valuep(), Const);
@ -1091,11 +1115,11 @@ AstVarScope* AstConstPool::findConst(AstConst* initp, bool mergeDType) {
}
// No such constant yet, create it.
string name = "CONST_";
name += cvtToHex(hash);
name += hash.toString();
name += "_";
name += cvtToStr(std::distance(er.first, er.second));
AstVarScope* const varScopep = createNewEntry(name, initp);
m_consts.emplace(hash, varScopep);
m_consts.emplace(hash.value(), varScopep);
return varScopep;
}
@ -1627,6 +1651,10 @@ void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "[]";
}
void AstEmptyQueueDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "emptyq";
}
void AstVoidDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "void";
@ -1827,6 +1855,7 @@ void AstCFunc::dump(std::ostream& str) const {
if (dpiExportImpl()) str << " [DPIEI]";
if (dpiImportPrototype()) str << " [DPIIP]";
if (dpiImportWrapper()) str << " [DPIIW]";
if (dpiContext()) str << " [DPICTX]";
if (isConstructor()) str << " [CTOR]";
if (isDestructor()) str << " [DTOR]";
if (isVirtual()) str << " [VIRT]";

View File

@ -183,6 +183,17 @@ public:
static AstConst* parseParamLiteral(FileLine* fl, const string& literal);
};
class AstEmptyQueue final : public AstNodeMath {
public:
AstEmptyQueue(FileLine* fl)
: ASTGEN_SUPER_EmptyQueue(fl) {}
ASTNODE_NODE_FUNCS(EmptyQueue)
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
virtual string emitVerilog() override { return "{}"; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool cleanOut() const override { return true; }
};
class AstRange final : public AstNodeRange {
// Range specification, for use under variables and cells
public:
@ -307,7 +318,6 @@ public:
: ASTGEN_SUPER_Class(fl, name) {}
ASTNODE_NODE_FUNCS(Class)
virtual string verilogKwd() const override { return "class"; }
virtual bool isHeavy() const override { return true; }
virtual bool maybePointedTo() const override { return true; }
virtual void dump(std::ostream& str) const override;
virtual const char* broken() const override {
@ -556,7 +566,6 @@ public:
virtual void dumpSmall(std::ostream& str) const override;
virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); }
virtual AstNodeDType* getChild2DTypep() const override { return keyChildDTypep(); }
virtual bool isHeavy() const override { return true; }
// op1 = Range of variable
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); }
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
@ -653,7 +662,6 @@ public:
}
virtual string prettyDTypeName() const override;
virtual void dumpSmall(std::ostream& str) const override;
virtual bool isHeavy() const override { return true; }
virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); }
// op1 = Range of variable
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); }
@ -881,7 +889,6 @@ public:
BROKEN_RTN(dtypep() != this);
return nullptr;
}
virtual bool isHeavy() const override { return keyword() == AstBasicDTypeKwd::STRING; }
AstRange* rangep() const { return VN_CAST(op1p(), Range); } // op1 = Range of variable
void rangep(AstRange* nodep) { setNOp1p(nodep); }
void setSignedState(const VSigning& signst) {
@ -1141,7 +1148,6 @@ public:
}
virtual void dumpSmall(std::ostream& str) const override;
virtual string prettyDTypeName() const override;
virtual bool isHeavy() const override { return true; }
virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); }
// op1 = Range of variable
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); }
@ -1359,6 +1365,33 @@ public:
}
};
class AstEmptyQueueDType final : public AstNodeDType {
// For EmptyQueue
public:
explicit AstEmptyQueueDType(FileLine* fl)
: ASTGEN_SUPER_EmptyQueueDType(fl) {
dtypep(this);
}
ASTNODE_NODE_FUNCS(EmptyQueueDType)
virtual void dumpSmall(std::ostream& str) const override;
virtual bool hasDType() const override { return true; }
virtual bool maybePointedTo() const override { return true; }
virtual AstNodeDType* subDTypep() const override { return nullptr; }
virtual AstNodeDType* virtRefDTypep() const override { return nullptr; }
virtual void virtRefDTypep(AstNodeDType* nodep) override {}
virtual bool similarDType(AstNodeDType* samep) const override { return this == samep; }
virtual AstBasicDType* basicp() const override { return nullptr; }
// cppcheck-suppress csyleCast
virtual AstNodeDType* skipRefp() const override { return (AstNodeDType*)this; }
// cppcheck-suppress csyleCast
virtual AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
// cppcheck-suppress csyleCast
virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
virtual int widthAlignBytes() const override { return 1; }
virtual int widthTotalBytes() const override { return 1; }
virtual bool isCompound() const override { return false; }
};
class AstVoidDType final : public AstNodeDType {
// For e.g. a function returning void
public:
@ -2405,7 +2438,7 @@ public:
}
}
virtual int instrCount() const override {
return widthInstrs() * (access().isReadOrRW() ? instrCountLd() : 1);
return widthInstrs() * (access().isReadOrRW() ? INSTR_COUNT_LD : 1);
}
virtual string emitVerilog() override { V3ERROR_NA_RETURN(""); }
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
@ -3484,6 +3517,17 @@ public:
virtual bool brokeLhsMustBeLvalue() const override { return true; }
};
class AstDpiExportUpdated final : public AstNodeStmt {
// Denotes that the referenced variable may have been updated via a DPI Export
public:
AstDpiExportUpdated(FileLine* fl, AstVarScope* varScopep)
: ASTGEN_SUPER_DpiExportUpdated(fl) {
addOp1p(new AstVarRef{fl, varScopep, VAccess::WRITE});
}
ASTNODE_NODE_FUNCS(DpiExportUpdated)
AstVarScope* varScopep() const { return VN_CAST(op1p(), VarRef)->varScopep(); }
};
class AstExprStmt final : public AstNodeMath {
// Perform a statement, often assignment inside an expression/math node,
// the parent gets passed the 'resultp()'.
@ -3589,7 +3633,7 @@ public:
if (m_dataDeclp && m_dataDeclp->clonep()) m_dataDeclp = m_dataDeclp->clonep();
}
virtual void dump(std::ostream& str) const override;
virtual int instrCount() const override { return 1 + 2 * instrCountLd(); }
virtual int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; }
virtual bool maybePointedTo() const override { return true; }
void binNum(int flag) { m_binNum = flag; }
int binNum() const { return m_binNum; }
@ -3632,7 +3676,7 @@ public:
if (m_declp->clonep()) m_declp = m_declp->clonep();
}
virtual void dump(std::ostream& str) const override;
virtual int instrCount() const override { return 1 + 2 * instrCountLd(); }
virtual int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; }
virtual bool same(const AstNode* samep) const override {
return declp() == static_cast<const AstCoverInc*>(samep)->declp();
}
@ -3655,7 +3699,7 @@ public:
setOp3p(changep);
}
ASTNODE_NODE_FUNCS(CoverToggle)
virtual int instrCount() const override { return 3 + instrCountBranch() + instrCountLd(); }
virtual int instrCount() const override { return 3 + INSTR_COUNT_BRANCH + INSTR_COUNT_LD; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return true; }
@ -3748,7 +3792,7 @@ public:
addNOp2p(bodysp);
}
ASTNODE_NODE_FUNCS(CaseItem)
virtual int instrCount() const override { return widthInstrs() + instrCountBranch(); }
virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
AstNode* condsp() const { return op1p(); } // op1 = list of possible matching expressions
AstNode* bodysp() const { return op2p(); } // op2 = what to do
void condsp(AstNode* nodep) { setOp1p(nodep); }
@ -3792,7 +3836,7 @@ public:
}
ASTNODE_NODE_FUNCS(SFormatF)
virtual string name() const override { return m_text; }
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
virtual bool hasDType() const override { return true; }
virtual bool same(const AstNode* samep) const override {
return text() == static_cast<const AstSFormatF*>(samep)->text();
@ -3857,7 +3901,7 @@ public:
virtual bool same(const AstNode* samep) const override {
return displayType() == static_cast<const AstDisplay*>(samep)->displayType();
}
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
AstDisplayType displayType() const { return m_displayType; }
void displayType(AstDisplayType type) { m_displayType = type; }
// * = Add a newline for $display
@ -3882,7 +3926,6 @@ public:
ASTNODE_NODE_FUNCS(DumpCtl)
virtual string verilogKwd() const override { return ctlType().ascii(); }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isHeavy() const override { return true; }
virtual bool isPredictOptimizable() const override { return false; }
virtual bool isOutputter() const override { return true; }
virtual bool cleanOut() const { return true; }
@ -3922,7 +3965,7 @@ public:
virtual bool same(const AstNode* samep) const override {
return displayType() == static_cast<const AstElabDisplay*>(samep)->displayType();
}
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
AstDisplayType displayType() const { return m_displayType; }
void displayType(AstDisplayType type) { m_displayType = type; }
void fmtp(AstSFormatF* nodep) { addOp1p(nodep); } // op1 = To-String formatter
@ -3956,7 +3999,7 @@ public:
virtual bool isPure() const override { return true; }
virtual bool isOutputter() const override { return false; }
virtual bool cleanOut() const { return false; }
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
virtual bool same(const AstNode* samep) const override { return true; }
void fmtp(AstSFormatF* nodep) { addOp1p(nodep); } // op1 = To-String formatter
AstSFormatF* fmtp() const { return VN_CAST(op1p(), SFormatF); }
@ -4001,7 +4044,7 @@ public:
} // Though deleted before opt
virtual bool isPure() const override { return false; } // Though deleted before opt
virtual bool isOutputter() const override { return true; } // Though deleted before opt
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
AstNode* exprsp() const { return op1p(); } // op1 = Expressions to output
void exprsp(AstNode* nodep) { addOp1p(nodep); } // op1 = Expressions to output
};
@ -4038,7 +4081,6 @@ public:
ASTNODE_NODE_FUNCS(FOpen)
virtual string verilogKwd() const override { return "$fopen"; }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isHeavy() const override { return true; }
virtual bool isPredictOptimizable() const override { return false; }
virtual bool isPure() const override { return false; }
virtual bool isOutputter() const override { return true; }
@ -4060,7 +4102,6 @@ public:
ASTNODE_NODE_FUNCS(FOpenMcd)
virtual string verilogKwd() const override { return "$fopen"; }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isHeavy() const override { return true; }
virtual bool isPredictOptimizable() const override { return false; }
virtual bool isPure() const override { return false; }
virtual bool isOutputter() const override { return true; }
@ -4284,7 +4325,6 @@ public:
setNOp4p(msbp);
}
virtual bool isGateOptimizable() const override { return false; }
virtual bool isHeavy() const override { return true; }
virtual bool isPredictOptimizable() const override { return false; }
virtual bool isPure() const override { return false; }
virtual bool isOutputter() const override { return true; }
@ -4335,7 +4375,7 @@ public:
} // Though deleted before opt
virtual bool isPure() const override { return false; } // Though deleted before opt
virtual bool isOutputter() const override { return true; } // Though deleted before opt
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
virtual bool same(const AstNode* samep) const override {
return m_off == static_cast<const AstMonitorOff*>(samep)->m_off;
}
@ -4395,7 +4435,6 @@ public:
virtual string emitVerilog() override { return "%f$value$plusargs(%l, %k%r)"; }
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isHeavy() const override { return true; }
virtual bool isPredictOptimizable() const override { return false; }
virtual bool cleanOut() const override { return true; }
virtual bool same(const AstNode* samep) const override { return true; }
@ -4448,7 +4487,7 @@ public:
AstNode* arrayp() const { return op1p(); } // op1 = array and index vars
AstNode* bodysp() const { return op4p(); } // op4 = body of loop
virtual bool isGateOptimizable() const override { return false; }
virtual int instrCount() const override { return instrCountBranch(); }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual bool same(const AstNode* samep) const override { return true; }
};
@ -4465,7 +4504,7 @@ public:
virtual bool isGateOptimizable() const override {
return false;
} // Not relevant - converted to FOR
virtual int instrCount() const override { return instrCountBranch(); }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual bool same(const AstNode* samep) const override { return true; }
};
@ -4498,7 +4537,7 @@ public:
void addBodysp(AstNode* newp) { addOp3p(newp); }
void addIncsp(AstNode* newp) { addOp4p(newp); }
virtual bool isGateOptimizable() const override { return false; }
virtual int instrCount() const override { return instrCountBranch(); }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual bool same(const AstNode* samep) const override { return true; }
// Stop statement searchback here
virtual void addBeforeStmt(AstNode* newp, AstNode* belowp) override;
@ -4679,7 +4718,7 @@ public:
if (m_labelp->clonep()) m_labelp = m_labelp->clonep();
}
virtual void dump(std::ostream& str) const override;
virtual int instrCount() const override { return instrCountBranch(); }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual bool same(const AstNode* samep) const override {
return labelp() == static_cast<const AstJumpGo*>(samep)->labelp();
}
@ -4736,7 +4775,7 @@ public:
bool isClockReq() const { return m_clockReq; }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
virtual int instrCount() const override { return widthInstrs(); }
virtual int instrCount() const override { return widthInstrs() * 2; } // xor, or/logor
virtual bool same(const AstNode* samep) const override { return true; }
};
@ -5075,7 +5114,7 @@ public:
virtual bool isPredictOptimizable() const override { return false; }
virtual bool isPure() const override { return false; }
virtual bool isOutputter() const override { return true; }
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
VTimescale timeunit() const { return m_timeunit; }
};
@ -5174,7 +5213,7 @@ public:
virtual bool isPredictOptimizable() const override { return false; }
virtual bool isPure() const override { return false; }
virtual bool isOutputter() const override { return true; }
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
AstNode* unitsp() const { return op1p(); }
AstNode* precisionp() const { return op2p(); }
AstNode* suffixp() const { return op3p(); }
@ -5244,11 +5283,14 @@ class AstTraceInc final : public AstNodeStmt {
private:
AstTraceDecl* m_declp; // Pointer to declaration
const bool m_full; // Is this a full vs incremental dump
const uint32_t m_baseCode; // Trace code base value in function containing this AstTraceInc
public:
AstTraceInc(FileLine* fl, AstTraceDecl* declp, bool full)
AstTraceInc(FileLine* fl, AstTraceDecl* declp, bool full, uint32_t baseCode = 0)
: ASTGEN_SUPER_TraceInc(fl)
, m_declp{declp}
, m_full{full} {
, m_full{full}
, m_baseCode{baseCode} {
dtypeFrom(declp);
addOp2p(declp->valuep()->cloneTree(true));
}
@ -5261,7 +5303,7 @@ public:
if (m_declp->clonep()) m_declp = m_declp->clonep();
}
virtual void dump(std::ostream& str) const override;
virtual int instrCount() const override { return 10 + 2 * instrCountLd(); }
virtual int instrCount() const override { return 10 + 2 * INSTR_COUNT_LD; }
virtual bool hasDType() const override { return true; }
virtual bool same(const AstNode* samep) const override {
return declp() == static_cast<const AstTraceInc*>(samep)->declp();
@ -5276,6 +5318,7 @@ public:
AstNode* valuep() const { return op2p(); }
AstTraceDecl* declp() const { return m_declp; }
bool full() const { return m_full; }
uint32_t baseCode() const { return m_baseCode; }
};
class AstActive final : public AstNode {
@ -5438,7 +5481,7 @@ public:
virtual bool cleanOut() const override { return true; }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
virtual bool same(const AstNode* samep) const override { return true; }
AstNode* seedp() const { return op1p(); }
bool reset() const { return m_reset; }
@ -5468,7 +5511,7 @@ public:
virtual bool sizeMattersRhs() const override { return false; }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
};
class AstTime final : public AstNodeTermop {
@ -5485,7 +5528,7 @@ public:
virtual bool cleanOut() const override { return true; }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
virtual int instrCount() const override { return instrCountTime(); }
virtual int instrCount() const override { return INSTR_COUNT_TIME; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual void dump(std::ostream& str = std::cout) const override;
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
@ -5506,7 +5549,7 @@ public:
virtual bool cleanOut() const override { return true; }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
virtual int instrCount() const override { return instrCountTime(); }
virtual int instrCount() const override { return INSTR_COUNT_TIME; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual void dump(std::ostream& str = std::cout) const override;
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
@ -5531,7 +5574,7 @@ public:
virtual bool isGateOptimizable() const override { return false; }
virtual bool isSubstOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
virtual int instrCount() const override { return instrCountPli(); }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
virtual bool same(const AstNode* samep) const override { return true; }
};
@ -5567,7 +5610,7 @@ public:
virtual bool cleanOut() const override { return true; }
virtual bool cleanLhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
virtual bool doubleFlavor() const override { return true; }
};
class AstRedAnd final : public AstNodeUniop {
@ -5760,7 +5803,7 @@ public:
virtual bool cleanOut() const override { return false; }
virtual bool cleanLhs() const override { return false; } // Eliminated before matters
virtual bool sizeMattersLhs() const override { return false; } // Eliminated before matters
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
};
class AstRToIRoundS final : public AstNodeUniop {
// Convert real to integer, with arbitrary sized output (not just "integer" format)
@ -5778,7 +5821,7 @@ public:
virtual bool cleanOut() const override { return false; }
virtual bool cleanLhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
};
class AstIToRD final : public AstNodeUniop {
// $itor where lhs is unsigned
@ -5794,7 +5837,7 @@ public:
virtual bool cleanOut() const override { return false; }
virtual bool cleanLhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
};
class AstISToRD final : public AstNodeUniop {
// $itor where lhs is signed
@ -5811,7 +5854,7 @@ public:
virtual bool cleanOut() const override { return false; }
virtual bool cleanLhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
};
class AstRealToBits final : public AstNodeUniop {
public:
@ -5828,7 +5871,7 @@ public:
virtual bool cleanOut() const override { return false; }
virtual bool cleanLhs() const override { return false; } // Eliminated before matters
virtual bool sizeMattersLhs() const override { return false; } // Eliminated before matters
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
};
class AstBitsToRealD final : public AstNodeUniop {
public:
@ -5845,7 +5888,7 @@ public:
virtual bool cleanOut() const override { return false; }
virtual bool cleanLhs() const override { return false; } // Eliminated before matters
virtual bool sizeMattersLhs() const override { return false; } // Eliminated before matters
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
};
class AstCLog2 final : public AstNodeUniop {
@ -6219,7 +6262,7 @@ public:
virtual bool cleanOut() const override { return true; }
virtual bool cleanLhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual int instrCount() const override { return instrCountDoubleTrig(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL_TRIG; }
virtual bool doubleFlavor() const override { return true; }
};
@ -6530,7 +6573,6 @@ public:
virtual bool cleanOut() const override { return true; }
virtual bool cleanLhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool isHeavy() const override { return true; }
FmtType format() const { return m_fmt; }
};
@ -6558,7 +6600,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return widthInstrs() + instrCountBranch(); }
virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
};
class AstLogAnd final : public AstNodeBiop {
public:
@ -6581,7 +6623,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return widthInstrs() + instrCountBranch(); }
virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
};
class AstLogEq final : public AstNodeBiCom {
public:
@ -6604,7 +6646,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return widthInstrs() + instrCountBranch(); }
virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
};
class AstLogIf final : public AstNodeBiop {
public:
@ -6627,7 +6669,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return widthInstrs() + instrCountBranch(); }
virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
};
class AstOr final : public AstNodeBiComAsv {
public:
@ -6740,7 +6782,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
virtual bool doubleFlavor() const override { return true; }
};
class AstEqN final : public AstNodeBiCom {
@ -6764,7 +6806,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountString(); }
virtual int instrCount() const override { return INSTR_COUNT_STR; }
virtual bool stringFlavor() const override { return true; }
};
class AstNeq final : public AstNodeBiCom {
@ -6810,7 +6852,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
virtual bool doubleFlavor() const override { return true; }
};
class AstNeqN final : public AstNodeBiCom {
@ -6834,7 +6876,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountString(); }
virtual int instrCount() const override { return INSTR_COUNT_STR; }
virtual bool stringFlavor() const override { return true; }
};
class AstLt final : public AstNodeBiop {
@ -6880,7 +6922,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
virtual bool doubleFlavor() const override { return true; }
};
class AstLtS final : public AstNodeBiop {
@ -6927,7 +6969,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountString(); }
virtual int instrCount() const override { return INSTR_COUNT_STR; }
virtual bool stringFlavor() const override { return true; }
};
class AstGt final : public AstNodeBiop {
@ -6973,7 +7015,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
virtual bool doubleFlavor() const override { return true; }
};
class AstGtS final : public AstNodeBiop {
@ -7020,7 +7062,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountString(); }
virtual int instrCount() const override { return INSTR_COUNT_STR; }
virtual bool stringFlavor() const override { return true; }
};
class AstGte final : public AstNodeBiop {
@ -7068,7 +7110,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
virtual bool doubleFlavor() const override { return true; }
};
class AstGteS final : public AstNodeBiop {
@ -7115,7 +7157,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountString(); }
virtual int instrCount() const override { return INSTR_COUNT_STR; }
virtual bool stringFlavor() const override { return true; }
};
class AstLte final : public AstNodeBiop {
@ -7163,7 +7205,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
virtual bool doubleFlavor() const override { return true; }
};
class AstLteS final : public AstNodeBiop {
@ -7210,7 +7252,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountString(); }
virtual int instrCount() const override { return INSTR_COUNT_STR; }
virtual bool stringFlavor() const override { return true; }
};
class AstShiftL final : public AstNodeBiop {
@ -7331,7 +7373,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
virtual bool doubleFlavor() const override { return true; }
};
class AstSub final : public AstNodeBiop {
@ -7377,7 +7419,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
virtual bool doubleFlavor() const override { return true; }
};
class AstMul final : public AstNodeBiComAsv {
@ -7401,7 +7443,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return true; }
virtual int instrCount() const override { return widthInstrs() * instrCountMul(); }
virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL; }
};
class AstMulD final : public AstNodeBiComAsv {
public:
@ -7424,7 +7466,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return true; }
virtual int instrCount() const override { return instrCountDouble(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL; }
virtual bool doubleFlavor() const override { return true; }
};
class AstMulS final : public AstNodeBiComAsv {
@ -7449,7 +7491,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return true; }
virtual int instrCount() const override { return widthInstrs() * instrCountMul(); }
virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL; }
virtual bool signedFlavor() const override { return true; }
};
class AstDiv final : public AstNodeBiop {
@ -7472,7 +7514,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return true; }
virtual int instrCount() const override { return widthInstrs() * instrCountDiv(); }
virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_DIV; }
};
class AstDivD final : public AstNodeBiop {
public:
@ -7495,7 +7537,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDoubleDiv(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL_DIV; }
virtual bool doubleFlavor() const override { return true; }
};
class AstDivS final : public AstNodeBiop {
@ -7518,7 +7560,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return true; }
virtual int instrCount() const override { return widthInstrs() * instrCountDiv(); }
virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_DIV; }
virtual bool signedFlavor() const override { return true; }
};
class AstModDiv final : public AstNodeBiop {
@ -7541,7 +7583,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return true; }
virtual int instrCount() const override { return widthInstrs() * instrCountDiv(); }
virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_DIV; }
};
class AstModDivS final : public AstNodeBiop {
public:
@ -7563,7 +7605,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return true; }
virtual int instrCount() const override { return widthInstrs() * instrCountDiv(); }
virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_DIV; }
virtual bool signedFlavor() const override { return true; }
};
class AstPow final : public AstNodeBiop {
@ -7587,7 +7629,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return widthInstrs() * instrCountMul() * 10; }
virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL * 10; }
};
class AstPowD final : public AstNodeBiop {
public:
@ -7609,7 +7651,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDoubleDiv() * 5; }
virtual int instrCount() const override { return INSTR_COUNT_DBL_DIV * 5; }
virtual bool doubleFlavor() const override { return true; }
};
class AstPowSU final : public AstNodeBiop {
@ -7635,7 +7677,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return widthInstrs() * instrCountMul() * 10; }
virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL * 10; }
virtual bool signedFlavor() const override { return true; }
};
class AstPowSS final : public AstNodeBiop {
@ -7661,7 +7703,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return widthInstrs() * instrCountMul() * 10; }
virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL * 10; }
virtual bool signedFlavor() const override { return true; }
};
class AstPowUS final : public AstNodeBiop {
@ -7687,7 +7729,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return true; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return widthInstrs() * instrCountMul() * 10; }
virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL * 10; }
virtual bool signedFlavor() const override { return true; }
};
class AstPreAdd final : public AstNodeTriop {
@ -7928,7 +7970,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountString(); }
virtual int instrCount() const override { return INSTR_COUNT_STR; }
virtual bool stringFlavor() const override { return true; }
};
class AstReplicate final : public AstNodeBiop {
@ -8091,7 +8133,7 @@ public:
virtual bool cleanRhs() const override { return false; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual int instrCount() const override { return instrCountDoubleTrig(); }
virtual int instrCount() const override { return INSTR_COUNT_DBL_TRIG; }
virtual bool doubleFlavor() const override { return true; }
};
@ -8148,7 +8190,6 @@ public:
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual bool sizeMattersThs() const override { return false; }
virtual bool isHeavy() const override { return true; }
};
class AstGetcN final : public AstNodeBiop {
@ -8174,7 +8215,6 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual bool isHeavy() const override { return true; }
};
class AstGetcRefN final : public AstNodeBiop {
@ -8200,7 +8240,6 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual bool isHeavy() const override { return true; }
};
class AstSubstrN final : public AstNodeTriop {
@ -8226,7 +8265,6 @@ public:
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual bool sizeMattersThs() const override { return false; }
virtual bool isHeavy() const override { return true; }
};
class AstCompareNN final : public AstNodeBiop {
@ -8259,7 +8297,6 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual bool isHeavy() const override { return true; }
};
class AstFell final : public AstNodeMath {
@ -8493,9 +8530,9 @@ private:
public:
AstNodeCoverOrAssert(AstType t, FileLine* fl, AstNode* propp, AstNode* passsp, bool immediate,
const string& name = "")
: AstNodeStmt(t, fl)
, m_immediate(immediate)
, m_name(name) {
: AstNodeStmt{t, fl}
, m_immediate{immediate}
, m_name{name} {
addOp1p(propp);
addNOp4p(passsp);
}
@ -8728,7 +8765,6 @@ class AstCFunc final : public AstNode {
// Parents: MODULE/SCOPE
// Children: VAR/statements
private:
AstCFuncType m_funcType;
AstScope* m_scopep;
string m_name;
string m_cname; // C name, for dpiExports
@ -8738,6 +8774,7 @@ private:
string m_ifdef; // #ifdef symbol around this function
VBoolOrUnknown m_isConst; // Function is declared const (*this not changed)
bool m_isStatic : 1; // Function is static (no need for a 'this' pointer)
bool m_isTrace : 1; // Function is related to tracing
bool m_dontCombine : 1; // V3Combine shouldn't compare this func tree, it's special
bool m_declPrivate : 1; // Declare it private
bool m_formCallTree : 1; // Make a global function to call entire tree of functions
@ -8756,15 +8793,16 @@ private:
bool m_dpiExportImpl : 1; // DPI export implementation (called from DPI dispatcher via lookup)
bool m_dpiImportPrototype : 1; // This is the DPI import prototype (i.e.: provided by user)
bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code
bool m_dpiContext : 1; // Declared as 'context' DPI import/export function
public:
AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "")
: ASTGEN_SUPER_CFunc(fl) {
m_funcType = AstCFuncType::FT_NORMAL;
m_isConst = VBoolOrUnknown::BU_UNKNOWN; // Unknown until analyzed
m_scopep = scopep;
m_name = name;
m_rtnType = rtnType;
m_isStatic = false;
m_isTrace = false;
m_dontCombine = false;
m_declPrivate = false;
m_formCallTree = false;
@ -8782,6 +8820,7 @@ public:
m_dpiExportImpl = false;
m_dpiImportPrototype = false;
m_dpiImportWrapper = false;
m_dpiContext = false;
}
ASTNODE_NODE_FUNCS(CFunc)
virtual string name() const override { return m_name; }
@ -8793,25 +8832,29 @@ public:
virtual void dump(std::ostream& str = std::cout) const override;
virtual bool same(const AstNode* samep) const override {
const AstCFunc* asamep = static_cast<const AstCFunc*>(samep);
return ((funcType() == asamep->funcType()) && (rtnTypeVoid() == asamep->rtnTypeVoid())
return ((isTrace() == asamep->isTrace()) && (rtnTypeVoid() == asamep->rtnTypeVoid())
&& (argTypes() == asamep->argTypes()) && (ctorInits() == asamep->ctorInits())
&& isLoose() == asamep->isLoose()
&& (!(dpiImportPrototype() || dpiExportImpl()) || name() == asamep->name()));
}
//
virtual void name(const string& name) override { m_name = name; }
virtual int instrCount() const override { return dpiImportPrototype() ? instrCountDpi() : 0; }
virtual int instrCount() const override {
return dpiImportPrototype() ? v3Global.opt.instrCountDpi() : 0;
}
VBoolOrUnknown isConst() const { return m_isConst; }
void isConst(bool flag) { m_isConst.setTrueOrFalse(flag); }
void isConst(VBoolOrUnknown flag) { m_isConst = flag; }
bool isStatic() const { return m_isStatic; }
void isStatic(bool flag) { m_isStatic = flag; }
bool isTrace() const { return m_isTrace; }
void isTrace(bool flag) { m_isTrace = flag; }
void cname(const string& name) { m_cname = name; }
string cname() const { return m_cname; }
AstScope* scopep() const { return m_scopep; }
void scopep(AstScope* nodep) { m_scopep = nodep; }
string rtnTypeVoid() const { return ((m_rtnType == "") ? "void" : m_rtnType); }
bool dontCombine() const { return m_dontCombine || funcType() != AstCFuncType::FT_NORMAL; }
bool dontCombine() const { return m_dontCombine || isTrace(); }
void dontCombine(bool flag) { m_dontCombine = flag; }
bool dontInline() const { return dontCombine() || slow() || funcPublic(); }
bool declPrivate() const { return m_declPrivate; }
@ -8828,8 +8871,6 @@ public:
string ctorInits() const { return m_ctorInits; }
void ifdef(const string& str) { m_ifdef = str; }
string ifdef() const { return m_ifdef; }
void funcType(AstCFuncType flag) { m_funcType = flag; }
AstCFuncType funcType() const { return m_funcType; }
bool isConstructor() const { return m_isConstructor; }
void isConstructor(bool flag) { m_isConstructor = flag; }
bool isDestructor() const { return m_isDestructor; }
@ -8855,6 +8896,8 @@ public:
void dpiImportPrototype(bool flag) { m_dpiImportPrototype = flag; }
bool dpiImportWrapper() const { return m_dpiImportWrapper; }
void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; }
bool dpiContext() const { return m_dpiContext; }
void dpiContext(bool flag) { m_dpiContext = flag; }
//
// If adding node accessors, see below emptyBody
AstNode* argsp() const { return op1p(); }
@ -8876,10 +8919,17 @@ class AstCCall final : public AstNodeCCall {
// C++ function call
// Parents: Anything above a statement
// Children: Args to the function
string m_selfPointer; // Output code object pointer (e.g.: 'this')
public:
AstCCall(FileLine* fl, AstCFunc* funcp, AstNode* argsp = nullptr)
: ASTGEN_SUPER_CCall(fl, funcp, argsp) {}
ASTNODE_NODE_FUNCS(CCall)
string selfPointer() const { return m_selfPointer; }
void selfPointer(const string& value) { m_selfPointer = value; }
string selfPointerProtect(bool useSelfForThis) const;
};
class AstCMethodCall final : public AstNodeCCall {
@ -9002,8 +9052,8 @@ class AstCUse final : public AstNode {
// C++ use of a class or #include; indicates need of forward declaration
// Parents: NODEMODULE
private:
VUseType m_useType; // What sort of use this is
string m_name;
const VUseType m_useType; // What sort of use this is
const string m_name;
public:
AstCUse(FileLine* fl, VUseType useType, const string& name)
@ -9011,10 +9061,9 @@ public:
, m_useType{useType}
, m_name{name} {}
ASTNODE_NODE_FUNCS(CUse)
virtual string name() const override { return m_name; }
virtual void dump(std::ostream& str = std::cout) const override;
virtual string name() const override { return m_name; }
VUseType useType() const { return m_useType; }
void useType(VUseType useType) { m_useType = useType; }
};
class AstMTaskBody final : public AstNode {
@ -9077,8 +9126,9 @@ public:
class AstTypeTable final : public AstNode {
// Container for hash of standard data types
// Children: NODEDTYPEs
AstVoidDType* m_voidp = nullptr;
AstEmptyQueueDType* m_emptyQueuep = nullptr;
AstQueueDType* m_queueIndexp = nullptr;
AstVoidDType* m_voidp = nullptr;
AstBasicDType* m_basicps[AstBasicDTypeKwd::_ENUM_MAX];
//
using DetailedMap = std::map<VBasicTypeKey, AstBasicDType*>;
@ -9089,14 +9139,15 @@ public:
ASTNODE_NODE_FUNCS(TypeTable)
AstNodeDType* typesp() const { return VN_CAST(op1p(), NodeDType); } // op1 = List of dtypes
void addTypesp(AstNodeDType* nodep) { addOp1p(nodep); }
AstVoidDType* findVoidDType(FileLine* fl);
AstQueueDType* findQueueIndexDType(FileLine* fl);
AstBasicDType* findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd);
AstBasicDType* findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd, int width, int widthMin,
VSigning numeric);
AstBasicDType* findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd, const VNumRange& range,
int widthMin, VSigning numeric);
AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
AstEmptyQueueDType* findEmptyQueueDType(FileLine* fl);
AstQueueDType* findQueueIndexDType(FileLine* fl);
AstVoidDType* findVoidDType(FileLine* fl);
void clearCache();
void repairCache();
virtual void dump(std::ostream& str = std::cout) const override;
@ -9143,8 +9194,10 @@ private:
AstPackage* m_dollarUnitPkgp = nullptr; // $unit
AstCFunc* m_evalp = nullptr; // The '_eval' function
AstExecGraph* m_execGraphp = nullptr; // Execution MTask graph for threads>1 mode
AstVarScope* m_dpiExportTriggerp = nullptr; // The DPI export trigger variable
VTimescale m_timeunit; // Global time unit
VTimescale m_timeprecision; // Global time precision
bool m_changeRequest = false; // Have _change_request method
bool m_timescaleSpecified = false; // Input HDL specified timescale
public:
AstNetlist();
@ -9152,6 +9205,7 @@ public:
virtual const char* broken() const override {
BROKEN_RTN(m_dollarUnitPkgp && !m_dollarUnitPkgp->brokeExists());
BROKEN_RTN(m_evalp && !m_evalp->brokeExists());
BROKEN_RTN(m_dpiExportTriggerp && !m_dpiExportTriggerp->brokeExists());
return nullptr;
}
virtual string name() const override { return "$root"; }
@ -9168,6 +9222,8 @@ public:
AstNode* miscsp() const { return op3p(); } // op3 = List of dtypes etc
void addMiscsp(AstNode* nodep) { addOp3p(nodep); }
AstTypeTable* typeTablep() { return m_typeTablep; }
void changeRequest(bool specified) { m_changeRequest = specified; }
bool changeRequest() const { return m_changeRequest; }
AstConstPool* constPoolp() { return m_constPoolp; }
AstPackage* dollarUnitPkgp() const { return m_dollarUnitPkgp; }
AstPackage* dollarUnitPkgAddp() {
@ -9185,6 +9241,8 @@ public:
void evalp(AstCFunc* evalp) { m_evalp = evalp; }
AstExecGraph* execGraphp() const { return m_execGraphp; }
void execGraphp(AstExecGraph* graphp) { m_execGraphp = graphp; }
AstVarScope* dpiExportTriggerp() const { return m_dpiExportTriggerp; }
void dpiExportTriggerp(AstVarScope* varScopep) { m_dpiExportTriggerp = varScopep; }
VTimescale timeunit() const { return m_timeunit; }
void timeunit(const VTimescale& value) { m_timeunit = value; }
VTimescale timeprecision() const { return m_timeprecision; }

View File

@ -33,7 +33,7 @@ template <class T_Node, class T_Data, int T_UserN> class AstUserAllocatorBase VL
private:
std::vector<T_Data*> m_allocated;
inline T_Data* getUserp(T_Node* nodep) const {
inline T_Data* getUserp(const T_Node* nodep) const {
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
if (T_UserN == 1) {
const VNUser user = nodep->user1u();
@ -100,6 +100,13 @@ public:
}
return *userp;
}
// Get a reference to the user data
T_Data& operator()(const T_Node* nodep) {
T_Data* userp = getUserp(nodep);
UASSERT_OBJ(userp, nodep, "Missing User data on const AstNode");
return *userp;
}
};
// User pointer allocator classes. T_Node is the type of node the allocator should be applied to

View File

@ -62,6 +62,7 @@ private:
BeginState* m_statep; // Current global state
AstNodeModule* m_modp = nullptr; // Current module
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
AstNode* m_liftedp = nullptr; // Local nodes we are lifting into m_ftaskp
string m_namedScope; // Name of begin blocks above us
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
int m_ifDepth = 0; // Current if depth
@ -74,6 +75,21 @@ private:
return a + "__DOT__" + b;
}
void liftNode(AstNode* nodep) {
nodep->unlinkFrBack();
if (m_ftaskp) {
// AstBegin under ftask, just move into the ftask
if (!m_liftedp) {
m_liftedp = nodep;
} else {
m_liftedp->addNext(nodep);
}
} else {
// Move to module
m_modp->addStmtp(nodep);
}
}
// VISITORS
virtual void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
@ -101,7 +117,17 @@ private:
m_namedScope = "";
m_unnamedScope = "";
m_ftaskp = nodep;
m_liftedp = nullptr;
iterateChildren(nodep);
if (m_liftedp) {
// Place lifted nodes at beginning of stmtsp, so Var nodes appear before referenced
if (AstNode* const stmtsp = nodep->stmtsp()) {
stmtsp->unlinkFrBackWithNext();
m_liftedp->addNext(stmtsp);
}
nodep->addStmtsp(m_liftedp);
m_liftedp = nullptr;
}
m_ftaskp = nullptr;
}
}
@ -157,13 +183,8 @@ private:
// Rename it
nodep->name(dot(m_unnamedScope, nodep->name()));
m_statep->userMarkChanged(nodep);
// Move to module
nodep->unlinkFrBack();
if (m_ftaskp) {
m_ftaskp->addStmtsp(nodep); // Begins under funcs just move into the func
} else {
m_modp->addStmtp(nodep);
}
// Move it under enclosing tree
liftNode(nodep);
}
}
virtual void visit(AstTypedef* nodep) override {
@ -171,14 +192,8 @@ private:
// Rename it
nodep->name(dot(m_unnamedScope, nodep->name()));
m_statep->userMarkChanged(nodep);
// Move to module
nodep->unlinkFrBack();
// Begins under funcs just move into the func
if (m_ftaskp) {
m_ftaskp->addStmtsp(nodep);
} else {
m_modp->addStmtp(nodep);
}
// Move it under enclosing tree
liftNode(nodep);
}
}
virtual void visit(AstCell* nodep) override {
@ -293,7 +308,7 @@ void V3Begin::debeginAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{
BeginState state;
{ BeginVisitor bvisitor(nodep, &state); }
{ BeginVisitor bvisitor{nodep, &state}; }
if (state.anyFuncInBegin()) { BeginRelinkVisitor brvisitor(nodep, &state); }
} // Destruct before checking
V3Global::dumpCheckGlobalTree("begin", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);

View File

@ -43,8 +43,8 @@ private:
AstUser1InUse m_inuser1;
// STATE
int m_likely; // Excuses for branch likely taken
int m_unlikely; // Excuses for branch likely not taken
int m_likely = false; // Excuses for branch likely taken
int m_unlikely = false; // Excuses for branch likely not taken
std::vector<AstCFunc*> m_cfuncsp; // List of all tasks
// METHODS
@ -123,5 +123,5 @@ public:
void V3Branch::branchAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
BranchVisitor visitor(nodep);
BranchVisitor visitor{nodep};
}

View File

@ -18,6 +18,7 @@
// Entire netlist
// Mark all nodes
// Check all links point to marked nodes
// Check local variables in CFuncs appear before they are referenced
//
//*************************************************************************
@ -31,201 +32,129 @@
// This visitor does not edit nodes, and is called at error-exit, so should use constant iterators
#include "V3AstConstOnly.h"
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
//######################################################################
// Generation counter for AstNode::m_brokenState
class BrokenTable VL_NOT_FINAL : public AstNVisitor {
// Table of brokenExists node pointers
static class BrokenCntGlobal {
// This is a 7 bit generation counter, stored in the bottom 7 bits of AstNode::m_brokenState,
// used to mark a node as being present under the root AstNetlist in the current traversal. A
// value 0 is invalid, as the AstNode constructor uses that to initialize m_brokenState
static constexpr uint8_t MIN_VALUE = 1;
static constexpr uint8_t MAX_VALUE = 127;
uint8_t m_count = MIN_VALUE;
public:
uint8_t get() {
UASSERT(MIN_VALUE <= m_count && m_count <= MAX_VALUE, "Invalid generation number");
return m_count;
}
void inc() {
++m_count;
if (m_count > MAX_VALUE) m_count = MIN_VALUE;
}
} s_brokenCntGlobal;
//######################################################################
// Table of allocated AstNode pointers
static class AllocTable final {
private:
// MEMBERS
// For each node, we keep if it exists or not.
using NodeMap = std::unordered_map<const AstNode*, int>; // Performance matters (when --debug)
static NodeMap s_nodes; // Set of all nodes that exist
// BITMASK
enum { FLAG_ALLOCATED = 0x01 }; // new() and not delete()ed
enum { FLAG_IN_TREE = 0x02 }; // Is in netlist tree
enum { FLAG_LINKABLE = 0x04 }; // Is in netlist tree, can be linked to
enum { FLAG_LEAKED = 0x08 }; // Known to have been leaked
enum { FLAG_UNDER_NOW = 0x10 }; // Is in tree as parent of current node
std::unordered_set<const AstNode*> m_allocated; // Set of all nodes allocated but not freed
public:
// METHODS
static void deleted(const AstNode* nodep) {
// Called by operator delete on any node - only if VL_LEAK_CHECKS
if (debug() >= 9) cout << "-nodeDel: " << cvtToHex(nodep) << endl;
const auto iter = s_nodes.find(nodep);
UASSERT_OBJ(!(iter == s_nodes.end() || !(iter->second & FLAG_ALLOCATED)),
reinterpret_cast<const AstNode*>(nodep),
"Deleting AstNode object that was never tracked or already deleted");
if (iter != s_nodes.end()) s_nodes.erase(iter);
}
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 4
// GCC 4.4.* compiler warning bug, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39390
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#endif
static void addNewed(const AstNode* nodep) {
void addNewed(const AstNode* nodep) {
// Called by operator new on any node - only if VL_LEAK_CHECKS
if (debug() >= 9) cout << "-nodeNew: " << cvtToHex(nodep) << endl;
const auto iter = s_nodes.find(nodep);
UASSERT_OBJ(!(iter != s_nodes.end() && (iter->second & FLAG_ALLOCATED)), nodep,
"Newing AstNode object that is already allocated");
if (iter == s_nodes.end()) {
const int flags = FLAG_ALLOCATED; // This int needed to appease GCC 4.1.2
s_nodes.emplace(nodep, flags);
// LCOV_EXCL_START
if (VL_UNCOVERABLE(!m_allocated.emplace(nodep).second)) {
nodep->v3fatalSrc("Newing AstNode object that is already allocated");
}
// LCOV_EXCL_STOP
}
static void setUnder(const AstNode* nodep, bool flag) {
// Called by BrokenCheckVisitor when each node entered/exited
if (!okIfLinkedTo(nodep)) return;
const auto iter = s_nodes.find(nodep);
if (iter != s_nodes.end()) {
iter->second &= ~FLAG_UNDER_NOW;
if (flag) iter->second |= FLAG_UNDER_NOW;
void deleted(const AstNode* nodep) {
// Called by operator delete on any node - only if VL_LEAK_CHECKS
// LCOV_EXCL_START
if (VL_UNCOVERABLE(m_allocated.erase(nodep) == 0)) {
nodep->v3fatalSrc("Deleting AstNode object that was not allocated or already freed");
}
// LCOV_EXCL_STOP
}
static void addInTree(AstNode* nodep, bool linkable) {
#ifndef VL_LEAK_CHECKS
// cppcheck-suppress knownConditionTrueFalse
if (!linkable) return; // save some time, else the map will get huge!
#endif
const auto iter = s_nodes.find(nodep);
if (VL_UNCOVERABLE(iter == s_nodes.end())) {
#ifdef VL_LEAK_CHECKS
nodep->v3fatalSrc("AstNode is in tree, but not allocated");
#endif
} else {
#ifdef VL_LEAK_CHECKS
UASSERT_OBJ(iter->second & FLAG_ALLOCATED, nodep,
"AstNode is in tree, but not allocated");
#endif
UASSERT_OBJ(!(iter->second & FLAG_IN_TREE), nodep,
"AstNode is already in tree at another location");
}
const int or_flags = FLAG_IN_TREE | (linkable ? FLAG_LINKABLE : 0);
if (iter == s_nodes.end()) {
s_nodes.emplace(nodep, or_flags);
} else {
iter->second |= or_flags;
}
}
static bool isAllocated(const AstNode* nodep) {
// Some generic node has a pointer to this node. Is it allocated?
// Use this when might not be in tree; otherwise use okIfLinkedTo().
#ifdef VL_LEAK_CHECKS
const auto iter = s_nodes.find(nodep);
if (iter == s_nodes.end()) return false;
if (!(iter->second & FLAG_ALLOCATED)) return false;
#endif
return true;
}
static bool okIfLinkedTo(const AstNode* nodep) {
// Some node in tree has a pointer to this node. Is it kosher?
const auto iter = s_nodes.find(nodep);
if (iter == s_nodes.end()) return false;
#ifdef VL_LEAK_CHECKS
if (!(iter->second & FLAG_ALLOCATED)) return false;
#endif
if (!(iter->second & FLAG_IN_TREE)) return false;
if (!(iter->second & FLAG_LINKABLE)) return false;
return true;
}
static bool okIfAbove(const AstNode* nodep) {
// Must be linked to and below current node
if (!okIfLinkedTo(nodep)) return false;
const auto iter = s_nodes.find(nodep);
if (iter == s_nodes.end()) return false;
if ((iter->second & FLAG_UNDER_NOW)) return false;
return true;
}
static bool okIfBelow(const AstNode* nodep) {
// Must be linked to and below current node
if (!okIfLinkedTo(nodep)) return false;
const auto iter = s_nodes.find(nodep);
if (iter == s_nodes.end()) return false;
if (!(iter->second & FLAG_UNDER_NOW)) return false;
return true;
}
static void prepForTree() {
#ifndef VL_LEAK_CHECKS
s_nodes.clear();
#else
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
it->second &= ~FLAG_IN_TREE;
it->second &= ~FLAG_LINKABLE;
}
#endif
}
static void doneWithTree() {
for (int backs = 0; backs < 2;
backs++) { // Those with backp() are probably under one leaking without
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
// LCOV_EXCL_START
if (VL_UNCOVERABLE((it->second & FLAG_ALLOCATED) && !(it->second & FLAG_IN_TREE)
&& !(it->second & FLAG_LEAKED)
&& (it->first->backp() ? backs == 1 : backs == 0))) {
bool isAllocated(const AstNode* nodep) const { return m_allocated.count(nodep) != 0; }
void checkForLeaks() {
if (!v3Global.opt.debugCheck()) return;
const uint8_t brokenCntCurrent = s_brokenCntGlobal.get();
// Those with backp() are probably under a parent that was leaked and has no backp()
for (const bool withBack : {false, true}) {
for (const AstNode* const nodep : m_allocated) {
// LCOV_EXCL_START
// Most likely not leaked, so check that first
if (VL_UNCOVERABLE(nodep->brokenState() != brokenCntCurrent)) {
const bool hasBack = nodep->backp() != nullptr;
if (hasBack != withBack) continue;
// Use only AstNode::dump instead of the virtual one, as there
// may be varp() and other cross links that are bad.
if (v3Global.opt.debugCheck()) {
// When get this message, find what forgot to delete the
// node by running GDB, where for node "<e###>" use:
// watch AstNode::s_editCntGbl==####
// run
// bt
std::cerr << "%Error: LeakedNode"
<< (it->first->backp() ? "Back: " : ": ");
AstNode* rawp
= const_cast<AstNode*>(static_cast<const AstNode*>(it->first));
rawp->AstNode::dump(std::cerr);
std::cerr << endl;
V3Error::incErrors();
}
it->second |= FLAG_LEAKED;
// When get this message, find what forgot to delete the
// node by running GDB, where for node "<e###>" use:
// watch AstNode::s_editCntGbl==####
// run
// bt
std::cerr << "%Error: LeakedNode" << (withBack ? "with back pointer: " : ": ");
nodep->AstNode::dump(std::cerr);
std::cerr << endl;
V3Error::incErrors();
}
// LCOV_EXCL_STOP
}
}
}
} s_allocTable;
// CONSTRUCTORS
BrokenTable() = default;
virtual ~BrokenTable() override = default;
};
BrokenTable::NodeMap BrokenTable::s_nodes;
bool AstNode::brokeExists() const {
// Called by node->broken() routines to do table lookup
return BrokenTable::okIfLinkedTo(this);
}
bool AstNode::brokeExistsAbove() const {
// Called by node->broken() routines to do table lookup
return BrokenTable::okIfBelow(this);
}
bool AstNode::brokeExistsBelow() const {
// Called by node->broken() routines to do table lookup
return BrokenTable::okIfAbove(this);
}
void V3Broken::addNewed(const AstNode* nodep) { s_allocTable.addNewed(nodep); }
void V3Broken::deleted(const AstNode* nodep) { s_allocTable.deleted(nodep); }
//######################################################################
// Table of AstNode pointers that can be linked to via member pointers
static class LinkableTable final {
private:
// MEMBERS
std::unordered_set<const AstNode*> m_linkable; // Set of all nodes allocated but not freed
public:
// METHODS
void clear() { m_linkable.clear(); }
inline void addLinkable(const AstNode* nodep) { m_linkable.emplace(nodep); }
inline bool isLinkable(const AstNode* nodep) const { return m_linkable.count(nodep) != 0; }
} s_linkableTable;
bool V3Broken::isLinkable(const AstNode* nodep) { return s_linkableTable.isLinkable(nodep); }
//######################################################################
// Mark every node in the tree
class BrokenMarkVisitor final : public AstNVisitor {
// Mark every node in the tree
private:
// NODE STATE
// Nothing! // This may be called deep inside other routines
// // so userp and friends may not be used
// METHODS
void processAndIterate(AstNode* nodep) {
BrokenTable::addInTree(nodep, nodep->maybePointedTo());
iterateChildrenConst(nodep);
}
const uint8_t m_brokenCntCurrent = s_brokenCntGlobal.get();
// VISITORS
virtual void visit(AstNode* nodep) override {
// Process not just iterate
processAndIterate(nodep);
#ifdef VL_LEAK_CHECKS
UASSERT_OBJ(s_allocTable.isAllocated(nodep), nodep,
"AstNode is in tree, but not allocated");
#endif
UASSERT_OBJ(nodep->brokenState() != m_brokenCntCurrent, nodep,
"AstNode is already in tree at another location");
if (nodep->maybePointedTo()) s_linkableTable.addLinkable(nodep);
nodep->brokenState(m_brokenCntCurrent);
iterateChildrenConst(nodep);
}
public:
@ -235,19 +164,33 @@ public:
};
//######################################################################
// Broken state, as a visitor of each AstNode
// Check every node in tree
class BrokenCheckVisitor final : public AstNVisitor {
bool m_inScope = false; // Under AstScope
// Constants for marking we are under/not under a node
const uint8_t m_brokenCntCurrentNotUnder = s_brokenCntGlobal.get(); // Top bit is clear
const uint8_t m_brokenCntCurrentUnder = m_brokenCntCurrentNotUnder | 0x80; // Top bit is set
// Current CFunc, if any
const AstCFunc* m_cfuncp = nullptr;
// All local variables declared in current function
std::unordered_set<const AstVar*> m_localVars;
// Variable references in current function that do not reference an in-scope local
std::unordered_map<const AstVar*, const AstNodeVarRef*> m_suspectRefs;
// Local variables declared in the scope of the current statement
std::vector<std::unordered_set<const AstVar*>> m_localsStack;
private:
static void checkWidthMin(const AstNode* nodep) {
UASSERT_OBJ(nodep->width() == nodep->widthMin()
|| v3Global.widthMinUsage() != VWidthMinUsage::MATCHES_WIDTH,
nodep, "Width != WidthMin");
}
void processAndIterate(AstNode* nodep) {
BrokenTable::setUnder(nodep, true);
void processEnter(AstNode* nodep) {
nodep->brokenState(m_brokenCntCurrentUnder);
const char* whyp = nodep->broken();
UASSERT_OBJ(!whyp, nodep,
"Broken link in node (or something without maybePointedTo): " << whyp);
@ -270,8 +213,30 @@ private:
if (const AstNodeDType* dnodep = VN_CAST(nodep, NodeDType)) checkWidthMin(dnodep);
}
checkWidthMin(nodep);
}
void processExit(AstNode* nodep) { nodep->brokenState(m_brokenCntCurrentNotUnder); }
void processAndIterate(AstNode* nodep) {
processEnter(nodep);
iterateChildrenConst(nodep);
BrokenTable::setUnder(nodep, false);
processExit(nodep);
}
void processAndIterateList(AstNode* nodep) {
while (nodep) {
processAndIterate(nodep);
nodep = nodep->nextp();
};
}
void pushLocalScope() {
if (m_cfuncp) m_localsStack.emplace_back();
}
void popLocalScope() {
if (m_cfuncp) m_localsStack.pop_back();
}
bool isInScopeLocal(const AstVar* varp) const {
for (const auto& set : m_localsStack) {
if (set.count(varp)) return true;
}
return false;
}
virtual void visit(AstNodeAssign* nodep) override {
processAndIterate(nodep);
@ -295,6 +260,65 @@ private:
UASSERT_OBJ(
!(v3Global.assertScoped() && m_inScope && nodep->varp() && !nodep->varScopep()), nodep,
"VarRef missing VarScope pointer");
if (m_cfuncp) {
// Check if variable is an in-scope local, otherwise mark as suspect
if (const AstVar* const varp = nodep->varp()) {
if (!isInScopeLocal(varp)) {
// This only stores the first ref for each Var, which is what we want
m_suspectRefs.emplace(varp, nodep);
}
}
}
}
virtual void visit(AstCFunc* nodep) override {
UASSERT_OBJ(!m_cfuncp, nodep, "Nested AstCFunc");
m_cfuncp = nodep;
m_localVars.clear();
m_suspectRefs.clear();
m_localsStack.clear();
pushLocalScope();
processAndIterate(nodep);
// Check suspect references are all to non-locals
for (const auto& pair : m_suspectRefs) {
UASSERT_OBJ(m_localVars.count(pair.first) == 0, pair.second,
"Local variable not in scope where referenced: " << pair.first);
}
m_cfuncp = nullptr;
}
virtual void visit(AstNodeIf* nodep) override {
// Each branch is a separate local variable scope
pushLocalScope();
processEnter(nodep);
processAndIterate(nodep->condp());
if (AstNode* const ifsp = nodep->ifsp()) {
pushLocalScope();
processAndIterateList(ifsp);
popLocalScope();
}
if (AstNode* const elsesp = nodep->elsesp()) {
pushLocalScope();
processAndIterateList(elsesp);
popLocalScope();
}
processExit(nodep);
popLocalScope();
}
virtual void visit(AstNodeStmt* nodep) override {
// For local variable checking act as if any statement introduces a new scope.
// This is aggressive but conservatively correct.
pushLocalScope();
processAndIterate(nodep);
popLocalScope();
}
virtual void visit(AstVar* nodep) override {
processAndIterate(nodep);
if (m_cfuncp) {
m_localVars.insert(nodep);
m_localsStack.back().insert(nodep);
}
}
virtual void visit(AstNode* nodep) override {
// Process not just iterate
@ -308,7 +332,7 @@ public:
};
//######################################################################
// Broken class functions
// Broken check entry point
void V3Broken::brokenAll(AstNetlist* nodep) {
// UINFO(9, __FUNCTION__ << ": " << endl);
@ -318,24 +342,23 @@ void V3Broken::brokenAll(AstNetlist* nodep) {
UINFO(1, "Broken called under broken, skipping recursion.\n"); // LCOV_EXCL_LINE
} else {
inBroken = true;
BrokenTable::prepForTree();
BrokenMarkVisitor mvisitor(nodep);
BrokenCheckVisitor cvisitor(nodep);
BrokenTable::doneWithTree();
BrokenMarkVisitor mvisitor{nodep};
BrokenCheckVisitor cvisitor{nodep};
s_allocTable.checkForLeaks();
s_linkableTable.clear();
s_brokenCntGlobal.inc();
inBroken = false;
}
}
void V3Broken::addNewed(AstNode* nodep) { BrokenTable::addNewed(nodep); }
void V3Broken::deleted(AstNode* nodep) { BrokenTable::deleted(nodep); }
bool V3Broken::isAllocated(AstNode* nodep) { return BrokenTable::isAllocated(nodep); }
//######################################################################
// Self test
void V3Broken::selfTest() {
// Warmup addNewed and deleted for coverage, as otherwise only with VL_LEAK_CHECKS
FileLine* fl = new FileLine(FileLine::commandLineFilename());
auto* newp = new AstBegin(fl, "[EditWrapper]", nullptr);
#ifndef VL_LEAK_CHECKS
// Exercise addNewed and deleted for coverage, as otherwise only used with VL_LEAK_CHECKS
FileLine* const fl = new FileLine(FileLine::commandLineFilename());
const AstNode* const newp = new AstBegin(fl, "[EditWrapper]", nullptr);
addNewed(newp);
deleted(newp);
#endif
VL_DO_DANGLING(delete newp, newp);
}

View File

@ -20,17 +20,18 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
// Forward declare so we can include this in V3Ast.h
class AstNode;
class AstNetlist;
class V3Broken final {
public:
static void brokenAll(AstNetlist* nodep);
static void addNewed(AstNode* nodep);
static void deleted(AstNode* nodep);
static bool isAllocated(AstNode* nodep);
static bool isLinkable(const AstNode* nodep);
static void addNewed(const AstNode* nodep);
static void deleted(const AstNode* nodep);
static void selfTest();
};

View File

@ -60,7 +60,7 @@ private:
AstCFunc* makeNewFunc() {
const int funcNum = m_newFunctions.size();
const string funcName = m_basename + "_" + cvtToStr(funcNum);
AstCFunc* const funcp = new AstCFunc(m_modp->fileline(), funcName, nullptr, "void");
AstCFunc* const funcp = new AstCFunc{m_modp->fileline(), funcName, nullptr, "void"};
funcp->isStatic(false);
funcp->isLoose(!m_type.isClass());
funcp->declPrivate(true);
@ -74,7 +74,7 @@ private:
preventUnusedStmt = "if (false && first) {} // Prevent unused\n";
}
if (!preventUnusedStmt.empty()) {
funcp->addStmtsp(new AstCStmt(m_modp->fileline(), preventUnusedStmt));
funcp->addStmtsp(new AstCStmt{m_modp->fileline(), preventUnusedStmt});
}
m_modp->addStmtp(funcp);
m_numStmts = 0;
@ -91,9 +91,9 @@ public:
}
V3CCtorsBuilder(AstNodeModule* nodep, const string& basename, VCtorType type)
: m_modp(nodep)
: m_modp{nodep}
, m_basename{basename}
, m_type(type) {
, m_type{type} {
// Note: The constructor is always called, even if empty, so we must always create at least
// one.
m_newFunctions.push_back(makeNewFunc());
@ -108,7 +108,7 @@ public:
AstCFunc* const rootFuncp = makeNewFunc();
rootFuncp->name(m_basename);
for (AstCFunc* const funcp : m_newFunctions) {
AstCCall* const callp = new AstCCall(m_modp->fileline(), funcp);
AstCCall* const callp = new AstCCall{m_modp->fileline(), funcp};
if (m_type.isClass()) {
callp->argTypes("vlSymsp");
} else {
@ -127,8 +127,9 @@ private:
//######################################################################
void V3CCtors::evalAsserts() {
AstNodeModule* modp = v3Global.rootp()->modulesp(); // Top module wrapper
AstCFunc* funcp = new AstCFunc(modp->fileline(), "_eval_debug_assertions", nullptr, "void");
AstNodeModule* const modp = v3Global.rootp()->modulesp(); // Top module wrapper
AstCFunc* const funcp
= new AstCFunc{modp->fileline(), "_eval_debug_assertions", nullptr, "void"};
funcp->declPrivate(true);
funcp->isStatic(false);
funcp->isLoose(true);
@ -136,7 +137,7 @@ void V3CCtors::evalAsserts() {
funcp->ifdef("VL_DEBUG");
modp->addStmtp(funcp);
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
if (AstVar* varp = VN_CAST(np, Var)) {
if (AstVar* const varp = VN_CAST(np, Var)) {
if (varp->isPrimaryInish() && !varp->isSc()) {
if (AstBasicDType* basicp = VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
const int storedWidth = basicp->widthAlignBytes() * 8;
@ -144,22 +145,22 @@ void V3CCtors::evalAsserts() {
if (lastWordWidth != 0) {
// if (signal & CONST(upper_non_clean_mask)) { fail; }
AstVarRef* const vrefp
= new AstVarRef(varp->fileline(), varp, VAccess::READ);
= new AstVarRef{varp->fileline(), varp, VAccess::READ};
vrefp->selfPointer("this");
AstNode* newp = vrefp;
if (varp->isWide()) {
newp = new AstWordSel(
newp = new AstWordSel{
varp->fileline(), newp,
new AstConst(varp->fileline(), varp->widthWords() - 1));
new AstConst(varp->fileline(), varp->widthWords() - 1)};
}
uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth);
newp = new AstAnd(varp->fileline(), newp,
const uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth);
newp = new AstAnd{varp->fileline(), newp,
new AstConst(varp->fileline(), AstConst::WidthedValue(),
storedWidth, value));
AstNodeIf* ifp = new AstIf(
storedWidth, value)};
AstNodeIf* const ifp = new AstIf{
varp->fileline(), newp,
new AstCStmt(varp->fileline(), "Verilated::overWidthError(\""
+ varp->prettyName() + "\");"));
new AstCStmt{varp->fileline(), "Verilated::overWidthError(\""
+ varp->prettyName() + "\");"}};
ifp->branchPred(VBranchPred::BP_UNLIKELY);
newp = ifp;
funcp->addStmtsp(newp);
@ -177,20 +178,21 @@ void V3CCtors::cctorsAll() {
modp = VN_CAST(modp->nextp(), NodeModule)) {
// Process each module in turn
{
V3CCtorsBuilder var_reset(modp, "_ctor_var_reset",
VN_IS(modp, Class) ? VCtorType::CLASS : VCtorType::MODULE);
V3CCtorsBuilder var_reset{modp, "_ctor_var_reset",
VN_IS(modp, Class) ? VCtorType::CLASS : VCtorType::MODULE};
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
if (AstVar* const varp = VN_CAST(np, Var)) {
if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset()) {
const auto vrefp = new AstVarRef(varp->fileline(), varp, VAccess::WRITE);
var_reset.add(new AstCReset(varp->fileline(), vrefp));
if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset()
&& !varp->isParam()) {
const auto vrefp = new AstVarRef{varp->fileline(), varp, VAccess::WRITE};
var_reset.add(new AstCReset{varp->fileline(), vrefp});
}
}
}
}
if (v3Global.opt.coverage()) {
V3CCtorsBuilder configure_coverage(modp, "_configure_coverage", VCtorType::COVERAGE);
V3CCtorsBuilder configure_coverage{modp, "_configure_coverage", VCtorType::COVERAGE};
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
if (AstCoverDecl* const coverp = VN_CAST(np, CoverDecl)) {
np = coverp->backp();
@ -198,8 +200,8 @@ void V3CCtors::cctorsAll() {
}
}
}
if (AstClass* classp = VN_CAST(modp, Class)) {
AstCFunc* funcp = new AstCFunc(modp->fileline(), "~", nullptr, "");
if (AstClass* const classp = VN_CAST(modp, Class)) {
AstCFunc* const funcp = new AstCFunc{modp->fileline(), "~", nullptr, ""};
funcp->isDestructor(true);
funcp->isStatic(false);
// If can be referred to by base pointer, need virtual delete

View File

@ -13,14 +13,12 @@
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
// V3Class's Transformations:
// V3CUse's Transformations:
//
// Each module:
// Each cell:
// Create CUse for cell forward declaration
// Each class:
// Create string access functions
// Search for dtypes referencing class, and create CUse for forward declaraion
// Search for dtypes referencing class, and create CUse for forward declaration
//
//*************************************************************************
@ -30,61 +28,37 @@
#include "V3Global.h"
#include "V3CUse.h"
#include "V3Ast.h"
#include "V3EmitCBase.h"
#include <unordered_map>
#include <set>
//######################################################################
class CUseState final {
private:
// MEMBERS
AstNodeModule* m_modInsertp; // Current module to insert AstCUse under
using UseString = std::pair<VUseType, std::string>;
std::map<const UseString, AstCUse*> m_didUse; // What we already used
// NODE STATE
// Entire netlist:
// AstClass::user1() -> bool. True if class needs to_string dumper
AstUser1InUse m_inuser1;
// AstClass::user2() -> bool. True if iterated
AstUser2InUse m_inuser2;
// METHODS
VL_DEBUG_FUNC; // Declare debug()
public:
AstCUse* newUse(AstNode* nodep, VUseType useType, const string& name) {
UseString key(useType, name);
if (m_didUse.find(key) == m_didUse.end()) {
AstCUse* newp = new AstCUse(nodep->fileline(), useType, name);
m_modInsertp->addStmtp(newp);
UINFO(8, "Insert " << newp << endl);
m_didUse[key] = newp;
}
return m_didUse[key];
}
// CONSTRUCTORS
explicit CUseState(AstNodeModule* nodep)
: m_modInsertp{nodep} {}
virtual ~CUseState() = default;
VL_UNCOPYABLE(CUseState);
};
// Visit within a module all nodes and data types they reference, finding
// any classes so we can make sure they are defined when Verilated code
// compiles
class CUseDTypeVisitor final : public AstNVisitor {
class CUseVisitor final : public AstNVisitor {
// NODE STATE
// AstNode::user1() -> bool. True if already visited
AstUser1InUse m_inuser1;
// MEMBERS
CUseState& m_stater; // State for inserter
bool m_impOnly = false; // In details needed only for implementation
AstNodeModule* const m_modp; // Current module
std::set<std::pair<VUseType, std::string>> m_didUse; // What we already used
// METHODS
void addNewUse(AstNode* nodep, VUseType useType, const string& name) {
if (m_didUse.emplace(useType, name).second) {
AstCUse* const newp = new AstCUse{nodep->fileline(), useType, name};
m_modp->addStmtp(newp);
UINFO(8, "Insert " << newp << endl);
}
}
// VISITORS
virtual void visit(AstClassRefDType* nodep) override {
if (nodep->user2SetOnce()) return; // Process once
if (!m_impOnly) m_stater.newUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
// No class.h, it's inside the class package's h file
m_stater.newUse(nodep, VUseType::IMP_INCLUDE, nodep->classp()->classOrPackagep()->name());
if (nodep->user1SetOnce()) return; // Process once
if (!m_impOnly) addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
// Need to include extends() when we implement, but no need for pointers to know
VL_RESTORER(m_impOnly);
{
@ -93,131 +67,27 @@ class CUseDTypeVisitor final : public AstNVisitor {
}
}
virtual void visit(AstNodeDType* nodep) override {
if (nodep->user2SetOnce()) return; // Process once
if (nodep->user1SetOnce()) return; // Process once
if (nodep->virtRefDTypep()) iterate(nodep->virtRefDTypep());
if (nodep->virtRefDType2p()) iterate(nodep->virtRefDType2p());
}
virtual void visit(AstNode* nodep) override {
if (nodep->user2SetOnce()) return; // Process once
if (nodep->dtypep() && !nodep->dtypep()->user2()) iterate(nodep->dtypep());
if (nodep->user1SetOnce()) return; // Process once
if (nodep->dtypep() && !nodep->dtypep()->user1()) iterate(nodep->dtypep());
iterateChildren(nodep);
}
virtual void visit(AstCell* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
// Currently no IMP_INCLUDE because we include __Syms which has them all
addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->modp()->name());
iterateChildren(nodep);
}
public:
// CONSTRUCTORS
explicit CUseDTypeVisitor(AstNodeModule* nodep, CUseState& stater)
: m_stater(stater) { // Need () or GCC 4.8 false warning
iterate(nodep);
}
virtual ~CUseDTypeVisitor() override = default;
VL_UNCOPYABLE(CUseDTypeVisitor);
};
class CUseVisitor final : public AstNVisitor {
// MEMBERS
CUseState m_state; // Inserter state
// METHODS
VL_DEBUG_FUNC; // Declare debug()
// Module use builders
void makeUseCells(AstNodeModule* nodep) {
for (AstNode* itemp = nodep->stmtsp(); itemp; itemp = itemp->nextp()) {
if (AstCell* cellp = VN_CAST(itemp, Cell)) {
// Currently no include because we include __Syms which has them all
m_state.newUse(nodep, VUseType::INT_FWD_CLASS, cellp->modp()->name());
}
}
}
void makeVlToString(AstClass* nodep) {
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "VL_TO_STRING", nullptr, "std::string");
funcp->argTypes("const VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(nodep)
+ ">& obj");
funcp->isMethod(false);
funcp->isConst(false);
funcp->isStatic(false);
funcp->protect(false);
AstNode* exprp = new AstCMath(nodep->fileline(), "obj ? obj->to_string() : \"null\"", 0);
exprp->dtypeSetString();
funcp->addStmtsp(new AstCReturn(nodep->fileline(), exprp));
nodep->addStmtp(funcp);
}
void makeToString(AstClass* nodep) {
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "to_string", nullptr, "std::string");
funcp->isConst(true);
funcp->isStatic(false);
funcp->protect(false);
AstNode* exprp = new AstCMath(nodep->fileline(),
R"(std::string("'{") + to_string_middle() + "}")", 0);
exprp->dtypeSetString();
funcp->addStmtsp(new AstCReturn(nodep->fileline(), exprp));
nodep->addStmtp(funcp);
}
void makeToStringMiddle(AstClass* nodep) {
AstCFunc* funcp
= new AstCFunc(nodep->fileline(), "to_string_middle", nullptr, "std::string");
funcp->isConst(true);
funcp->isStatic(false);
funcp->protect(false);
funcp->addStmtsp(new AstCStmt(nodep->fileline(), "std::string out;\n"));
std::string comma;
for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) {
if (auto* varp = VN_CAST(itemp, Var)) {
if (!varp->isParam()) {
string stmt = "out += \"";
stmt += comma;
comma = ", ";
stmt += itemp->origNameProtect();
stmt += ":\" + ";
if (itemp->isWide()) {
stmt += "VL_TO_STRING_W(";
stmt += cvtToStr(itemp->widthWords());
stmt += ", ";
} else {
stmt += "VL_TO_STRING(";
}
stmt += itemp->nameProtect();
stmt += ");\n";
nodep->user1(true); // So what we extend dumps this
funcp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
}
}
}
if (nodep->extendsp() && nodep->extendsp()->classp()->user1()) {
string stmt = "out += \"";
if (!comma.empty()) stmt += "\", \"+ ";
// comma = ", "; // Nothing further so not needed
stmt += nodep->extendsp()->dtypep()->nameProtect();
stmt += "::to_string_middle();\n";
nodep->user1(true); // So what we extend dumps this
funcp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
}
funcp->addStmtsp(new AstCStmt(nodep->fileline(), "return out;\n"));
nodep->addStmtp(funcp);
}
// VISITORS
virtual void visit(AstNodeModule* nodep) override {
if (v3Global.opt.trace()) {
AstCUse* usep
= m_state.newUse(nodep, VUseType::INT_FWD_CLASS, v3Global.opt.traceClassBase());
usep->protect(false);
}
makeUseCells(nodep);
{ CUseDTypeVisitor dtypeVisitor(nodep, m_state); }
if (AstClass* classp = VN_CAST(nodep, Class)) {
makeVlToString(classp);
makeToString(classp);
makeToStringMiddle(classp);
}
}
virtual void visit(AstNode*) override {} // All in AstNodeModule
public:
// CONSTRUCTORS
explicit CUseVisitor(AstNodeModule* nodep)
: m_state{nodep} {
iterate(nodep);
explicit CUseVisitor(AstNodeModule* modp)
: m_modp(modp) {
iterate(modp);
}
virtual ~CUseVisitor() override = default;
VL_UNCOPYABLE(CUseVisitor);
@ -233,7 +103,7 @@ void V3CUse::cUseAll() {
modp = VN_CAST(modp->nextp(), NodeModule)) {
// Insert under this module; someday we should e.g. make Ast
// for each output file and put under that
CUseVisitor visitor(modp);
CUseVisitor{modp};
}
V3Global::dumpCheckGlobalTree("cuse", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

View File

@ -530,10 +530,10 @@ public:
void V3Case::caseAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CaseVisitor visitor(nodep); } // Destruct before checking
{ CaseVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("case", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
void V3Case::caseLint(AstNodeCase* nodep) {
UINFO(4, __FUNCTION__ << ": " << endl);
CaseLintVisitor visitor(nodep);
CaseLintVisitor visitor{nodep};
}

View File

@ -66,7 +66,8 @@ private:
AstNRelinker relinkHandle;
nodep->unlinkFrBack(&relinkHandle);
//
AstCCast* castp = new AstCCast(nodep->fileline(), nodep, needsize, nodep->widthMin());
AstCCast* const castp
= new AstCCast{nodep->fileline(), nodep, needsize, nodep->widthMin()};
relinkHandle.relink(castp);
// if (debug() > 8) castp->dumpTree(cout, "-castins: ");
//
@ -103,7 +104,7 @@ private:
if (!VN_IS(nodep->backp(), NullCheck)) {
AstNRelinker relinkHandle;
nodep->unlinkFrBack(&relinkHandle);
AstNode* newp = new AstNullCheck(nodep->fileline(), nodep);
AstNode* const newp = new AstNullCheck{nodep->fileline(), nodep};
relinkHandle.relink(newp);
}
}
@ -154,9 +155,10 @@ private:
}
}
virtual void visit(AstVarRef* nodep) override {
if (nodep->access().isReadOnly() && !VN_IS(nodep->backp(), CCast)
&& VN_IS(nodep->backp(), NodeMath) && !VN_IS(nodep->backp(), ArraySel)
&& nodep->backp()->width() && castSize(nodep) != castSize(nodep->varp())) {
AstNode* const backp = nodep->backp();
if (nodep->access().isReadOnly() && !VN_IS(backp, CCast) && VN_IS(backp, NodeMath)
&& !VN_IS(backp, ArraySel) && !VN_IS(backp, RedXor) && backp->width()
&& castSize(nodep) != castSize(nodep->varp())) {
// Cast vars to IData first, else below has upper bits wrongly set
// CData x=3; out = (QData)(x<<30);
insertCast(nodep, castSize(nodep));
@ -201,6 +203,6 @@ public:
void V3Cast::castAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CastVisitor visitor(nodep); } // Destruct before checking
{ CastVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("cast", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

View File

@ -337,7 +337,7 @@ private:
int filelineWidth() {
if (!m_filelineWidth) {
CdcWidthVisitor visitor(v3Global.rootp());
CdcWidthVisitor visitor{v3Global.rootp()};
m_filelineWidth = visitor.maxWidth();
}
return m_filelineWidth;
@ -495,7 +495,7 @@ private:
UINFO(3, __FUNCTION__ << ": " << endl);
// Trace all sources and sinks
for (int traceDests = 0; traceDests < 2; ++traceDests) {
for (const bool& traceDests : {false, true}) {
UINFO(9, " Trace Direction " << (traceDests ? "dst" : "src") << endl);
m_graph.userClearVertices(); // user1: bool - was analyzed
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
@ -508,7 +508,7 @@ private:
const string filename
= v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc_edges.txt";
const std::unique_ptr<std::ofstream> ofp(V3File::new_ofstream(filename));
const std::unique_ptr<std::ofstream> ofp{V3File::new_ofstream(filename)};
if (ofp->fail()) v3fatal("Can't write " << filename);
*ofp << "Edge Report for " << v3Global.opt.prefix() << '\n';
@ -758,5 +758,5 @@ public:
void V3Cdc::cdcAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
CdcVisitor visitor(nodep);
CdcVisitor visitor{nodep};
}

View File

@ -47,11 +47,33 @@ public:
AstCFunc* m_tlChgFuncp = nullptr; // Top level change function we're building
int m_numStmts = 0; // Number of statements added to m_chgFuncp
int m_funcNum = 0; // Number of change functions emitted
bool m_madeTopChg = false;
ChangedState() = default;
~ChangedState() = default;
void maybeCreateChgFuncp() {
maybeCreateTopChg();
maybeCreateMidChg();
}
void maybeCreateTopChg() {
if (m_madeTopChg) return;
m_madeTopChg = true;
v3Global.rootp()->changeRequest(true);
// Create a wrapper change detection function that calls each change detection function
m_tlChgFuncp
= new AstCFunc{m_scopetopp->fileline(), "_change_request", m_scopetopp, "QData"};
m_tlChgFuncp->isStatic(false);
m_tlChgFuncp->isLoose(true);
m_tlChgFuncp->declPrivate(true);
m_scopetopp->addActivep(m_tlChgFuncp);
// Each change detection function needs at least one AstChangeDet
// to ensure that V3EmitC outputs the necessary code.
maybeCreateMidChg();
m_chgFuncp->addStmtsp(new AstChangeDet{m_scopetopp->fileline(), nullptr, nullptr, false});
}
void maybeCreateMidChg() {
// Don't create an extra function call if splitting is disabled
if (!v3Global.opt.outputSplitCFuncs()) {
m_chgFuncp = m_tlChgFuncp;
@ -59,29 +81,29 @@ public:
}
if (!m_chgFuncp || v3Global.opt.outputSplitCFuncs() < m_numStmts) {
m_chgFuncp
= new AstCFunc(m_scopetopp->fileline(), "_change_request_" + cvtToStr(++m_funcNum),
m_scopetopp, "QData");
= new AstCFunc{m_scopetopp->fileline(), "_change_request_" + cvtToStr(++m_funcNum),
m_scopetopp, "QData"};
m_chgFuncp->isStatic(false);
m_chgFuncp->isLoose(true);
m_chgFuncp->declPrivate(true);
m_scopetopp->addActivep(m_chgFuncp);
// Add a top call to it
AstCCall* callp = new AstCCall(m_scopetopp->fileline(), m_chgFuncp);
AstCCall* const callp = new AstCCall{m_scopetopp->fileline(), m_chgFuncp};
if (!m_tlChgFuncp->stmtsp()) {
m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp));
m_tlChgFuncp->addStmtsp(new AstCReturn{m_scopetopp->fileline(), callp});
} else {
AstCReturn* returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn);
AstCReturn* const returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn);
UASSERT_OBJ(returnp, m_scopetopp, "Lost CReturn in top change function");
// This is currently using AstLogOr which will shortcut the
// evaluation if any function returns true. This is likely what
// we want and is similar to the logic already in use inside
// V3EmitC, however, it also means that verbose logging may
// miss to print change detect variables.
AstNode* newp = new AstCReturn(
AstNode* const newp = new AstCReturn{
m_scopetopp->fileline(),
new AstLogOr(m_scopetopp->fileline(), callp, returnp->lhsp()->unlinkFrBack()));
new AstLogOr{m_scopetopp->fileline(), callp, returnp->lhsp()->unlinkFrBack()}};
returnp->replaceWith(newp);
VL_DO_DANGLING(returnp->deleteTree(), returnp);
}
@ -123,14 +145,17 @@ private:
}
m_statep->maybeCreateChgFuncp();
AstChangeDet* changep = new AstChangeDet(m_vscp->fileline(), m_varEqnp->cloneTree(true),
m_newRvEqnp->cloneTree(true), false);
AstChangeDet* const changep = new AstChangeDet{
m_vscp->fileline(), m_varEqnp->cloneTree(true), m_newRvEqnp->cloneTree(true), false};
m_statep->m_chgFuncp->addStmtsp(changep);
AstAssign* initp = new AstAssign(m_vscp->fileline(), m_newLvEqnp->cloneTree(true),
m_varEqnp->cloneTree(true));
AstAssign* const initp = new AstAssign{m_vscp->fileline(), m_newLvEqnp->cloneTree(true),
m_varEqnp->cloneTree(true)};
m_statep->m_chgFuncp->addFinalsp(initp);
EmitCBaseCounterVisitor visitor(initp);
m_statep->m_numStmts += visitor.count();
// Later code will expand words which adds to GCC compile time,
// so add penalty based on word width also
EmitCBaseCounterVisitor visitor{initp};
m_statep->m_numStmts += visitor.count() + m_varEqnp->widthWords();
}
virtual void visit(AstBasicDType*) override { //
@ -145,11 +170,11 @@ private:
VL_RESTORER(m_newLvEqnp);
VL_RESTORER(m_newRvEqnp);
{
m_varEqnp = new AstArraySel(nodep->fileline(), m_varEqnp->cloneTree(true), index);
m_varEqnp = new AstArraySel{nodep->fileline(), m_varEqnp->cloneTree(true), index};
m_newLvEqnp
= new AstArraySel(nodep->fileline(), m_newLvEqnp->cloneTree(true), index);
= new AstArraySel{nodep->fileline(), m_newLvEqnp->cloneTree(true), index};
m_newRvEqnp
= new AstArraySel(nodep->fileline(), m_newRvEqnp->cloneTree(true), index);
= new AstArraySel{nodep->fileline(), m_newRvEqnp->cloneTree(true), index};
iterate(nodep->subDTypep()->skipRefp());
@ -180,26 +205,30 @@ private:
public:
// CONSTRUCTORS
ChangedInsertVisitor(AstVarScope* vscp, ChangedState* statep) {
// DPI export trigger should never need change detect. See similar assertions in V3Order
// (OrderVisitor::nodeMarkCircular), and V3GenClk (GenClkRenameVisitor::genInpClk).
UASSERT_OBJ(vscp != v3Global.rootp()->dpiExportTriggerp(), vscp,
"DPI export trigger should not need change detect");
m_statep = statep;
m_vscp = vscp;
m_detects = 0;
{
AstVar* varp = m_vscp->varp();
string newvarname
= ("__Vchglast__" + m_vscp->scopep()->nameDotless() + "__" + varp->shortName());
AstVar* const varp = m_vscp->varp();
string newvarname{"__Vchglast__" + m_vscp->scopep()->nameDotless() + "__"
+ varp->shortName()};
// Create: VARREF(_last)
// ASSIGN(VARREF(_last), VARREF(var))
// ...
// CHANGEDET(VARREF(_last), VARREF(var))
AstVar* newvarp
= new AstVar(varp->fileline(), AstVarType::MODULETEMP, newvarname, varp);
AstVar* const newvarp
= new AstVar{varp->fileline(), AstVarType::MODULETEMP, newvarname, varp};
m_statep->m_topModp->addStmtp(newvarp);
m_newvscp = new AstVarScope(m_vscp->fileline(), m_statep->m_scopetopp, newvarp);
m_newvscp = new AstVarScope{m_vscp->fileline(), m_statep->m_scopetopp, newvarp};
m_statep->m_scopetopp->addVarp(m_newvscp);
m_varEqnp = new AstVarRef(m_vscp->fileline(), m_vscp, VAccess::READ);
m_newLvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, VAccess::WRITE);
m_newRvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, VAccess::READ);
m_varEqnp = new AstVarRef{m_vscp->fileline(), m_vscp, VAccess::READ};
m_newLvEqnp = new AstVarRef{m_vscp->fileline(), m_newvscp, VAccess::WRITE};
m_newRvEqnp = new AstVarRef{m_vscp->fileline(), m_newvscp, VAccess::READ};
}
iterate(vscp->dtypep()->skipRefp());
m_varEqnp->deleteTree();
@ -228,7 +257,7 @@ private:
void genChangeDet(AstVarScope* vscp) {
vscp->v3warn(IMPERFECTSCH, "Imperfect scheduling of variable: " << vscp->prettyNameQ());
ChangedInsertVisitor visitor(vscp, m_statep);
ChangedInsertVisitor visitor{vscp, m_statep};
}
// VISITORS
@ -242,24 +271,11 @@ private:
UINFO(4, " TS " << nodep << endl);
// Clearing
AstNode::user1ClearTree();
// Create the change detection function
AstScope* scopep = nodep->scopep();
// Prep for if make change detection function
AstScope* const scopep = nodep->scopep();
UASSERT_OBJ(scopep, nodep, "No scope found on top level, perhaps you have no statements?");
m_statep->m_scopetopp = scopep;
// Create a wrapper change detection function that calls each change detection function
m_statep->m_tlChgFuncp
= new AstCFunc(nodep->fileline(), "_change_request", scopep, "QData");
m_statep->m_tlChgFuncp->isStatic(false);
m_statep->m_tlChgFuncp->isLoose(true);
m_statep->m_tlChgFuncp->declPrivate(true);
m_statep->m_scopetopp->addActivep(m_statep->m_tlChgFuncp);
// Each change detection function needs at least one AstChangeDet
// to ensure that V3EmitC outputs the necessary code.
m_statep->maybeCreateChgFuncp();
m_statep->m_chgFuncp->addStmtsp(
new AstChangeDet(nodep->fileline(), nullptr, nullptr, false));
iterateChildren(nodep);
}
virtual void visit(AstVarScope* nodep) override {
@ -288,7 +304,7 @@ void V3Changed::changedAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{
ChangedState state;
ChangedVisitor visitor(nodep, &state);
ChangedVisitor visitor{nodep, &state};
} // Destruct before checking
V3Global::dumpCheckGlobalTree("changed", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

View File

@ -153,6 +153,6 @@ public:
void V3Class::classAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ClassVisitor visitor(nodep); } // Destruct before checking
{ ClassVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("class", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

View File

@ -314,6 +314,6 @@ public:
void V3Clean::cleanAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CleanVisitor visitor(nodep); } // Destruct before checking
{ CleanVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("clean", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

View File

@ -257,6 +257,14 @@ private:
// Process the activates
iterateChildren(nodep);
UINFO(4, " TOPSCOPE iter done " << nodep << endl);
// Clear the DPI export trigger flag at the end of eval
if (AstVarScope* const dpiExportTriggerp = v3Global.rootp()->dpiExportTriggerp()) {
FileLine* const fl = dpiExportTriggerp->fileline();
AstAssign* const assignp
= new AstAssign{fl, new AstVarRef{fl, dpiExportTriggerp, VAccess::WRITE},
new AstConst{fl, AstConst::BitFalse{}}};
m_evalFuncp->addFinalsp(assignp);
}
// Split large functions
splitCheck(m_evalFuncp);
splitCheck(m_initFuncp);
@ -435,6 +443,6 @@ public:
void V3Clock::clockAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ClockVisitor visitor(nodep); } // Destruct before checking
{ ClockVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("clock", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

View File

@ -223,6 +223,6 @@ public:
void V3Combine::combineAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CombineVisitor visitor(nodep); } // Destruct before checking
{ CombineVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("combine", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

120
src/V3Common.cpp Normal file
View File

@ -0,0 +1,120 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Add common contents to modules
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
// V3Common's Transformations:
//
// Each class:
// Create string access functions
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include "V3Common.h"
#include "V3Ast.h"
#include "V3Global.h"
#include "V3EmitCBase.h"
//######################################################################
// Common component builders
static void makeVlToString(AstClass* nodep) {
AstCFunc* const funcp
= new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"};
funcp->argTypes("const VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(nodep) + ">& obj");
funcp->isMethod(false);
funcp->isConst(false);
funcp->isStatic(false);
funcp->protect(false);
AstNode* const exprp = new AstCMath{nodep->fileline(), "obj ? obj->to_string() : \"null\"", 0};
exprp->dtypeSetString();
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
nodep->addStmtp(funcp);
}
static void makeToString(AstClass* nodep) {
AstCFunc* const funcp = new AstCFunc{nodep->fileline(), "to_string", nullptr, "std::string"};
funcp->isConst(true);
funcp->isStatic(false);
funcp->protect(false);
AstNode* const exprp
= new AstCMath{nodep->fileline(), R"(std::string("'{") + to_string_middle() + "}")", 0};
exprp->dtypeSetString();
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
nodep->addStmtp(funcp);
}
static void makeToStringMiddle(AstClass* nodep) {
AstCFunc* const funcp
= new AstCFunc{nodep->fileline(), "to_string_middle", nullptr, "std::string"};
funcp->isConst(true);
funcp->isStatic(false);
funcp->protect(false);
funcp->addStmtsp(new AstCStmt{nodep->fileline(), "std::string out;\n"});
std::string comma;
for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) {
if (auto* const varp = VN_CAST(itemp, Var)) {
if (!varp->isParam()) {
string stmt = "out += \"";
stmt += comma;
comma = ", ";
stmt += itemp->origNameProtect();
stmt += ":\" + ";
if (itemp->isWide()) {
stmt += "VL_TO_STRING_W(";
stmt += cvtToStr(itemp->widthWords());
stmt += ", ";
} else {
stmt += "VL_TO_STRING(";
}
stmt += itemp->nameProtect();
stmt += ");\n";
nodep->user1(true); // So what we extend dumps this
funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt});
}
}
}
if (nodep->extendsp() && nodep->extendsp()->classp()->user1()) {
string stmt = "out += \"";
if (!comma.empty()) stmt += "\", \"+ ";
// comma = ", "; // Nothing further so not needed
stmt += nodep->extendsp()->dtypep()->nameProtect();
stmt += "::to_string_middle();\n";
nodep->user1(true); // So what we extend dumps this
funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt});
}
funcp->addStmtsp(new AstCStmt{nodep->fileline(), "return out;\n"});
nodep->addStmtp(funcp);
}
//######################################################################
// V3Common class functions
void V3Common::commonAll() {
UINFO(2, __FUNCTION__ << ": " << endl);
// Create common contents for each module
for (AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) {
if (AstClass* const classp = VN_CAST(nodep, Class)) {
// NODE STATE
// Entire netlist:
// AstClass::user1() -> bool. True if class needs to_string dumper
AstUser1InUse m_inuser1;
// Create ToString methods
makeVlToString(classp);
makeToString(classp);
makeToStringMiddle(classp);
}
}
V3Global::dumpCheckGlobalTree("common", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

30
src/V3Common.h Normal file
View File

@ -0,0 +1,30 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Add common contents to modules
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef VERILATOR_V3COMMON_H_
#define VERILATOR_V3COMMON_H_
#include "config_build.h"
#include "verilatedos.h"
//============================================================================
class V3Common final {
public:
static void commonAll();
};
#endif // Guard

File diff suppressed because it is too large Load Diff

View File

@ -543,6 +543,6 @@ public:
void V3Coverage::coverage(AstNetlist* rootp) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CoverageVisitor visitor(rootp); } // Destruct before checking
{ CoverageVisitor visitor{rootp}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("coverage", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

View File

@ -114,6 +114,6 @@ public:
void V3CoverageJoin::coverageJoin(AstNetlist* rootp) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CoverageJoinVisitor visitor(rootp); } // Destruct before checking
{ CoverageJoinVisitor visitor{rootp}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("coveragejoin", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

View File

@ -318,7 +318,7 @@ private:
// And its children may now be killable too; correct counts
// Recurse, as cells may not be directly under the module but in a generate
if (!modp->dead()) { // If was dead didn't increment user1's
DeadModVisitor visitor(modp);
DeadModVisitor visitor{modp};
}
VL_DO_DANGLING(modp->unlinkFrBack()->deleteTree(), modp);
retry = true;
@ -463,31 +463,31 @@ public:
void V3Dead::deadifyModules(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor visitor(nodep, false, false, false, false); } // Destruct before checking
{ DeadVisitor visitor{nodep, false, false, false, false}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadModules", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
}
void V3Dead::deadifyDTypes(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor visitor(nodep, false, true, false, false); } // Destruct before checking
{ DeadVisitor visitor{nodep, false, true, false, false}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadDtypes", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
void V3Dead::deadifyDTypesScoped(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor visitor(nodep, false, true, true, false); } // Destruct before checking
{ DeadVisitor visitor{nodep, false, true, true, false}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0,
v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
void V3Dead::deadifyAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor visitor(nodep, true, true, false, true); } // Destruct before checking
{ DeadVisitor visitor{nodep, true, true, false, true}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadAll", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
void V3Dead::deadifyAllScoped(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor visitor(nodep, true, true, true, true); } // Destruct before checking
{ DeadVisitor visitor{nodep, true, true, true, true}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadAllScoped", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

View File

@ -510,6 +510,6 @@ public:
void V3Delayed::delayedAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DelayedVisitor visitor(nodep); } // Destruct before checking
{ DelayedVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("delayed", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

View File

@ -29,6 +29,7 @@
#include "V3Global.h"
#include "V3Depth.h"
#include "V3Ast.h"
#include "V3UniqueNames.h"
#include <algorithm>
@ -39,11 +40,11 @@ private:
// NODE STATE
// STATE
AstNodeModule* m_modp = nullptr; // Current module
AstCFunc* m_cfuncp = nullptr; // Current block
AstNode* m_stmtp = nullptr; // Current statement
int m_depth = 0; // How deep in an expression
int m_maxdepth = 0; // Maximum depth in an expression
V3UniqueNames m_tempNames; // For generating unique temporary variable names
// METHODS
VL_DEBUG_FUNC; // Declare debug()
@ -51,18 +52,16 @@ private:
void createDeepTemp(AstNode* nodep) {
UINFO(6, " Deep " << nodep << endl);
// if (debug() >= 9) nodep->dumpTree(cout, "deep:");
const string newvarname = (string("__Vdeeptemp") + cvtToStr(m_modp->varNumGetInc()));
AstVar* varp
= new AstVar(nodep->fileline(), AstVarType::STMTTEMP, newvarname, nodep->dtypep());
AstVar* const varp = new AstVar{nodep->fileline(), AstVarType::STMTTEMP,
m_tempNames.get(nodep), nodep->dtypep()};
UASSERT_OBJ(m_cfuncp, nodep, "Deep expression not under a function");
m_cfuncp->addInitsp(varp);
// Replace node tree with reference to var
AstVarRef* newp = new AstVarRef(nodep->fileline(), varp, VAccess::READ);
AstVarRef* const newp = new AstVarRef{nodep->fileline(), varp, VAccess::READ};
nodep->replaceWith(newp);
// Put assignment before the referencing statement
AstAssign* assp = new AstAssign(
nodep->fileline(), new AstVarRef(nodep->fileline(), varp, VAccess::WRITE), nodep);
AstAssign* const assp = new AstAssign{
nodep->fileline(), new AstVarRef{nodep->fileline(), varp, VAccess::WRITE}, nodep};
AstNRelinker linker2;
m_stmtp->unlinkFrBack(&linker2);
assp->addNext(m_stmtp);
@ -70,21 +69,13 @@ private:
}
// VISITORS
virtual void visit(AstNodeModule* nodep) override {
UINFO(4, " MOD " << nodep << endl);
VL_RESTORER(m_modp);
{
m_modp = nodep;
m_cfuncp = nullptr;
iterateChildren(nodep);
}
}
virtual void visit(AstCFunc* nodep) override {
VL_RESTORER(m_cfuncp);
{
m_cfuncp = nodep;
m_depth = 0;
m_maxdepth = 0;
m_tempNames.reset();
iterateChildren(nodep);
}
}
@ -149,7 +140,10 @@ private:
public:
// CONSTRUCTORS
explicit DepthVisitor(AstNetlist* nodep) { iterate(nodep); }
explicit DepthVisitor(AstNetlist* nodep)
: m_tempNames{"__Vdeeptemp"} {
iterate(nodep);
}
virtual ~DepthVisitor() override = default;
};
@ -158,6 +152,6 @@ public:
void V3Depth::depthAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DepthVisitor visitor(nodep); } // Destruct before checking
{ DepthVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("depth", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
}

View File

@ -129,6 +129,6 @@ public:
void V3DepthBlock::depthBlockAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DepthBlockVisitor visitor(nodep); } // Destruct before checking
{ DepthBlockVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deepblock", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

View File

@ -73,6 +73,7 @@ private:
// module.
string descopedSelfPointer(const AstScope* scopep) {
UASSERT(scopep, "Var/Func not scoped");
UASSERT(!VN_IS(scopep->modp(), Class), "References to classes handled elsewhere");
// Static functions can't use relative references via 'this->'
const bool relativeRefOk = !m_funcp->isStatic();
@ -83,9 +84,7 @@ private:
if (relativeRefOk && scopep == m_scopep) {
return "this";
} else if (VN_IS(scopep->modp(), Class)) {
return "this";
} else if (!m_modSingleton && relativeRefOk && scopep->aboveScopep() == m_scopep
} else if (relativeRefOk && !m_modSingleton && scopep->aboveScopep() == m_scopep
&& VN_IS(scopep->modp(), Module)) {
// Reference to scope of instance directly under this module, can just "this->cell",
// which can potentially be V3Combined, but note this requires one extra pointer
@ -183,6 +182,10 @@ private:
}
// VISITORS
virtual void visit(AstNetlist* nodep) override {
nodep->dpiExportTriggerp(nullptr);
iterateChildren(nodep);
}
virtual void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
{
@ -215,20 +218,40 @@ private:
UASSERT_OBJ(m_scopep, nodep, "Node not under scope");
const AstVar* const varp = nodep->varScopep()->varp();
const AstScope* const scopep = nodep->varScopep()->scopep();
if (!varp->isFuncLocal()) { nodep->selfPointer(descopedSelfPointer(scopep)); }
if (varp->isFuncLocal()) {
// Reference to function locals need no self pointer
nodep->selfPointer("");
} else if (scopep->modp() == v3Global.rootp()->constPoolp()->modp()) {
// Reference to constant pool value need no self pointer
nodep->selfPointer("");
} else if (VN_IS(scopep->modp(), Class)) {
// Direct reference to class members are from within the class itself, references from
// outside the class must go via AstMemberSel
nodep->selfPointer("this");
} else {
nodep->selfPointer(descopedSelfPointer(scopep));
}
nodep->varScopep(nullptr);
UINFO(9, " refout " << nodep << endl);
}
virtual void visit(AstNodeCCall* nodep) override {
virtual void visit(AstCCall* nodep) override {
// UINFO(9, " " << nodep << endl);
iterateChildren(nodep);
// Convert the hierch name
UASSERT_OBJ(m_scopep, nodep, "Node not under scope");
const AstScope* const scopep = nodep->funcp()->scopep();
nodep->selfPointer(descopedSelfPointer(scopep));
if (VN_IS(scopep->modp(), Class)) {
// Direct call to class methods are from within the class itself, method calls from
// outside the class must go via AstCMethodCall
nodep->selfPointer("this");
} else {
nodep->selfPointer(descopedSelfPointer(scopep));
}
// Can't do this, as we may have more calls later
// nodep->funcp()->scopep(nullptr);
}
virtual void visit(AstCMethodCall* nodep) override { iterateChildren(nodep); }
virtual void visit(AstCNew* nodep) override { iterateChildren(nodep); }
virtual void visit(AstCFunc* nodep) override {
VL_RESTORER(m_funcp);
if (!nodep->user1()) {
@ -266,6 +289,6 @@ public:
void V3Descope::descopeAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DescopeVisitor visitor(nodep); } // Destruct before checking
{ DescopeVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("descope", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

View File

@ -44,7 +44,7 @@ V3DupFinder::iterator V3DupFinder::findDuplicate(AstNode* nodep, V3DupFinderUser
}
void V3DupFinder::dumpFile(const string& filename, bool tree) {
const std::unique_ptr<std::ofstream> logp(V3File::new_ofstream(filename));
const std::unique_ptr<std::ofstream> logp{V3File::new_ofstream(filename)};
if (logp->fail()) v3fatal("Can't write " << filename);
std::unordered_map<int, int> dist;

File diff suppressed because it is too large Load Diff

View File

@ -27,12 +27,12 @@
class V3EmitC final {
public:
static void emitc();
static void emitcConstPool();
static void emitcHeaders();
static void emitcImp();
static void emitcInlines();
static void emitcModel();
static void emitcSyms(bool dpiHdrOnly = false);
static void emitcTrace();
static void emitcFiles();
};

View File

@ -20,11 +20,26 @@
#include "V3EmitCBase.h"
#include "V3Task.h"
//######################################################################
// EmitCParentModule implementation
EmitCParentModule::EmitCParentModule() {
const auto setAll = [](AstNodeModule* modp) -> void {
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (VN_IS(nodep, CFunc) || VN_IS(nodep, Var)) { nodep->user4p(modp); }
}
};
for (AstNode* modp = v3Global.rootp()->modulesp(); modp; modp = modp->nextp()) {
setAll(VN_CAST(modp, NodeModule));
}
setAll(v3Global.rootp()->constPoolp()->modp());
}
//######################################################################
// EmitCBaseVisitor implementation
string EmitCBaseVisitor::funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp) {
modp = modp ? modp : VN_CAST(nodep->user4p(), NodeModule);
modp = modp ? modp : EmitCParentModule::get(nodep);
string name;
if (nodep->isConstructor()) {
name += prefixNameProtect(modp);
@ -54,8 +69,7 @@ string EmitCBaseVisitor::cFuncArgs(const AstCFunc* nodep) {
string args;
if (nodep->isLoose() && !nodep->isStatic()) {
if (nodep->isConst().trueKnown()) args += "const ";
AstNodeModule* modp = VN_CAST(nodep->user4p(), NodeModule);
args += prefixNameProtect(modp);
args += prefixNameProtect(EmitCParentModule::get(nodep));
args += "* vlSelf";
}
if (!nodep->argTypes().empty()) {
@ -82,6 +96,7 @@ string EmitCBaseVisitor::cFuncArgs(const AstCFunc* nodep) {
void EmitCBaseVisitor::emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp,
bool withScope) {
if (funcp->slow()) puts("VL_ATTR_COLD ");
if (!funcp->isConstructor() && !funcp->isDestructor()) {
puts(funcp->rtnTypeVoid());
puts(" ");
@ -109,12 +124,11 @@ void EmitCBaseVisitor::emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule*
puts("virtual ");
}
emitCFuncHeader(funcp, modp, /* withScope: */ false);
if (funcp->slow()) puts(" VL_ATTR_COLD");
puts(";\n");
if (!funcp->ifdef().empty()) puts("#endif // " + funcp->ifdef() + "\n");
}
void EmitCBaseVisitor::emitVarDecl(const AstVar* nodep, const string& prefixIfImp, bool asRef) {
void EmitCBaseVisitor::emitVarDecl(const AstVar* nodep, bool asRef) {
const AstBasicDType* const basicp = nodep->basicp();
bool refNeedParens = VN_IS(nodep->dtypeSkipRefp(), UnpackArrayDType);
@ -199,12 +213,12 @@ void EmitCBaseVisitor::emitVarDecl(const AstVar* nodep, const string& prefixIfIm
&& name.substr(name.size() - suffix.size()) == suffix;
if (beStatic) puts("static VL_THREAD_LOCAL ");
}
puts(nodep->vlArgType(true, false, false, prefixIfImp, asRef));
puts(nodep->vlArgType(true, false, false, "", asRef));
puts(";\n");
}
}
void EmitCBaseVisitor::emitModCUse(AstNodeModule* modp, VUseType useType) {
void EmitCBaseVisitor::emitModCUse(const AstNodeModule* modp, VUseType useType) {
string nl;
for (AstNode* itemp = modp->stmtsp(); itemp; itemp = itemp->nextp()) {
if (AstCUse* usep = VN_CAST(itemp, CUse)) {
@ -221,3 +235,24 @@ void EmitCBaseVisitor::emitModCUse(AstNodeModule* modp, VUseType useType) {
}
puts(nl);
}
void EmitCBaseVisitor::emitTextSection(const AstNodeModule* modp, AstType type) {
int last_line = -999;
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstNodeText* textp = VN_CAST(nodep, NodeText)) {
if (nodep->type() == type) {
if (last_line != nodep->fileline()->lineno()) {
if (last_line < 0) {
puts("\n//*** Below code from `systemc in Verilog file\n");
}
putsDecoration(
ifNoProtect("// From `systemc at " + nodep->fileline()->ascii() + "\n"));
last_line = nodep->fileline()->lineno();
}
ofp()->putsNoTracking(textp->text());
last_line++;
}
}
}
if (last_line > 0) puts("//*** Above code from `systemc in Verilog file\n\n");
}

View File

@ -27,6 +27,24 @@
#include <cstdarg>
#include <cmath>
//######################################################################
// Set user4p in all CFunc and Var to point to the containing AstNodeModule
class EmitCParentModule final {
// NODE STATE
// AstFunc::user4p() AstNodeModule* Parent module pointer
// AstVar::user4p() AstNodeModule* Parent module pointer
AstUser4InUse user4InUse;
public:
EmitCParentModule();
VL_UNCOPYABLE(EmitCParentModule);
static const AstNodeModule* get(const AstNode* nodep) {
return VN_CAST_CONST(nodep->user4p(), NodeModule);
}
};
//######################################################################
// Base Visitor class -- holds output file pointer
@ -71,16 +89,24 @@ public:
return v3Global.opt.prefix();
}
static bool isConstPoolMod(AstNode* modp) {
static bool isConstPoolMod(const AstNode* modp) {
return modp == v3Global.rootp()->constPoolp()->modp();
}
static bool isAnonOk(const AstVar* varp) {
return v3Global.opt.compLimitMembers() != 0 // Enabled
&& !varp->isStatic() // Not a static variable
&& !varp->isSc() // Aggregates can't be anon
&& (varp->basicp() && !varp->basicp()->isOpaque()); // Aggregates can't be anon
}
static AstCFile* newCFile(const string& filename, bool slow, bool source);
string cFuncArgs(const AstCFunc* nodep);
void emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp, bool withScope);
void emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp, bool cLinkage = false);
void emitVarDecl(const AstVar* nodep, const string& prefixIfImp, bool asRef = false);
void emitModCUse(AstNodeModule* modp, VUseType useType);
void emitVarDecl(const AstVar* nodep, bool asRef = false);
void emitModCUse(const AstNodeModule* modp, VUseType useType);
void emitTextSection(const AstNodeModule* modp, AstType type);
// CONSTRUCTORS
EmitCBaseVisitor() = default;
@ -96,7 +122,7 @@ private:
int m_count = 0; // Number of statements
// VISITORS
virtual void visit(AstNode* nodep) override {
m_count++;
++m_count;
iterateChildren(nodep);
}

122
src/V3EmitCConstInit.h Normal file
View File

@ -0,0 +1,122 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Emit C++ for tree
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3EmitCBase.h"
#include "V3Error.h"
#include <cinttypes>
//######################################################################
// Emitter that can emit constant initializer expressions
class EmitCConstInit VL_NOT_FINAL : public EmitCBaseVisitor {
// MEMBERS
bool m_inUnpacked = false;
uint32_t m_unpackedWord = 0;
// METHODS
VL_DEBUG_FUNC; // Declare debug()
protected:
// VISITORS
virtual void visit(AstInitArray* nodep) override {
const AstUnpackArrayDType* const dtypep
= VN_CAST(nodep->dtypep()->skipRefp(), UnpackArrayDType);
UASSERT_OBJ(dtypep, nodep, "Array initializer has non-array dtype");
const uint32_t size = dtypep->elementsConst();
const uint32_t elemBytes = dtypep->subDTypep()->widthTotalBytes();
const uint32_t tabMod = dtypep->subDTypep()->isString() ? 1 // String
: elemBytes <= 2 ? 8 // CData, SData
: elemBytes <= 4 ? 4 // IData
: elemBytes <= 8 ? 2 // QData
: 1;
VL_RESTORER(m_inUnpacked);
VL_RESTORER(m_unpackedWord);
m_inUnpacked = true;
// Note the double {{ initializer. The first { starts the initializer of the VlUnpacked,
// and the second starts the initializer of m_storage within the VlUnpacked.
puts("{");
ofp()->putsNoTracking("{");
puts("\n");
for (uint32_t n = 0; n < size; ++n) {
m_unpackedWord = n;
if (n) puts((n % tabMod) ? ", " : ",\n");
iterate(nodep->getIndexDefaultedValuep(n));
}
puts("\n");
puts("}");
ofp()->putsNoTracking("}");
}
virtual void visit(AstInitItem* nodep) override { // LCOV_EXCL_START
nodep->v3fatal("Handled by AstInitArray");
} // LCOV_EXCL_STOP
virtual void visit(AstConst* nodep) override {
const V3Number& num = nodep->num();
UASSERT_OBJ(!num.isFourState(), nodep, "4-state value in constant pool");
AstNodeDType* const dtypep = nodep->dtypep();
if (num.isString()) {
// Note: putsQuoted does not track indentation, so we use this instead
puts("\"");
puts(num.toString());
puts("\"");
} else if (dtypep->isWide()) {
const uint32_t size = dtypep->widthWords();
// Note the double {{ initializer. The first { starts the initializer of the VlWide,
// and the second starts the initializer of m_storage within the VlWide.
puts("{");
ofp()->putsNoTracking("{");
if (m_inUnpacked) puts(" // VlWide " + cvtToStr(m_unpackedWord));
puts("\n");
for (uint32_t n = 0; n < size; ++n) {
if (n) puts((n % 4) ? ", " : ",\n");
ofp()->printf("0x%08" PRIx32, num.edataWord(n));
}
puts("\n");
puts("}");
ofp()->putsNoTracking("}");
} else if (dtypep->isDouble()) {
const double dnum = num.toDouble();
const char* const fmt
= !m_inUnpacked && (static_cast<int>(dnum) == dnum && -1000 < dnum && dnum < 1000)
? "%3.1f" // Force decimal point
: "%.17e"; // %e always yields a float literal
ofp()->printf(fmt, dnum);
} else if (dtypep->isQuad()) {
const uint64_t qnum = static_cast<uint64_t>(num.toUQuad());
const char* const fmt
= !m_inUnpacked && (qnum < 10) ? ("%" PRIx64 "ULL") : ("0x%016" PRIx64 "ULL");
ofp()->printf(fmt, qnum);
} else {
const uint32_t unum = num.toUInt();
const char* const fmt = !m_inUnpacked && (unum < 10) ? ("%" PRIu32 "U")
: (dtypep->widthMin() > 16) ? ("0x%08" PRIx32 "U")
: (dtypep->widthMin() > 8) ? ("0x%04" PRIx32 "U")
: ("0x%02" PRIx32 "U");
ofp()->printf(fmt, unum);
}
}
// Default
virtual void visit(AstNode* nodep) override { // LCOV_EXCL_START
nodep->v3fatalSrc("Unknown node type reached EmitCConstInit: " << nodep->prettyTypeName());
} // LCOV_EXCL_STOP
};

View File

@ -18,7 +18,7 @@
#include "verilatedos.h"
#include "V3EmitC.h"
#include "V3EmitCBase.h"
#include "V3EmitCConstInit.h"
#include "V3File.h"
#include "V3Global.h"
#include "V3String.h"
@ -30,10 +30,8 @@
//######################################################################
// Const pool emitter
class EmitCConstPool final : EmitCBaseVisitor {
class EmitCConstPool final : public EmitCConstInit {
// MEMBERS
bool m_inUnpacked = false;
uint32_t m_unpackedWord = 0;
uint32_t m_outFileCount = 0;
int m_outFileSize = 0;
VDouble0 m_tablesEmitted;
@ -51,7 +49,7 @@ class EmitCConstPool final : EmitCBaseVisitor {
ofp->puts("// DESCRIPTION: Verilator output: Constant pool\n");
ofp->puts("//\n");
ofp->puts("\n");
ofp->puts("#include \"verilated_heavy.h\"\n");
ofp->puts("#include \"verilated.h\"\n");
return ofp;
}
@ -102,77 +100,9 @@ class EmitCConstPool final : EmitCBaseVisitor {
}
// VISITORS
virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
virtual void visit(AstInitArray* nodep) override {
const AstUnpackArrayDType* const dtypep
= VN_CAST(nodep->dtypep()->skipRefp(), UnpackArrayDType);
UASSERT_OBJ(dtypep, nodep, "Array initializer has non-array dtype");
const uint32_t size = dtypep->elementsConst();
const uint32_t elemBytes = dtypep->subDTypep()->widthTotalBytes();
const uint32_t tabMod = dtypep->subDTypep()->isString() ? 1 // String
: elemBytes <= 2 ? 8 // CData, SData
: elemBytes <= 4 ? 4 // IData
: elemBytes <= 8 ? 2 // QData
: 1;
VL_RESTORER(m_inUnpacked);
VL_RESTORER(m_unpackedWord);
m_inUnpacked = true;
// Note the double {{ initializer. The first { starts the initializer of the VlUnpacked,
// and the second starts the initializer of m_storage within the VlUnpacked.
puts("{");
ofp()->putsNoTracking("{");
puts("\n");
for (uint32_t n = 0; n < size; ++n) {
m_unpackedWord = n;
if (n) puts(n % tabMod ? ", " : ",\n");
iterate(nodep->getIndexDefaultedValuep(n));
}
puts("\n");
puts("}");
ofp()->putsNoTracking("}");
}
virtual void visit(AstInitItem* nodep) override { // LCOV_EXCL_START
nodep->v3fatal("Handled by AstInitArray");
} // LCOV_EXCL_END
virtual void visit(AstConst* nodep) override {
const V3Number& num = nodep->num();
UASSERT_OBJ(!num.isFourState(), nodep, "4-state value in constant pool");
AstNodeDType* const dtypep = nodep->dtypep();
m_outFileSize += 1;
if (num.isString()) {
// Note: putsQuoted does not track indentation, so we use this instead
puts("\"");
puts(num.toString());
puts("\"");
m_outFileSize += 9;
} else if (dtypep->isWide()) {
const uint32_t size = dtypep->widthWords();
m_outFileSize += size - 1;
// Note the double {{ initializer. The first { starts the initializer of the VlWide,
// and the second starts the initializer of m_storage within the VlWide.
puts("{");
ofp()->putsNoTracking("{");
if (m_inUnpacked) puts(" // VlWide " + cvtToStr(m_unpackedWord));
puts("\n");
for (uint32_t n = 0; n < size; ++n) {
if (n) puts(n % 4 ? ", " : ",\n");
ofp()->printf("0x%08" PRIx32, num.edataWord(n));
}
puts("\n");
puts("}");
ofp()->putsNoTracking("}");
} else if (dtypep->isQuad()) {
ofp()->printf("0x%016" PRIx64, static_cast<uint64_t>(num.toUQuad()));
} else if (dtypep->widthMin() > 16) {
ofp()->printf("0x%08" PRIx32, num.toUInt());
} else if (dtypep->widthMin() > 8) {
ofp()->printf("0x%04" PRIx32, num.toUInt());
} else {
ofp()->printf("0x%02" PRIx32, num.toUInt());
}
m_outFileSize += nodep->num().isString() ? 10 : nodep->isWide() ? nodep->widthWords() : 1;
EmitCConstInit::visit(nodep);
}
public:

View File

@ -28,90 +28,9 @@
// We use a static char array in VL_VALUE_STRING
constexpr int VL_VALUE_STRING_MAX_WIDTH = 8192;
//######################################################################
// Establish mtask variable sort order in mtasks mode
class EmitVarTspSorter final : public V3TSP::TspStateBase {
private:
// MEMBERS
const MTaskIdSet& m_mtaskIds; // Mtask we're ordering
static unsigned s_serialNext; // Unique ID to establish serial order
unsigned m_serial; // Serial ordering
public:
// CONSTRUCTORS
explicit EmitVarTspSorter(const MTaskIdSet& mtaskIds)
: m_mtaskIds(mtaskIds) { // Cannot be {} or GCC 4.8 false warning
m_serial = ++s_serialNext; // Cannot be ()/{} or GCC 4.8 false warning
}
virtual ~EmitVarTspSorter() = default;
// METHODS
virtual bool operator<(const TspStateBase& other) const override {
return operator<(dynamic_cast<const EmitVarTspSorter&>(other));
}
bool operator<(const EmitVarTspSorter& other) const { return m_serial < other.m_serial; }
const MTaskIdSet& mtaskIds() const { return m_mtaskIds; }
virtual int cost(const TspStateBase* otherp) const override {
return cost(dynamic_cast<const EmitVarTspSorter*>(otherp));
}
virtual int cost(const EmitVarTspSorter* otherp) const {
int cost = diffs(m_mtaskIds, otherp->m_mtaskIds);
cost += diffs(otherp->m_mtaskIds, m_mtaskIds);
return cost;
}
// Returns the number of elements in set_a that don't appear in set_b
static int diffs(const MTaskIdSet& set_a, const MTaskIdSet& set_b) {
int diffs = 0;
for (int i : set_a) {
if (set_b.find(i) == set_b.end()) ++diffs;
}
return diffs;
}
};
unsigned EmitVarTspSorter::s_serialNext = 0;
//######################################################################
// EmitCFunc
void EmitCFunc::emitCtorSep(bool* firstp) {
if (*firstp) {
puts(" : ");
*firstp = false;
} else {
puts(", ");
}
if (ofp()->exceededWidth()) puts("\n ");
}
void EmitCFunc::emitVarCtors(bool* firstp) {
if (!m_ctorVarsVec.empty()) {
ofp()->indentInc();
if (*firstp) puts("\n");
for (const AstVar* varp : m_ctorVarsVec) {
const AstBasicDType* const dtypep = VN_CAST(varp->dtypeSkipRefp(), BasicDType);
if (!dtypep) {
puts("// Skipping array: ");
puts(varp->nameProtect());
puts("\n");
} else if (dtypep->keyword().isMTaskState()) {
emitCtorSep(firstp);
puts(varp->nameProtect());
puts("(");
iterate(varp->valuep());
puts(")");
} else {
emitCtorSep(firstp);
puts(varp->nameProtect());
puts("(");
putsQuoted(varp->nameProtect());
puts(")");
}
}
puts("\n");
ofp()->indentDec();
}
}
bool EmitCFunc::emitSimpleOk(AstNodeMath* nodep) {
// Can we put out a simple (A + B) instead of VL_ADD_III(A,B)?
if (nodep->emitSimpleOperator() == "") return false;
@ -514,199 +433,12 @@ void EmitCFunc::displayNode(AstNode* nodep, AstScopeName* scopenamep, const stri
displayEmit(nodep, isScan);
}
void EmitCFunc::emitVarList(AstNode* firstp, EisWhich which, const string& prefixIfImp,
string& sectionr) {
// Put out a list of signal declarations
// in order of 0:clocks, 1:vluint8, 2:vluint16, 4:vluint32, 5:vluint64, 6:wide, 7:arrays
// This aids cache packing and locality
//
// Largest->smallest reduces the number of pad variables. Also
// experimented with alternating between large->small and small->large
// on successive Mtask groups, but then when a new mtask gets added may
// cause a huge delta.
//
// TODO: Move this sort to an earlier visitor stage.
VarSortMap varAnonMap;
VarSortMap varNonanonMap;
for (int isstatic = 1; isstatic >= 0; isstatic--) {
if (prefixIfImp != "" && !isstatic) continue;
for (AstNode* nodep = firstp; nodep; nodep = nodep->nextp()) {
if (const AstVar* varp = VN_CAST(nodep, Var)) {
bool doit = true;
switch (which) {
case EVL_CLASS_IO: doit = varp->isIO(); break;
case EVL_CLASS_SIG:
doit = ((varp->isSignal() || varp->isClassMember()) && !varp->isIO());
break;
case EVL_CLASS_TEMP: doit = (varp->isTemp() && !varp->isIO()); break;
case EVL_CLASS_PAR:
doit = (varp->isParam() && !VN_IS(varp->valuep(), Const));
break;
case EVL_CLASS_ALL: doit = true; break;
case EVL_FUNC_ALL: doit = true; break;
default: v3fatalSrc("Bad Case");
}
if (varp->isStatic() ? !isstatic : isstatic) doit = false;
if (doit) {
const int sigbytes = varp->dtypeSkipRefp()->widthAlignBytes();
int sortbytes = 9;
if (varp->isUsedClock() && varp->widthMin() == 1) {
sortbytes = 0;
} else if (VN_IS(varp->dtypeSkipRefp(), UnpackArrayDType)) {
sortbytes = 8;
} else if (varp->basicp() && varp->basicp()->isOpaque()) {
sortbytes = 7;
} else if (varp->isScBv() || varp->isScBigUint()) {
sortbytes = 6;
} else if (sigbytes == 8) {
sortbytes = 5;
} else if (sigbytes == 4) {
sortbytes = 4;
} else if (sigbytes == 2) {
sortbytes = 2;
} else if (sigbytes == 1) {
sortbytes = 1;
}
const bool anonOk
= (v3Global.opt.compLimitMembers() != 0 // Enabled
&& !varp->isStatic() && !varp->isIO() // Confusing to user
&& !varp->isSc() // Aggregates can't be anon
&& (varp->basicp()
&& !varp->basicp()->isOpaque()) // Aggregates can't be anon
&& which != EVL_FUNC_ALL); // Anon not legal in funcs, and gcc
// bug free there anyhow
if (anonOk) {
varAnonMap[sortbytes].push_back(varp);
} else {
varNonanonMap[sortbytes].push_back(varp);
}
}
}
}
}
if (!varAnonMap.empty() || !varNonanonMap.empty()) {
if (!sectionr.empty()) {
puts(sectionr);
sectionr = "";
}
VarVec anons;
VarVec nonanons;
emitVarSort(varAnonMap, &anons);
emitVarSort(varNonanonMap, &nonanons);
emitSortedVarList(anons, nonanons, prefixIfImp);
}
}
void EmitCFunc::emitVarSort(const VarSortMap& vmap, VarVec* sortedp) {
UASSERT(sortedp->empty(), "Sorted should be initially empty");
if (!v3Global.opt.mtasks()) {
// Plain old serial mode. Sort by size, from small to large,
// to optimize for both packing and small offsets in code.
for (const auto& itr : vmap) {
for (VarVec::const_iterator jt = itr.second.begin(); jt != itr.second.end(); ++jt) {
sortedp->push_back(*jt);
}
}
return;
}
// MacroTask mode. Sort by MTask-affinity group first, size second.
using MTaskVarSortMap = std::map<const MTaskIdSet, VarSortMap>;
MTaskVarSortMap m2v;
for (VarSortMap::const_iterator it = vmap.begin(); it != vmap.end(); ++it) {
const int size_class = it->first;
const VarVec& vec = it->second;
for (const AstVar* varp : vec) { m2v[varp->mtaskIds()][size_class].push_back(varp); }
}
// Create a TSP sort state for each MTaskIdSet footprint
V3TSP::StateVec states;
for (MTaskVarSortMap::iterator it = m2v.begin(); it != m2v.end(); ++it) {
states.push_back(new EmitVarTspSorter(it->first));
}
// Do the TSP sort
V3TSP::StateVec sorted_states;
V3TSP::tspSort(states, &sorted_states);
for (V3TSP::StateVec::iterator it = sorted_states.begin(); it != sorted_states.end(); ++it) {
const EmitVarTspSorter* statep = dynamic_cast<const EmitVarTspSorter*>(*it);
const VarSortMap& localVmap = m2v[statep->mtaskIds()];
// use rbegin/rend to sort size large->small
for (VarSortMap::const_reverse_iterator jt = localVmap.rbegin(); jt != localVmap.rend();
++jt) {
const VarVec& vec = jt->second;
for (VarVec::const_iterator kt = vec.begin(); kt != vec.end(); ++kt) {
sortedp->push_back(*kt);
}
}
VL_DO_DANGLING(delete statep, statep);
}
}
void EmitCFunc::emitSortedVarList(const VarVec& anons, const VarVec& nonanons,
const string& prefixIfImp) {
string curVarCmt;
// Output anons
{
const int anonMembers = anons.size();
const int lim = v3Global.opt.compLimitMembers();
int anonL3s = 1;
int anonL2s = 1;
int anonL1s = 1;
if (anonMembers > (lim * lim * lim)) {
anonL3s = (anonMembers + (lim * lim * lim) - 1) / (lim * lim * lim);
anonL2s = lim;
anonL1s = lim;
} else if (anonMembers > (lim * lim)) {
anonL2s = (anonMembers + (lim * lim) - 1) / (lim * lim);
anonL1s = lim;
} else if (anonMembers > lim) {
anonL1s = (anonMembers + lim - 1) / lim;
}
if (anonL1s != 1)
puts("// Anonymous structures to workaround compiler member-count bugs\n");
auto it = anons.cbegin();
for (int l3 = 0; l3 < anonL3s && it != anons.cend(); ++l3) {
if (anonL3s != 1) puts("struct {\n");
for (int l2 = 0; l2 < anonL2s && it != anons.cend(); ++l2) {
if (anonL2s != 1) puts("struct {\n");
for (int l1 = 0; l1 < anonL1s && it != anons.cend(); ++l1) {
if (anonL1s != 1) puts("struct {\n");
for (int l0 = 0; l0 < lim && it != anons.cend(); ++l0) {
const AstVar* varp = *it;
emitVarDecl(varp, prefixIfImp);
++it;
}
if (anonL1s != 1) puts("};\n");
}
if (anonL2s != 1) puts("};\n");
}
if (anonL3s != 1) puts("};\n");
}
// Leftovers, just in case off by one error somewhere above
for (; it != anons.end(); ++it) {
const AstVar* varp = *it;
emitVarDecl(varp, prefixIfImp);
}
}
// Output nonanons
for (const AstVar* varp : nonanons) {
if (varp->isIO() && varp->isSc()) { m_ctorVarsVec.push_back(varp); }
AstBasicDType* const basicp = varp->basicp();
if (basicp && basicp->keyword().isMTaskState()) { m_ctorVarsVec.push_back(varp); }
emitVarDecl(varp, prefixIfImp);
}
}
void EmitCFunc::emitCCallArgs(AstNodeCCall* nodep) {
void EmitCFunc::emitCCallArgs(const AstNodeCCall* nodep, const string& selfPointer) {
puts("(");
bool comma = false;
if (nodep->funcp()->isLoose() && !nodep->funcp()->isStatic()) {
UASSERT_OBJ(!nodep->selfPointer().empty(), nodep,
"Call to loose method without self pointer");
puts(nodep->selfPointerProtect(m_useSelfForThis));
UASSERT_OBJ(!selfPointer.empty(), nodep, "Call to loose method without self pointer");
puts(selfPointer);
comma = true;
}
if (!nodep->argTypes().empty()) {
@ -719,6 +451,12 @@ void EmitCFunc::emitCCallArgs(AstNodeCCall* nodep) {
iterate(subnodep);
comma = true;
}
if (VN_IS(nodep->backp(), NodeMath) || VN_IS(nodep->backp(), CReturn)) {
// We should have a separate CCall for math and statement usage, but...
puts(")");
} else {
puts(");\n");
}
}
void EmitCFunc::emitDereference(const string& pointer) {
@ -1034,7 +772,7 @@ void EmitCFunc::emitChangeDet() {
puts("QData __req = false; // Logically a bool\n"); // But not because it results in
// faster code
bool gotOne = false;
for (AstChangeDet* changep : m_blkChangeDetVec) {
for (AstChangeDet* const changep : m_blkChangeDetVec) {
if (changep->lhsp()) {
if (!gotOne) { // Not a clocked block
puts("__req |= (");

View File

@ -21,7 +21,7 @@
#include "verilatedos.h"
#include "V3Global.h"
#include "V3EmitCBase.h"
#include "V3EmitCConstInit.h"
#include <algorithm>
#include <map>
@ -53,8 +53,7 @@ class EmitCLazyDecls final : public AstNVisitor {
// Already declared manually
if (m_emittedManually.count(funcp->nameProtect())) return;
// Needs lazy declaration, emit one
m_emitter.emitCFuncDecl(funcp, VN_CAST_CONST(funcp->user4p(), NodeModule),
funcp->dpiImportPrototype());
m_emitter.emitCFuncDecl(funcp, EmitCParentModule::get(funcp), funcp->dpiImportPrototype());
m_needsBlankLine = true;
}
@ -82,7 +81,9 @@ class EmitCLazyDecls final : public AstNVisitor {
virtual void visit(AstVarRef* nodep) override {
AstVar* const varp = nodep->varp();
// Only constant pool symbols are lazy declared for now ...
if (EmitCBaseVisitor::isConstPoolMod(varp->user4p())) { lazyDeclareConstPoolVar(varp); }
if (EmitCBaseVisitor::isConstPoolMod(EmitCParentModule::get(varp))) {
lazyDeclareConstPoolVar(varp);
}
}
virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
@ -90,7 +91,7 @@ class EmitCLazyDecls final : public AstNVisitor {
VL_DEBUG_FUNC;
public:
EmitCLazyDecls(EmitCBaseVisitor& emitter)
explicit EmitCLazyDecls(EmitCBaseVisitor& emitter)
: m_emitter(emitter) {}
void emit(AstNode* nodep) {
m_needsBlankLine = false;
@ -112,39 +113,31 @@ public:
//######################################################################
// Emit statements and math operators
class EmitCFunc VL_NOT_FINAL : public EmitCBaseVisitor {
class EmitCFunc VL_NOT_FINAL : public EmitCConstInit {
private:
using VarVec = std::vector<const AstVar*>;
using VarSortMap = std::map<int, VarVec>; // Map size class to VarVec
bool m_suppressSemi;
AstVarRef* m_wideTempRefp; // Variable that _WW macros should be setting
VarVec m_ctorVarsVec; // All variables in constructor order
int m_labelNum; // Next label number
int m_splitSize; // # of cfunc nodes placed into output file
int m_splitFilenum; // File number being created, 0 = primary
bool m_inUC = false; // Inside an AstUCStmt or AstUCMath
std::vector<AstChangeDet*> m_blkChangeDetVec; // All encountered changes in block
bool m_emitConstInit = false; // Emitting constant initializer
protected:
EmitCLazyDecls m_lazyDecls; // Visitor for emitting lazy declarations
bool m_useSelfForThis = false; // Replace "this" with "vlSelf"
AstNodeModule* m_modp = nullptr; // Current module being emitted
const AstNodeModule* m_modp = nullptr; // Current module being emitted
AstCFunc* m_cfuncp = nullptr; // Current function being emitted
public:
// METHODS
VL_DEBUG_FUNC; // Declare debug()
// ACCESSORS
int splitFilenumInc() {
m_splitSize = 0;
return ++m_splitFilenum;
}
int splitSize() const { return m_splitSize; }
void splitSizeInc(int count) { m_splitSize += count; }
void splitSizeInc(AstNode* nodep) { splitSizeInc(EmitCBaseCounterVisitor(nodep).count()); }
void splitSizeReset() { m_splitSize = 0; }
bool splitNeeded() const {
return v3Global.opt.outputSplit() && splitSize() >= v3Global.opt.outputSplit();
return v3Global.opt.outputSplit() && m_splitSize >= v3Global.opt.outputSplit();
}
// METHODS
@ -154,19 +147,6 @@ public:
void displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const string& vfmt, bool ignore,
char fmtLetter);
enum EisWhich : uint8_t {
EVL_CLASS_IO,
EVL_CLASS_SIG,
EVL_CLASS_TEMP,
EVL_CLASS_PAR,
EVL_CLASS_ALL,
EVL_FUNC_ALL
};
void emitVarList(AstNode* firstp, EisWhich which, const string& prefixIfImp, string& sectionr);
static void emitVarSort(const VarSortMap& vmap, VarVec* sortedp);
void emitSortedVarList(const VarVec& anons, const VarVec& nonanons, const string& prefixIfImp);
void emitVarCtors(bool* firstp);
void emitCtorSep(bool* firstp);
bool emitSimpleOk(AstNodeMath* nodep);
void emitIQW(AstNode* nodep) {
// Other abbrevs: "C"har, "S"hort, "F"loat, "D"ouble, stri"N"g
@ -188,7 +168,7 @@ public:
}
void emitOpName(AstNode* nodep, const string& format, AstNode* lhsp, AstNode* rhsp,
AstNode* thsp);
void emitCCallArgs(AstNodeCCall* nodep);
void emitCCallArgs(const AstNodeCCall* nodep, const string& selfPointer);
void emitDereference(const string& pointer);
void emitCvtPackStr(AstNode* nodep);
void emitCvtWideArray(AstNode* nodep, AstNode* fromp);
@ -199,10 +179,20 @@ public:
AstNodeDType* dtypep, int depth, const string& suffix);
void doubleOrDetect(AstChangeDet* changep, bool& gotOne);
void emitChangeDet();
void emitConstInit(AstNode* initp) {
// We should refactor emit to produce output into a provided buffer, not go through members
// variables. That way we could just invoke the appropriate emitter as needed.
VL_RESTORER(m_emitConstInit);
m_emitConstInit = true;
iterate(initp);
}
// VISITORS
using EmitCConstInit::visit;
virtual void visit(AstCFunc* nodep) override {
VL_RESTORER(m_useSelfForThis);
VL_RESTORER(m_cfuncp);
m_cfuncp = nodep;
m_blkChangeDetVec.clear();
@ -238,25 +228,28 @@ public:
puts(nodep->isLoose() ? "__" : "::");
puts(nodep->nameProtect() + "\\n\"); );\n");
if (nodep->initsp()) putsDecoration("// Variables\n");
for (AstNode* subnodep = nodep->argsp(); subnodep; subnodep = subnodep->nextp()) {
if (AstVar* varp = VN_CAST(subnodep, Var)) {
if (varp->isFuncReturn()) emitVarDecl(varp, "");
if (varp->isFuncReturn()) emitVarDecl(varp);
}
}
string section;
emitVarList(nodep->initsp(), EVL_FUNC_ALL, "", section /*ref*/);
emitVarList(nodep->stmtsp(), EVL_FUNC_ALL, "", section /*ref*/);
iterateAndNextNull(nodep->initsp());
if (nodep->initsp()) {
putsDecoration("// Init\n");
iterateAndNextNull(nodep->initsp());
}
if (nodep->stmtsp()) {
putsDecoration("// Body\n");
iterateAndNextNull(nodep->stmtsp());
}
if (nodep->stmtsp()) putsDecoration("// Body\n");
iterateAndNextNull(nodep->stmtsp());
if (!m_blkChangeDetVec.empty()) emitChangeDet();
if (nodep->finalsp()) putsDecoration("// Final\n");
iterateAndNextNull(nodep->finalsp());
//
if (nodep->finalsp()) {
putsDecoration("// Final\n");
iterateAndNextNull(nodep->finalsp());
}
if (!m_blkChangeDetVec.empty()) puts("return __req;\n");
@ -264,6 +257,11 @@ public:
if (nodep->ifdef() != "") puts("#endif // " + nodep->ifdef() + "\n");
}
virtual void visit(AstVar* nodep) override {
UASSERT_OBJ(m_cfuncp, nodep, "Cannot emit non-local variable");
emitVarDecl(nodep);
}
virtual void visit(AstNodeAssign* nodep) override {
bool paren = true;
bool decind = false;
@ -344,7 +342,7 @@ public:
iterateAndNextNull(nodep->rhsp());
if (paren) puts(")");
if (decind) ofp()->blockDec();
if (!m_suppressSemi) puts(";\n");
puts(";\n");
}
virtual void visit(AstAlwaysPublic*) override {}
virtual void visit(AstAssocSel* nodep) override {
@ -359,24 +357,19 @@ public:
}
puts(")");
}
virtual void visit(AstNodeCCall* nodep) override {
virtual void visit(AstCCall* nodep) override {
const AstCFunc* const funcp = nodep->funcp();
if (AstCMethodCall* ccallp = VN_CAST(nodep, CMethodCall)) {
UASSERT_OBJ(!funcp->isLoose(), nodep, "Loose method called via AstCMethodCall");
// make this a Ast type for future opt
iterate(ccallp->fromp());
putbs("->");
puts(funcp->nameProtect());
} else if (funcp->dpiImportPrototype()) {
const AstNodeModule* const funcModp = EmitCParentModule::get(funcp);
if (funcp->dpiImportPrototype()) {
// Calling DPI import
puts(funcp->name());
} else if (funcp->isProperMethod() && funcp->isStatic()) {
// Call static method via the containing class
puts(prefixNameProtect(funcp->user4p()) + "::");
puts(prefixNameProtect(funcModp) + "::");
puts(funcp->nameProtect());
} else if (VN_IS(funcp->user4p(), Class) && funcp->user4p() != m_modp) {
} else if (VN_IS(funcModp, Class) && funcModp != m_modp) {
// Calling superclass method
puts(prefixNameProtect(funcp->user4p()) + "::");
puts(prefixNameProtect(funcModp) + "::");
puts(funcp->nameProtect());
} else if (funcp->isLoose()) {
// Calling loose method
@ -388,14 +381,22 @@ public:
}
puts(funcp->nameProtect());
}
puts("(");
emitCCallArgs(nodep);
if (VN_IS(nodep->backp(), NodeMath) || VN_IS(nodep->backp(), CReturn)) {
// We should have a separate CCall for math and statement usage, but...
puts(")");
} else {
puts(");\n");
}
emitCCallArgs(nodep, nodep->selfPointerProtect(m_useSelfForThis));
}
virtual void visit(AstCMethodCall* nodep) override {
const AstCFunc* const funcp = nodep->funcp();
UASSERT_OBJ(!funcp->isLoose(), nodep, "Loose method called via AstCMethodCall");
iterate(nodep->fromp());
putbs("->");
puts(funcp->nameProtect());
emitCCallArgs(nodep, "");
}
virtual void visit(AstCNew* nodep) override {
puts("std::make_shared<" + prefixNameProtect(nodep->dtypep()) + ">(");
puts("vlSymsp"); // TODO make this part of argsp, and eliminate when unnecessary
if (nodep->argsp()) puts(", ");
iterateAndNextNull(nodep->argsp());
puts(")");
}
virtual void visit(AstCMethodHard* nodep) override {
iterate(nodep->fromp());
@ -594,11 +595,11 @@ public:
}
void checkMaxWords(AstNode* nodep) {
if (nodep->widthWords() > VL_TO_STRING_MAX_WORDS) {
if (nodep->widthWords() > VL_VALUE_STRING_MAX_WORDS) {
nodep->v3error(
"String of "
<< nodep->width()
<< " bits exceeds hardcoded limit VL_TO_STRING_MAX_WORDS in verilatedos.h");
<< " bits exceeds hardcoded limit VL_VALUE_STRING_MAX_WORDS in verilatedos.h");
}
}
virtual void visit(AstFOpen* nodep) override {
@ -974,8 +975,13 @@ public:
if (nodep->lhsp()->isWide()) {
visit(VN_CAST(nodep, NodeUniop));
} else {
AstVarRef* const vrefp = VN_CAST(nodep->lhsp(), VarRef);
const int widthPow2 = vrefp ? vrefp->varp()->dtypep()->widthPow2()
: nodep->lhsp()->dtypep()->widthPow2();
UASSERT_OBJ(widthPow2 > 1, nodep,
"Reduction over single bit value should have been folded");
putbs("VL_REDXOR_");
puts(cvtToStr(nodep->lhsp()->dtypep()->widthPow2()));
puts(cvtToStr(widthPow2));
puts("(");
iterateAndNextNull(nodep->lhsp());
puts(")");
@ -1019,13 +1025,6 @@ public:
puts(cvtToStr(nodep->fileline()->lineno()));
puts(")");
}
virtual void visit(AstCNew* nodep) override {
puts("std::make_shared<" + prefixNameProtect(nodep->dtypep()) + ">(");
puts("vlSymsp"); // TODO make this part of argsp, and eliminate when unnecessary
if (nodep->argsp()) puts(", ");
iterateAndNextNull(nodep->argsp());
puts(")");
}
virtual void visit(AstNewCopy* nodep) override {
puts("std::make_shared<" + prefixNameProtect(nodep->dtypep()) + ">(");
puts("*"); // i.e. make into a reference
@ -1112,15 +1111,16 @@ public:
// Terminals
virtual void visit(AstVarRef* nodep) override {
const AstVar* const varp = nodep->varp();
if (isConstPoolMod(varp->user4p())) {
const AstNodeModule* const varModp = EmitCParentModule::get(varp);
if (isConstPoolMod(varModp)) {
// Reference to constant pool variable
puts(topClassName() + "__ConstPool__");
} else if (varp->isStatic()) {
// Access static variable via the containing class
puts(prefixNameProtect(varp->user4p()) + "::");
} else if (VN_IS(varp->user4p(), Class) && varp->user4p() != m_modp) {
puts(prefixNameProtect(varModp) + "::");
} else if (VN_IS(varModp, Class) && varModp != m_modp) {
// Superclass member reference
puts(prefixNameProtect(varp->user4p()) + "::");
puts(prefixNameProtect(varModp) + "::");
} else if (!nodep->selfPointer().empty()) {
emitDereference(nodep->selfPointerProtect(m_useSelfForThis));
}
@ -1134,7 +1134,9 @@ public:
puts(funcNameProtect(funcp));
}
virtual void visit(AstConst* nodep) override {
if (nodep->isWide()) {
if (m_emitConstInit) {
EmitCConstInit::visit(nodep);
} else if (nodep->isWide()) {
UASSERT_OBJ(m_wideTempRefp, nodep, "Wide Constant w/ no temp");
emitConstant(nodep, m_wideTempRefp, "");
m_wideTempRefp = nullptr; // We used it, barf if set it a second time
@ -1213,37 +1215,22 @@ public:
m_blkChangeDetVec.push_back(nodep);
}
// Just iterate
virtual void visit(AstNetlist* nodep) override { iterateChildren(nodep); }
virtual void visit(AstTopScope* nodep) override { iterateChildren(nodep); }
virtual void visit(AstScope* nodep) override { iterateChildren(nodep); }
// NOPs
virtual void visit(AstTypedef*) override {}
virtual void visit(AstPragma*) override {}
virtual void visit(AstCell*) override {} // Handled outside the Visit class
virtual void visit(AstVar*) override {} // Handled outside the Visit class
virtual void visit(AstNodeText*) override {} // Handled outside the Visit class
virtual void visit(AstTraceDecl*) override {} // Handled outside the Visit class
virtual void visit(AstTraceInc*) override {} // Handled outside the Visit class
virtual void visit(AstCFile*) override {} // Handled outside the Visit class
virtual void visit(AstCellInline*) override {} // Handled outside visit (in EmitCSyms)
virtual void visit(AstCUse*) override {} // Handled outside the Visit class
// Default
virtual void visit(AstNode* nodep) override {
puts(string("\n???? // ") + nodep->prettyTypeName() + "\n");
iterateChildren(nodep);
// LCOV_EXCL_START
if (!v3Global.opt.lintOnly()) { // An internal problem, so suppress
nodep->v3fatalSrc("Unknown node type reached emitter: " << nodep->prettyTypeName());
}
// LCOV_EXCL_STOP
}
EmitCFunc()
: m_lazyDecls(*this) {
m_suppressSemi = false;
m_wideTempRefp = nullptr;
m_labelNum = 0;
m_splitSize = 0;
m_splitFilenum = 0;
}
EmitCFunc(AstNode* nodep, V3OutCFile* ofp, bool trackText = false)
: EmitCFunc{} {

340
src/V3EmitCHeaders.cpp Normal file
View File

@ -0,0 +1,340 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Emit C++ for tree
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3EmitC.h"
#include "V3EmitCConstInit.h"
#include <algorithm>
#include <vector>
//######################################################################
// Internal EmitC implementation
class EmitCHeader final : public EmitCConstInit {
// METHODS
VL_DEBUG_FUNC; // Declare debug()
void decorateFirst(bool& first, const string& str) {
if (first) {
putsDecoration(str);
first = false;
}
}
void emitCellDecls(const AstNodeModule* modp) {
bool first = true;
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstCell* const cellp = VN_CAST_CONST(nodep, Cell)) {
decorateFirst(first, "// CELLS\n");
puts(prefixNameProtect(cellp->modp()) + "* " + cellp->nameProtect() + ";\n");
}
}
}
void emitDesignVarDecls(const AstNodeModule* modp) {
bool first = true;
std::vector<const AstVar*> varList;
bool lastAnon = false; // initial value is not important, but is used
const auto emitCurrentList = [this, &first, &varList, &lastAnon]() {
if (varList.empty()) return;
decorateFirst(first, "\n// DESIGN SPECIFIC STATE\n");
if (lastAnon) { // Output as anons
const int anonMembers = varList.size();
const int lim = v3Global.opt.compLimitMembers();
int anonL3s = 1;
int anonL2s = 1;
int anonL1s = 1;
if (anonMembers > (lim * lim * lim)) {
anonL3s = (anonMembers + (lim * lim * lim) - 1) / (lim * lim * lim);
anonL2s = lim;
anonL1s = lim;
} else if (anonMembers > (lim * lim)) {
anonL2s = (anonMembers + (lim * lim) - 1) / (lim * lim);
anonL1s = lim;
} else if (anonMembers > lim) {
anonL1s = (anonMembers + lim - 1) / lim;
}
if (anonL1s != 1)
puts("// Anonymous structures to workaround compiler member-count bugs\n");
auto it = varList.cbegin();
for (int l3 = 0; l3 < anonL3s && it != varList.cend(); ++l3) {
if (anonL3s != 1) puts("struct {\n");
for (int l2 = 0; l2 < anonL2s && it != varList.cend(); ++l2) {
if (anonL2s != 1) puts("struct {\n");
for (int l1 = 0; l1 < anonL1s && it != varList.cend(); ++l1) {
if (anonL1s != 1) puts("struct {\n");
for (int l0 = 0; l0 < lim && it != varList.cend(); ++l0) {
emitVarDecl(*it);
++it;
}
if (anonL1s != 1) puts("};\n");
}
if (anonL2s != 1) puts("};\n");
}
if (anonL3s != 1) puts("};\n");
}
// Leftovers, just in case off by one error somewhere above
for (; it != varList.cend(); ++it) emitVarDecl(*it);
} else { // Output as nonanons
for (const auto& pair : varList) emitVarDecl(pair);
}
varList.clear();
};
// Emit variables in consecutive anon and non-anon batches
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) {
if (varp->isIO() || varp->isSignal() || varp->isClassMember() || varp->isTemp()) {
const bool anon = isAnonOk(varp);
if (anon != lastAnon) emitCurrentList();
lastAnon = anon;
varList.emplace_back(varp);
}
}
}
// Emit final batch
emitCurrentList();
}
void emitInternalVarDecls(const AstNodeModule* modp) {
if (!VN_IS(modp, Class)) {
putsDecoration("\n// INTERNAL VARIABLES\n");
puts(symClassName() + "* vlSymsp; // Symbol table\n");
}
}
void emitParamDecls(const AstNodeModule* modp) {
bool first = true;
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstVar* const varp = VN_CAST(nodep, Var)) {
if (varp->isParam()) {
decorateFirst(first, "\n// PARAMETERS\n");
UASSERT_OBJ(varp->valuep(), nodep, "No init for a param?");
// Only C++ LiteralTypes can be constexpr
const bool canBeConstexpr = varp->dtypep()->isLiteralType();
puts("static ");
puts(canBeConstexpr ? "constexpr " : "const ");
puts(varp->dtypep()->cType(varp->nameProtect(), false, false));
if (canBeConstexpr) {
puts(" = ");
iterate(varp->valuep());
}
puts(";\n");
}
}
}
}
void emitCtorDtorDecls(const AstNodeModule* modp) {
if (!VN_IS(modp, Class)) { // Classes use CFuncs with isConstructor/isDestructor
const string& name = prefixNameProtect(modp);
putsDecoration("\n// CONSTRUCTORS\n");
puts(name + "(const char* name);\n");
puts("~" + name + "();\n");
puts("VL_UNCOPYABLE(" + name + ");\n");
}
}
void emitInternalMethodDecls(const AstNodeModule* modp) {
bool first = true;
const string section = "\n// INTERNAL METHODS\n";
if (!VN_IS(modp, Class)) {
decorateFirst(first, section);
puts("void " + protect("__Vconfigure") + "(" + symClassName()
+ "* symsp, bool first);\n");
}
if (v3Global.opt.coverage()) {
decorateFirst(first, section);
puts("void __vlCoverInsert(");
puts(v3Global.opt.threads() ? "std::atomic<uint32_t>" : "uint32_t");
puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n");
puts("const char* hierp, const char* pagep, const char* commentp, const char* "
"linescovp);\n");
}
if (v3Global.opt.savable()) {
decorateFirst(first, section);
puts("void " + protect("__Vserialize") + "(VerilatedSerialize& os);\n");
puts("void " + protect("__Vdeserialize") + "(VerilatedDeserialize& os);\n");
}
}
void emitEnums(const AstNodeModule* modp) {
bool first = true;
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
const AstTypedef* const tdefp = VN_CAST_CONST(nodep, Typedef);
if (!tdefp) continue;
if (!tdefp->attrPublic()) continue;
const AstEnumDType* const edtypep
= VN_CAST(tdefp->dtypep()->skipRefToEnump(), EnumDType);
if (!edtypep) continue;
decorateFirst(first, "\n// ENUMS (that were declared public)\n");
if (edtypep->width() > 64) {
putsDecoration("// enum " + tdefp->nameProtect() + " ignored: Too wide for C++\n");
} else {
puts("enum " + tdefp->name() + " {\n");
for (const AstEnumItem* itemp = edtypep->itemsp(); itemp;
itemp = VN_CAST(itemp->nextp(), EnumItem)) {
puts(itemp->nameProtect());
puts(" = ");
iterate(itemp->valuep());
if (VN_IS(itemp->nextp(), EnumItem)) puts(",");
puts("\n");
}
puts("};\n");
}
}
}
void emitFuncDecls(const AstNodeModule* modp, bool inClassBody) {
std::vector<const AstCFunc*> funcsp;
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstCFunc* const funcp = VN_CAST_CONST(nodep, CFunc)) {
if (funcp->dpiImportPrototype()) // Declared in __Dpi.h
continue;
if (funcp->dpiExportDispatcher()) // Declared in __Dpi.h
continue;
if (funcp->isMethod() != inClassBody) // Only methods go inside class
continue;
if (funcp->isMethod() && funcp->isLoose()) // Loose methods are declared lazily
continue;
funcsp.push_back(funcp);
}
}
std::stable_sort(funcsp.begin(), funcsp.end(), [](const AstNode* ap, const AstNode* bp) {
return ap->name() < bp->name();
});
for (const AstCFunc* const funcp : funcsp) {
if (inClassBody) ofp()->putsPrivate(funcp->declPrivate());
emitCFuncDecl(funcp, modp);
}
}
void emitAll(const AstNodeModule* modp) {
// Include files required by this AstNodeModule
if (const AstClass* const classp = VN_CAST_CONST(modp, Class)) {
if (classp->extendsp())
puts("#include \""
+ prefixNameProtect(classp->extendsp()->classp()->classOrPackagep())
+ ".h\"\n");
}
emitModCUse(modp, VUseType::INT_INCLUDE);
// Forward declarations required by this AstNodeModule
puts("\nclass " + symClassName() + ";\n");
emitModCUse(modp, VUseType::INT_FWD_CLASS);
// From `systemc_header
emitTextSection(modp, AstType::atScHdr);
// Open class body {{{
if (const AstClass* const classp = VN_CAST_CONST(modp, Class)) {
puts("class ");
puts(prefixNameProtect(modp));
if (classp->extendsp()) {
puts(" : public ");
puts(prefixNameProtect(classp->extendsp()->classp()));
}
} else {
puts("VL_MODULE(" + prefixNameProtect(modp) + ")");
}
puts(" {\n");
ofp()->resetPrivate();
ofp()->putsPrivate(false); // public:
// Emit all class body contents
emitCellDecls(modp);
emitEnums(modp);
emitDesignVarDecls(modp);
emitInternalVarDecls(modp);
emitParamDecls(modp);
emitCtorDtorDecls(modp);
emitInternalMethodDecls(modp);
emitFuncDecls(modp, /* inClassBody: */ true);
// From `systemc_interface
emitTextSection(modp, AstType::atScInt);
// Close class body
if (!VN_IS(modp, Class)) {
puts("} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);\n");
} else {
puts("};\n");
}
// }}}
// Emit out of class function declarations
puts("\n");
emitFuncDecls(modp, /* inClassBody: */ false);
}
explicit EmitCHeader(const AstNodeModule* modp) {
UINFO(5, " Emitting header for " << prefixNameProtect(modp) << endl);
// Open output file
const string filename = v3Global.opt.makeDir() + "/" + prefixNameProtect(modp) + ".h";
newCFile(filename, /* slow: */ false, /* source: */ false);
m_ofp = v3Global.opt.systemC() ? new V3OutScFile(filename) : new V3OutCFile(filename);
ofp()->putsHeader();
puts("// DESCRIPTION: Verilator output: Design internal header\n");
puts("// See " + topClassName() + ".h for the primary calling header\n");
ofp()->putsGuard();
// Include files
puts("\n");
ofp()->putsIntTopInclude();
puts("#include \"verilated.h\"\n");
if (v3Global.opt.mtasks()) puts("#include \"verilated_threads.h\"\n");
if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n");
if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n");
emitAll(modp);
if (const AstClassPackage* const packagep = VN_CAST_CONST(modp, ClassPackage)) {
// Put the non-static class implementation in same h file for speed
emitAll(packagep->classp());
}
ofp()->putsEndGuard();
// Close output file
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
}
virtual ~EmitCHeader() override = default;
public:
static void main(const AstNodeModule* modp) { EmitCHeader emitCHeader(modp); }
};
//######################################################################
// EmitC class functions
void V3EmitC::emitcHeaders() {
UINFO(2, __FUNCTION__ << ": " << endl);
// Process each module in turn
for (const AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) {
if (VN_IS(nodep, Class)) continue; // Declared with the ClassPackage
EmitCHeader::main(VN_CAST_CONST(nodep, NodeModule));
}
}

914
src/V3EmitCImp.cpp Normal file
View File

@ -0,0 +1,914 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Emit C++ for tree
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3EmitC.h"
#include "V3Ast.h"
#include "V3EmitCFunc.h"
#include "V3String.h"
#include "V3UniqueNames.h"
#include <map>
#include <set>
#include <vector>
//######################################################################
// Visitor that gathers the headers required by an AstCFunc
class EmitCGatherDependencies final : AstNVisitor {
// Ordered set, as it is used as a key in another map.
std::set<string> m_dependencies; // Header names to be included in output C++ file
// METHODS
void addSymsDependency() { m_dependencies.insert(EmitCBaseVisitor::symClassName()); }
void addModDependency(const AstNodeModule* modp) {
if (const AstClass* const classp = VN_CAST_CONST(modp, Class)) {
m_dependencies.insert(EmitCBaseVisitor::prefixNameProtect(classp->classOrPackagep()));
} else {
m_dependencies.insert(EmitCBaseVisitor::prefixNameProtect(modp));
}
}
void addDTypeDependency(const AstNodeDType* nodep) {
if (const AstClassRefDType* const dtypep = VN_CAST_CONST(nodep, ClassRefDType)) {
m_dependencies.insert(
EmitCBaseVisitor::prefixNameProtect(dtypep->classp()->classOrPackagep()));
}
}
void addSelfDependency(const string& selfPointer, AstNode* nodep) {
if (selfPointer.empty()) {
// No self pointer (e.g.: function locals, const pool values, loose static methods),
// so no dependency
} else if (VString::startsWith(selfPointer, "this")) {
// Dereferencing 'this', we need the definition of this module, which is also the
// module that contains the variable.
addModDependency(EmitCParentModule::get(nodep));
} else {
// Must be an absolute reference
UASSERT_OBJ(selfPointer.find("vlSymsp") != string::npos, nodep,
"Unknown self pointer: '" << selfPointer << "'");
// Dereferencing vlSymsp, so we need it's definition...
m_dependencies.insert(EmitCBaseVisitor::symClassName());
}
}
// VISITORS
virtual void visit(AstCCall* nodep) override {
addSelfDependency(nodep->selfPointer(), nodep->funcp());
iterateChildrenConst(nodep);
}
virtual void visit(AstCNew* nodep) override {
addDTypeDependency(nodep->dtypep());
iterateChildrenConst(nodep);
}
virtual void visit(AstCMethodCall* nodep) override {
addDTypeDependency(nodep->fromp()->dtypep());
iterateChildrenConst(nodep);
}
virtual void visit(AstNewCopy* nodep) override {
addDTypeDependency(nodep->dtypep());
iterateChildrenConst(nodep);
}
virtual void visit(AstMemberSel* nodep) override {
addDTypeDependency(nodep->fromp()->dtypep());
iterateChildrenConst(nodep);
}
virtual void visit(AstNodeVarRef* nodep) override {
addSelfDependency(nodep->selfPointer(), nodep->varp());
iterateChildrenConst(nodep);
}
virtual void visit(AstCoverDecl* nodep) override {
addSymsDependency();
iterateChildrenConst(nodep);
}
virtual void visit(AstCoverInc* nodep) override {
addSymsDependency();
iterateChildrenConst(nodep);
}
virtual void visit(AstDumpCtl* nodep) override {
addSymsDependency();
iterateChildrenConst(nodep);
}
virtual void visit(AstScopeName* nodep) override {
addSymsDependency();
iterateChildrenConst(nodep);
}
virtual void visit(AstPrintTimeScale* nodep) override {
addSymsDependency();
iterateChildrenConst(nodep);
}
virtual void visit(AstTimeFormat* nodep) override {
addSymsDependency();
iterateChildrenConst(nodep);
}
virtual void visit(AstNodeSimpleText* nodep) override {
if (nodep->text().find("vlSymsp") != string::npos) {
m_dependencies.insert(EmitCBaseVisitor::symClassName());
}
iterateChildrenConst(nodep);
}
virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
// CONSTRUCTOR
explicit EmitCGatherDependencies(AstCFunc* cfuncp) {
// Strictly speaking, for loose methods, we could get away with just a forward
// declaration of the receiver class, but their body very likely includes at least one
// relative reference, so we are probably not loosing much.
addModDependency(EmitCParentModule::get(cfuncp));
iterate(cfuncp);
}
public:
static const std::set<std::string> gather(AstCFunc* cfuncp) {
EmitCGatherDependencies visitor{cfuncp};
return std::move(visitor.m_dependencies);
}
};
//######################################################################
// Internal EmitC implementation
class EmitCImp final : EmitCFunc {
// MEMBERS
const AstNodeModule* const m_fileModp; // Files names/headers constructed using this module
const bool m_slow; // Creating __Slow file
const std::set<string>* m_requiredHeadersp; // Header files required by output file
std::string m_subFileName; // substring added to output filenames
V3UniqueNames m_uniqueNames; // For generating unique file names
// METHODS
void openNextOutputFile(const std::set<string>& headers, const string& subFileName) {
UASSERT(!m_ofp, "Output file already open");
splitSizeReset(); // Reset file size tracking
m_lazyDecls.reset(); // Need to emit new lazy declarations
if (v3Global.opt.lintOnly()) {
// Unfortunately we have some lint checks here, so we can't just skip processing.
// We should move them to a different stage.
const string filename = VL_DEV_NULL;
newCFile(filename, /* slow: */ m_slow, /* source: */ true);
m_ofp = new V3OutCFile(filename);
} else {
string filename = v3Global.opt.makeDir() + "/" + prefixNameProtect(m_fileModp);
if (!subFileName.empty()) {
filename += "__" + subFileName;
filename = m_uniqueNames.get(filename);
}
if (m_slow) filename += "__Slow";
filename += ".cpp";
newCFile(filename, /* slow: */ m_slow, /* source: */ true);
m_ofp = v3Global.opt.systemC() ? new V3OutScFile(filename) : new V3OutCFile(filename);
}
ofp()->putsHeader();
puts("// DESCRIPTION: Verilator output: Design implementation internals\n");
puts("// See " + topClassName() + ".h for the primary calling header\n");
// Include files
puts("\n#include \"verilated.h\"\n");
if (v3Global.dpi()) puts("#include \"verilated_dpi.h\"\n");
puts("\n");
for (const string& name : headers) puts("#include \"" + name + ".h\"\n");
emitTextSection(m_modp, AstType::atScImpHdr);
}
void emitStaticVarDefns(const AstNodeModule* modp) {
// Emit static variable definitions
const string modName = prefixNameProtect(modp);
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) {
if (varp->isStatic()) {
puts(varp->vlArgType(true, false, false, modName));
puts(";\n");
}
}
}
}
void emitParamDefns(const AstNodeModule* modp) {
const string modName = prefixNameProtect(modp);
bool first = true;
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) {
if (varp->isParam()) {
if (first) {
puts("\n");
putsDecoration("// Parameter definitions for " + modName + "\n");
first = false;
}
UASSERT_OBJ(varp->valuep(), nodep, "No init for a param?");
// Only C++ LiteralTypes can be constexpr
const bool canBeConstexpr = varp->dtypep()->isLiteralType();
puts(canBeConstexpr ? "constexpr " : "const ");
const string scopedName = modName + "::" + varp->nameProtect();
puts(varp->dtypep()->cType(scopedName, false, false));
if (!canBeConstexpr) {
puts(" = ");
emitConstInit(varp->valuep());
}
puts(";\n");
}
}
}
if (!first) puts("\n");
}
void emitCtorImp(const AstNodeModule* modp) {
const string modName = prefixNameProtect(modp);
puts("\n");
m_lazyDecls.emit("void " + modName + "__", protect("_ctor_var_reset"),
"(" + modName + "* vlSelf);");
puts("\n");
puts(modName + "::" + modName + "(const char* _vcname__)\n");
puts(" : VerilatedModule(_vcname__)\n");
ofp()->indentInc();
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) {
if (const AstBasicDType* const dtypep
= VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
if (dtypep->keyword().isMTaskState()) {
puts(", ");
puts(varp->nameProtect());
puts("(");
iterate(varp->valuep());
puts(")\n");
} else if (varp->isIO() && varp->isSc()) {
puts(", ");
puts(varp->nameProtect());
puts("(");
putsQuoted(varp->nameProtect());
puts(")\n");
}
}
}
}
ofp()->indentDec();
puts(" {\n");
putsDecoration("// Reset structure values\n");
puts(modName + "__" + protect("_ctor_var_reset") + "(this);\n");
emitTextSection(modp, AstType::atScCtor);
puts("}\n");
}
void emitConfigureImp(const AstNodeModule* modp) {
const string modName = prefixNameProtect(modp);
if (v3Global.opt.coverage()) {
puts("\n");
m_lazyDecls.emit("void " + modName + "__", protect("_configure_coverage"),
"(" + modName + "* vlSelf, bool first);");
}
puts("\nvoid " + modName + "::" + protect("__Vconfigure") + "(" + symClassName()
+ "* _vlSymsp, bool first) {\n");
puts("if (false && first) {} // Prevent unused\n");
puts("this->vlSymsp = _vlSymsp;\n"); // First, as later stuff needs it.
if (v3Global.opt.coverage()) {
puts(modName + "__" + protect("_configure_coverage") + "(this, first);\n");
}
puts("}\n");
splitSizeInc(10);
}
void emitCoverageImp() {
if (v3Global.opt.coverage()) {
puts("\n// Coverage\n");
// Rather than putting out VL_COVER_INSERT calls directly, we do it via this
// function. This gets around gcc slowness constructing all of the template
// arguments.
puts("void " + prefixNameProtect(m_modp) + "::__vlCoverInsert(");
puts(v3Global.opt.threads() ? "std::atomic<uint32_t>" : "uint32_t");
puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n");
puts("const char* hierp, const char* pagep, const char* commentp, const char* "
"linescovp) "
"{\n");
if (v3Global.opt.threads()) {
puts("assert(sizeof(uint32_t) == sizeof(std::atomic<uint32_t>));\n");
puts("uint32_t* count32p = reinterpret_cast<uint32_t*>(countp);\n");
} else {
puts("uint32_t* count32p = countp;\n");
}
// static doesn't need save-restore as is constant
puts("static uint32_t fake_zero_count = 0;\n");
// Used for second++ instantiation of identical bin
puts("if (!enable) count32p = &fake_zero_count;\n");
puts("*count32p = 0;\n");
puts("VL_COVER_INSERT(vlSymsp->_vm_contextp__->coveragep(), count32p,");
puts(" \"filename\",filenamep,");
puts(" \"lineno\",lineno,");
puts(" \"column\",column,\n");
// Need to move hier into scopes and back out if do this
// puts( "\"hier\",std::string(vlSymsp->name())+hierp,");
puts("\"hier\",std::string(name())+hierp,");
puts(" \"page\",pagep,");
puts(" \"comment\",commentp,");
puts(" (linescovp[0] ? \"linescov\" : \"\"), linescovp);\n");
puts("}\n");
splitSizeInc(10);
}
}
void emitDestructorImp(const AstNodeModule* modp) {
puts("\n");
puts(prefixNameProtect(modp) + "::~" + prefixNameProtect(modp) + "() {\n");
emitTextSection(modp, AstType::atScDtor);
puts("}\n");
splitSizeInc(10);
}
void emitSavableImp(const AstNodeModule* modp) {
if (v3Global.opt.savable()) {
puts("\n// Savable\n");
for (int de = 0; de < 2; ++de) {
const string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize";
const string funcname = de ? "__Vdeserialize" : "__Vserialize";
const string op = de ? ">>" : "<<";
// NOLINTNEXTLINE(performance-inefficient-string-concatenation)
puts("void " + prefixNameProtect(modp) + "::" + protect(funcname) + "(" + classname
+ "& os) {\n");
// Place a computed checksum to ensure proper structure save/restore formatting
// OK if this hash includes some things we won't dump, since
// just looking for loading the wrong model
VHashSha256 hash;
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstVar* varp = VN_CAST(nodep, Var)) {
hash.insert(varp->name());
hash.insert(varp->dtypep()->width());
}
}
ofp()->printf("vluint64_t __Vcheckval = 0x%" VL_PRI64 "xULL;\n",
static_cast<vluint64_t>(hash.digestUInt64()));
if (de) {
puts("os.readAssert(__Vcheckval);\n");
} else {
puts("os << __Vcheckval;\n");
}
// Save context
// If multiple models save the same context we'll save it multiple
// times, but is harmless, and doing it otherwise would break
// backwards compatibility.
puts("os " + op + " vlSymsp->_vm_contextp__;\n");
// Save all members
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstVar* varp = VN_CAST(nodep, Var)) {
if (varp->isIO() && modp->isTop() && optSystemC()) {
// System C top I/O doesn't need loading, as the
// lower level subinst code does it.
} else if (varp->isParam()) {
} else if (varp->isStatic() && varp->isConst()) {
} else {
int vects = 0;
AstNodeDType* elementp = varp->dtypeSkipRefp();
for (AstUnpackArrayDType* arrayp = VN_CAST(elementp, UnpackArrayDType);
arrayp; arrayp = VN_CAST(elementp, UnpackArrayDType)) {
const int vecnum = vects++;
UASSERT_OBJ(arrayp->hi() >= arrayp->lo(), varp,
"Should have swapped msb & lsb earlier.");
const string ivar = string("__Vi") + cvtToStr(vecnum);
puts("for (int __Vi" + cvtToStr(vecnum) + "=" + cvtToStr(0));
puts("; " + ivar + "<" + cvtToStr(arrayp->elementsConst()));
puts("; ++" + ivar + ") {\n");
elementp = arrayp->subDTypep()->skipRefp();
}
const AstBasicDType* const basicp = elementp->basicp();
// Do not save MTask state, only matters within an evaluation
if (basicp && basicp->keyword().isMTaskState()) continue;
// Want to detect types that are represented as arrays
// (i.e. packed types of more than 64 bits).
if (elementp->isWide()
&& !(basicp && basicp->keyword() == AstBasicDTypeKwd::STRING)) {
const int vecnum = vects++;
const string ivar = string("__Vi") + cvtToStr(vecnum);
puts("for (int __Vi" + cvtToStr(vecnum) + "=" + cvtToStr(0));
puts("; " + ivar + "<" + cvtToStr(elementp->widthWords()));
puts("; ++" + ivar + ") {\n");
}
puts("os" + op + varp->nameProtect());
for (int v = 0; v < vects; ++v) puts("[__Vi" + cvtToStr(v) + "]");
puts(";\n");
for (int v = 0; v < vects; ++v) puts("}\n");
}
}
}
puts("}\n");
}
}
}
// Predicate to check if we actually need to emit anything into the common implementation file.
// Used to avoid creating empty output files.
bool hasCommonImp(const AstNodeModule* modp) const {
// Nothing to emit if no module!
if (!modp) return false;
// We always need the slow file
if (m_slow) return true;
// The fast file is only required when we have ScImp nodes
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (VN_IS(nodep, ScImp)) return true;
}
return false;
}
// Actually emit common implementation contents for given AstNodeModule
void doCommonImp(const AstNodeModule* modp) {
if (m_slow) {
emitStaticVarDefns(modp);
if (!VN_IS(modp, Class)) {
emitParamDefns(modp);
emitCtorImp(modp);
emitConfigureImp(modp);
emitDestructorImp(modp);
}
emitSavableImp(modp);
emitCoverageImp();
} else {
// From `systemc_implementation
emitTextSection(modp, AstType::atScImp);
}
}
void emitCommonImp(const AstNodeModule* modp) {
const AstClass* const classp
= VN_IS(modp, ClassPackage) ? VN_CAST_CONST(modp, ClassPackage)->classp() : nullptr;
if (hasCommonImp(modp) || hasCommonImp(classp)) {
std::set<string> headers;
headers.insert(prefixNameProtect(m_fileModp));
headers.insert(symClassName());
openNextOutputFile(headers, "");
doCommonImp(modp);
if (classp) {
VL_RESTORER(m_modp);
m_modp = classp;
doCommonImp(classp);
}
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
}
}
void emitCFuncImp(const AstNodeModule* modp) {
// Partition functions based on which module definitions they require, by building a
// map from "AstNodeModules whose definitions are required" -> "functions that need
// them"
std::map<const std::set<string>, std::vector<AstCFunc*>> depSet2funcps;
const auto gather = [this, &depSet2funcps](const AstNodeModule* modp) {
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (AstCFunc* const funcp = VN_CAST(nodep, CFunc)) {
// TRACE_* and DPI handled elsewhere
if (funcp->isTrace()) continue;
if (funcp->dpiImportPrototype()) continue;
if (funcp->dpiExportDispatcher()) continue;
if (funcp->slow() != m_slow) continue;
const auto& depSet = EmitCGatherDependencies::gather(funcp);
depSet2funcps[depSet].push_back(funcp);
}
}
};
gather(modp);
if (const AstClassPackage* const packagep = VN_CAST_CONST(modp, ClassPackage)) {
gather(packagep->classp());
}
// Emit all functions in each dependency set into separate files
for (const auto& pair : depSet2funcps) {
m_requiredHeadersp = &pair.first;
// Compute the hash of the dependencies, so we can add it to the filenames to
// disambiguate them
V3Hash hash;
for (const string& name : *m_requiredHeadersp) { hash += name; }
m_subFileName = "DepSet_" + hash.toString();
// Open output file
openNextOutputFile(*m_requiredHeadersp, m_subFileName);
// Emit functions in this dependency set
for (AstCFunc* const funcp : pair.second) {
VL_RESTORER(m_modp);
m_modp = EmitCParentModule::get(funcp);
iterate(funcp);
}
// Close output file
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
}
}
// VISITORS
virtual void visit(AstCFunc* nodep) override {
if (splitNeeded()) {
// Splitting file, so using parallel build.
v3Global.useParallelBuild(true);
// Close old file
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
// Open a new file
openNextOutputFile(*m_requiredHeadersp, m_subFileName);
}
EmitCFunc::visit(nodep);
}
explicit EmitCImp(const AstNodeModule* modp, bool slow)
: m_fileModp{modp}
, m_slow{slow} {
UINFO(5, " Emitting implementation of " << prefixNameProtect(modp) << endl);
m_modp = modp;
// Emit implementation of this module, if this is an AstClassPackage, then put the
// corresponding AstClass implementation in the same file as often optimziations are
// possible when both are seen by the compiler
// TODO: is the above comment still true?
// Emit implementations of common parts
emitCommonImp(modp);
// Emit implementations of all AstCFunc
emitCFuncImp(modp);
}
virtual ~EmitCImp() override = default;
public:
static void main(const AstNodeModule* modp, bool slow) { EmitCImp{modp, slow}; }
};
//######################################################################
// Tracing routines
class EmitCTrace final : EmitCFunc {
// NODE STATE/TYPES
// Cleared on netlist
// AstNode::user1() -> int. Enum number
AstUser1InUse m_inuser1;
// MEMBERS
const bool m_slow; // Making slow file
int m_enumNum = 0; // Enumeration number (whole netlist)
V3UniqueNames m_uniqueNames; // For generating unique file names
// METHODS
void openNextOutputFile() {
UASSERT(!m_ofp, "Output file already open");
splitSizeReset(); // Reset file size tracking
m_lazyDecls.reset(); // Need to emit new lazy declarations
string filename
= (v3Global.opt.makeDir() + "/" + topClassName() + "_" + protect("_Trace"));
filename = m_uniqueNames.get(filename);
if (m_slow) filename += "__Slow";
filename += ".cpp";
AstCFile* cfilep = newCFile(filename, m_slow, true /*source*/);
cfilep->support(true);
if (optSystemC()) {
m_ofp = new V3OutScFile(filename);
} else {
m_ofp = new V3OutCFile(filename);
}
m_ofp->putsHeader();
m_ofp->puts("// DESCR"
"IPTION: Verilator output: Tracing implementation internals\n");
// Includes
puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n");
puts("#include \"" + symClassName() + ".h\"\n");
puts("\n");
}
bool emitTraceIsScBv(AstTraceInc* nodep) {
const AstVarRef* varrefp = VN_CAST(nodep->declp()->valuep(), VarRef);
if (!varrefp) return false;
AstVar* varp = varrefp->varp();
return varp->isSc() && varp->isScBv();
}
bool emitTraceIsScBigUint(AstTraceInc* nodep) {
const AstVarRef* varrefp = VN_CAST(nodep->declp()->valuep(), VarRef);
if (!varrefp) return false;
AstVar* varp = varrefp->varp();
return varp->isSc() && varp->isScBigUint();
}
bool emitTraceIsScUint(AstTraceInc* nodep) {
const AstVarRef* varrefp = VN_CAST(nodep->declp()->valuep(), VarRef);
if (!varrefp) return false;
AstVar* varp = varrefp->varp();
return varp->isSc() && varp->isScUint();
}
void emitTraceInitOne(AstTraceDecl* nodep, int enumNum) {
if (nodep->dtypep()->basicp()->isDouble()) {
puts("tracep->declDouble");
} else if (nodep->isWide()) {
puts("tracep->declArray");
} else if (nodep->isQuad()) {
puts("tracep->declQuad");
} else if (nodep->bitRange().ranged()) {
puts("tracep->declBus");
} else {
puts("tracep->declBit");
}
puts("(c+" + cvtToStr(nodep->code()));
if (nodep->arrayRange().ranged()) puts("+i*" + cvtToStr(nodep->widthWords()));
puts(",");
if (nodep->isScoped()) puts("Verilated::catName(scopep,");
putsQuoted(VIdProtect::protectWordsIf(nodep->showname(), nodep->protect()));
if (nodep->isScoped()) puts(",(int)scopet,\" \")");
// Direction
if (v3Global.opt.traceFormat().fst()) {
puts("," + cvtToStr(enumNum));
// fstVarDir
if (nodep->declDirection().isInoutish()) {
puts(",FST_VD_INOUT");
} else if (nodep->declDirection().isWritable()) {
puts(",FST_VD_OUTPUT");
} else if (nodep->declDirection().isNonOutput()) {
puts(",FST_VD_INPUT");
} else {
puts(", FST_VD_IMPLICIT");
}
//
// fstVarType
const AstVarType vartype = nodep->varType();
const AstBasicDTypeKwd kwd = nodep->declKwd();
string fstvt;
// Doubles have special decoding properties, so must indicate if a double
if (nodep->dtypep()->basicp()->isDouble()) {
if (vartype == AstVarType::GPARAM || vartype == AstVarType::LPARAM) {
fstvt = "FST_VT_VCD_REAL_PARAMETER";
} else {
fstvt = "FST_VT_VCD_REAL";
}
}
// clang-format off
else if (vartype == AstVarType::GPARAM) { fstvt = "FST_VT_VCD_PARAMETER"; }
else if (vartype == AstVarType::LPARAM) { fstvt = "FST_VT_VCD_PARAMETER"; }
else if (vartype == AstVarType::SUPPLY0) { fstvt = "FST_VT_VCD_SUPPLY0"; }
else if (vartype == AstVarType::SUPPLY1) { fstvt = "FST_VT_VCD_SUPPLY1"; }
else if (vartype == AstVarType::TRI0) { fstvt = "FST_VT_VCD_TRI0"; }
else if (vartype == AstVarType::TRI1) { fstvt = "FST_VT_VCD_TRI1"; }
else if (vartype == AstVarType::TRIWIRE) { fstvt = "FST_VT_VCD_TRI"; }
else if (vartype == AstVarType::WIRE) { fstvt = "FST_VT_VCD_WIRE"; }
else if (vartype == AstVarType::PORT) { fstvt = "FST_VT_VCD_WIRE"; }
//
else if (kwd == AstBasicDTypeKwd::INTEGER) { fstvt = "FST_VT_VCD_INTEGER"; }
else if (kwd == AstBasicDTypeKwd::BIT) { fstvt = "FST_VT_SV_BIT"; }
else if (kwd == AstBasicDTypeKwd::LOGIC) { fstvt = "FST_VT_SV_LOGIC"; }
else if (kwd == AstBasicDTypeKwd::INT) { fstvt = "FST_VT_SV_INT"; }
else if (kwd == AstBasicDTypeKwd::SHORTINT) { fstvt = "FST_VT_SV_SHORTINT"; }
else if (kwd == AstBasicDTypeKwd::LONGINT) { fstvt = "FST_VT_SV_LONGINT"; }
else if (kwd == AstBasicDTypeKwd::BYTE) { fstvt = "FST_VT_SV_BYTE"; }
else { fstvt = "FST_VT_SV_BIT"; }
// clang-format on
//
// Not currently supported
// FST_VT_VCD_EVENT
// FST_VT_VCD_PORT
// FST_VT_VCD_SHORTREAL
// FST_VT_VCD_REALTIME
// FST_VT_VCD_SPARRAY
// FST_VT_VCD_TRIAND
// FST_VT_VCD_TRIOR
// FST_VT_VCD_TRIREG
// FST_VT_VCD_WAND
// FST_VT_VCD_WOR
// FST_VT_SV_ENUM
// FST_VT_GEN_STRING
puts("," + fstvt);
}
// Range
if (nodep->arrayRange().ranged()) {
puts(", true,(i+" + cvtToStr(nodep->arrayRange().lo()) + ")");
} else {
puts(", false,-1");
}
if (!nodep->dtypep()->basicp()->isDouble() && nodep->bitRange().ranged()) {
puts(", " + cvtToStr(nodep->bitRange().left()) + ","
+ cvtToStr(nodep->bitRange().right()));
}
puts(");");
}
int emitTraceDeclDType(AstNodeDType* nodep) {
// Return enum number or -1 for none
if (v3Global.opt.traceFormat().fst()) {
// Skip over refs-to-refs, but stop before final ref so can get data type name
// Alternatively back in V3Width we could push enum names from upper typedefs
if (AstEnumDType* enump = VN_CAST(nodep->skipRefToEnump(), EnumDType)) {
int enumNum = enump->user1();
if (!enumNum) {
enumNum = ++m_enumNum;
enump->user1(enumNum);
int nvals = 0;
puts("{\n");
puts("const char* " + protect("__VenumItemNames") + "[]\n");
puts("= {");
for (AstEnumItem* itemp = enump->itemsp(); itemp;
itemp = VN_CAST(itemp->nextp(), EnumItem)) {
if (++nvals > 1) puts(", ");
putbs("\"" + itemp->prettyName() + "\"");
}
puts("};\n");
nvals = 0;
puts("const char* " + protect("__VenumItemValues") + "[]\n");
puts("= {");
for (AstEnumItem* itemp = enump->itemsp(); itemp;
itemp = VN_CAST(itemp->nextp(), EnumItem)) {
AstConst* constp = VN_CAST(itemp->valuep(), Const);
if (++nvals > 1) puts(", ");
putbs("\"" + constp->num().displayed(nodep, "%0b") + "\"");
}
puts("};\n");
puts("tracep->declDTypeEnum(" + cvtToStr(enumNum) + ", \""
+ enump->prettyName() + "\", " + cvtToStr(nvals) + ", "
+ cvtToStr(enump->widthMin()) + ", " + protect("__VenumItemNames") + ", "
+ protect("__VenumItemValues") + ");\n");
puts("}\n");
}
return enumNum;
}
}
return -1;
}
void emitTraceChangeOne(AstTraceInc* nodep, int arrayindex) {
iterateAndNextNull(nodep->precondsp());
const string func = nodep->full() ? "full" : "chg";
bool emitWidth = true;
if (nodep->dtypep()->basicp()->isDouble()) {
puts("tracep->" + func + "Double");
emitWidth = false;
} else if (nodep->isWide() || emitTraceIsScBv(nodep) || emitTraceIsScBigUint(nodep)) {
puts("tracep->" + func + "WData");
} else if (nodep->isQuad()) {
puts("tracep->" + func + "QData");
} else if (nodep->declp()->widthMin() > 16) {
puts("tracep->" + func + "IData");
} else if (nodep->declp()->widthMin() > 8) {
puts("tracep->" + func + "SData");
} else if (nodep->declp()->widthMin() > 1) {
puts("tracep->" + func + "CData");
} else {
puts("tracep->" + func + "Bit");
emitWidth = false;
}
const uint32_t offset = (arrayindex < 0) ? 0 : (arrayindex * nodep->declp()->widthWords());
const uint32_t code = nodep->declp()->code() + offset;
puts(v3Global.opt.trueTraceThreads() && !nodep->full() ? "(base+" : "(oldp+");
puts(cvtToStr(code - nodep->baseCode()));
puts(",");
emitTraceValue(nodep, arrayindex);
if (emitWidth) puts("," + cvtToStr(nodep->declp()->widthMin()));
puts(");\n");
}
void emitTraceValue(AstTraceInc* nodep, int arrayindex) {
if (AstVarRef* const varrefp = VN_CAST(nodep->valuep(), VarRef)) {
AstVar* varp = varrefp->varp();
puts("(");
if (emitTraceIsScBigUint(nodep)) {
puts("(vluint32_t*)");
} else if (emitTraceIsScBv(nodep)) {
puts("VL_SC_BV_DATAP(");
}
iterate(varrefp); // Put var name out
// Tracing only supports 1D arrays
if (nodep->declp()->arrayRange().ranged()) {
if (arrayindex == -2) {
puts("[i]");
} else if (arrayindex == -1) {
puts("[0]");
} else {
puts("[" + cvtToStr(arrayindex) + "]");
}
}
if (varp->isSc()) puts(".read()");
if (emitTraceIsScUint(nodep)) {
puts(nodep->isQuad() ? ".to_uint64()" : ".to_uint()");
} else if (emitTraceIsScBigUint(nodep)) {
puts(".get_raw()");
} else if (emitTraceIsScBv(nodep)) {
puts(")");
}
puts(")");
} else {
puts("(");
iterate(nodep->valuep());
puts(")");
}
}
// VISITORS
using EmitCFunc::visit; // Suppress hidden overloaded virtual function warning
virtual void visit(AstCFunc* nodep) override {
if (!nodep->isTrace()) return;
if (nodep->slow() != m_slow) return;
if (splitNeeded()) {
// Splitting file, so using parallel build.
v3Global.useParallelBuild(true);
// Close old file
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
// Open a new file
openNextOutputFile();
}
EmitCFunc::visit(nodep);
}
virtual void visit(AstTraceDecl* nodep) override {
const int enumNum = emitTraceDeclDType(nodep->dtypep());
if (nodep->arrayRange().ranged()) {
puts("{int i; for (i=0; i<" + cvtToStr(nodep->arrayRange().elements()) + "; i++) {\n");
emitTraceInitOne(nodep, enumNum);
puts("}}\n");
} else {
emitTraceInitOne(nodep, enumNum);
puts("\n");
}
}
virtual void visit(AstTraceInc* nodep) override {
if (nodep->declp()->arrayRange().ranged()) {
// It traces faster if we unroll the loop
for (int i = 0; i < nodep->declp()->arrayRange().elements(); i++) {
emitTraceChangeOne(nodep, i);
}
} else {
emitTraceChangeOne(nodep, -1);
}
}
explicit EmitCTrace(AstNodeModule* modp, bool slow)
: m_slow{slow} {
m_modp = modp;
// Open output file
openNextOutputFile();
// Emit functions
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (AstCFunc* const funcp = VN_CAST(nodep, CFunc)) { iterate(funcp); }
}
// Close output file
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
}
virtual ~EmitCTrace() override = default;
public:
static void main(AstNodeModule* modp, bool slow) { EmitCTrace{modp, slow}; }
};
//######################################################################
// EmitC class functions
void V3EmitC::emitcImp() {
UINFO(2, __FUNCTION__ << ": " << endl);
// Make parent module pointers available.
EmitCParentModule emitCParentModule;
// Process each module in turn
for (const AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) {
if (VN_IS(nodep, Class)) continue; // Imped with ClassPackage
const AstNodeModule* const modp = VN_CAST_CONST(nodep, NodeModule);
EmitCImp::main(modp, /* slow: */ true);
EmitCImp::main(modp, /* slow: */ false);
}
// Emit trace routines (currently they can only exist in the top module)
if (v3Global.opt.trace() && !v3Global.opt.lintOnly()) {
EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ true);
EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ false);
}
}
void V3EmitC::emitcFiles() {
UINFO(2, __FUNCTION__ << ": " << endl);
for (AstNodeFile* filep = v3Global.rootp()->filesp(); filep;
filep = VN_CAST(filep->nextp(), NodeFile)) {
AstCFile* cfilep = VN_CAST(filep, CFile);
if (cfilep && cfilep->tblockp()) {
V3OutCFile of(cfilep->name());
of.puts("// DESCR"
"IPTION: Verilator generated C++\n");
EmitCFunc visitor(cfilep->tblockp(), &of, true);
}
}
}

View File

@ -35,14 +35,14 @@ class EmitCMain final : EmitCBaseVisitor {
public:
// CONSTRUCTORS
explicit EmitCMain(AstNetlist*) { emitInt(); }
EmitCMain() { emitInt(); }
private:
// MAIN METHOD
void emitInt() {
const string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__main.cpp";
newCFile(filename, false /*slow*/, true /*source*/);
V3OutCFile cf(filename);
V3OutCFile cf{filename};
m_ofp = &cf;
// Not defining main_time/vl_time_stamp, so
@ -93,6 +93,8 @@ private:
puts("topp->final();\n");
puts("return 0;\n");
puts("}\n");
m_ofp = nullptr;
}
};
@ -101,5 +103,5 @@ private:
void V3EmitCMain::emit() {
UINFO(2, __FUNCTION__ << ": " << endl);
EmitCMain(v3Global.rootp());
{ EmitCMain visitor; }
}

View File

@ -78,8 +78,8 @@ class CMakeEmitter final {
}
static void emitOverallCMake() {
const std::unique_ptr<std::ofstream> of(
V3File::new_ofstream(v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + ".cmake"));
const std::unique_ptr<std::ofstream> of{
V3File::new_ofstream(v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + ".cmake")};
const string name = v3Global.opt.prefix();
*of << "# Verilated -*- CMake -*-\n";

View File

@ -20,11 +20,13 @@
#include "V3Global.h"
#include "V3EmitC.h"
#include "V3EmitCFunc.h"
#include "V3UniqueNames.h"
#include <algorithm>
#include <vector>
class EmitCModel final : public EmitCFunc {
V3UniqueNames m_uniqueNames; // For generating unique file names
// METHODS
VL_DEBUG_FUNC;
@ -54,7 +56,7 @@ class EmitCModel final : public EmitCFunc {
// Include files
puts("\n");
ofp()->putsIntTopInclude();
puts("#include \"verilated_heavy.h\"\n");
puts("#include \"verilated.h\"\n");
if (v3Global.opt.mtasks()) puts("#include \"verilated_threads.h\"\n");
if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n");
if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n");
@ -90,7 +92,7 @@ class EmitCModel final : public EmitCFunc {
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) {
if (varp->isPrimaryIO()) { //
emitVarDecl(varp, "", /* asRef: */ true);
emitVarDecl(varp, /* asRef: */ true);
}
}
}
@ -272,7 +274,7 @@ class EmitCModel final : public EmitCFunc {
const int vecnum = vects++;
UASSERT_OBJ(arrayp->hi() >= arrayp->lo(), varp,
"Should have swapped msb & lsb earlier.");
const string ivar = string("__Vi") + cvtToStr(vecnum);
const string ivar = std::string{"__Vi"} + cvtToStr(vecnum);
puts("for (int __Vi" + cvtToStr(vecnum) + "="
+ cvtToStr(arrayp->lo()));
puts("; " + ivar + "<=" + cvtToStr(arrayp->hi()));
@ -310,8 +312,10 @@ class EmitCModel final : public EmitCFunc {
const string topModNameProtected = prefixNameProtect(modp);
putsDecoration("// Evaluate till stable\n");
puts("int __VclockLoop = 0;\n");
puts("QData __Vchange = 1;\n");
if (v3Global.rootp()->changeRequest()) {
puts("int __VclockLoop = 0;\n");
puts("QData __Vchange = 1;\n");
}
if (v3Global.opt.trace()) puts("vlSymsp->__Vm_activity = true;\n");
puts("do {\n");
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+ ");
@ -320,29 +324,34 @@ class EmitCModel final : public EmitCFunc {
if (initial)
puts(topModNameProtected + "__" + protect("_eval_settle") + "(&(vlSymsp->TOP));\n");
puts(topModNameProtected + "__" + protect("_eval") + "(&(vlSymsp->TOP));\n");
puts("if (VL_UNLIKELY(++__VclockLoop > " + cvtToStr(v3Global.opt.convergeLimit())
+ ")) {\n");
puts("// About to fail, so enable debug to see what's not settling.\n");
puts("// Note you must run make with OPT=-DVL_DEBUG for debug prints.\n");
puts("int __Vsaved_debug = Verilated::debug();\n");
puts("Verilated::debug(1);\n");
puts("__Vchange = " + topModNameProtected + "__" + protect("_change_request")
+ "(&(vlSymsp->TOP));\n");
puts("Verilated::debug(__Vsaved_debug);\n");
puts("VL_FATAL_MT(");
putsQuoted(protect(modp->fileline()->filename()));
puts(", ");
puts(cvtToStr(modp->fileline()->lineno()));
puts(", \"\",\n");
puts("\"Verilated model didn't ");
if (initial) puts("DC ");
puts("converge\\n\"\n");
puts("\"- See https://verilator.org/warn/DIDNOTCONVERGE\");\n");
puts("} else {\n");
puts("__Vchange = " + topModNameProtected + "__" + protect("_change_request")
+ "(&(vlSymsp->TOP));\n");
puts("}\n");
puts("} while (VL_UNLIKELY(__Vchange));\n");
if (v3Global.rootp()->changeRequest()) {
puts("if (VL_UNLIKELY(++__VclockLoop > " + cvtToStr(v3Global.opt.convergeLimit())
+ ")) {\n");
puts("// About to fail, so enable debug to see what's not settling.\n");
puts("// Note you must run make with OPT=-DVL_DEBUG for debug prints.\n");
puts("int __Vsaved_debug = Verilated::debug();\n");
puts("Verilated::debug(1);\n");
puts("__Vchange = " + topModNameProtected + "__" + protect("_change_request")
+ "(&(vlSymsp->TOP));\n");
puts("Verilated::debug(__Vsaved_debug);\n");
puts("VL_FATAL_MT(");
putsQuoted(protect(modp->fileline()->filename()));
puts(", ");
puts(cvtToStr(modp->fileline()->lineno()));
puts(", \"\",\n");
puts("\"Verilated model didn't ");
if (initial) puts("DC ");
puts("converge\\n\"\n");
puts("\"- See https://verilator.org/warn/DIDNOTCONVERGE\");\n");
puts("} else {\n");
puts("__Vchange = " + topModNameProtected + "__" + protect("_change_request")
+ "(&(vlSymsp->TOP));\n");
puts("}\n");
}
puts("} while ("
+ (v3Global.rootp()->changeRequest() ? std::string{"VL_UNLIKELY(__Vchange)"}
: std::string{"0"})
+ ");\n");
}
void emitStandardMethods(AstNodeModule* modp) {
@ -358,8 +367,10 @@ class EmitCModel final : public EmitCFunc {
puts("void " + topModNameProtected + "__" + protect("_eval_initial") + selfDecl + ";\n");
puts("void " + topModNameProtected + "__" + protect("_eval_settle") + selfDecl + ";\n");
puts("void " + topModNameProtected + "__" + protect("_eval") + selfDecl + ";\n");
puts("QData " + topModNameProtected + "__" + protect("_change_request") + selfDecl
+ ";\n");
if (v3Global.rootp()->changeRequest()) {
puts("QData " + topModNameProtected + "__" + protect("_change_request") + selfDecl
+ ";\n");
}
puts("#ifdef VL_DEBUG\n");
puts("void " + topModNameProtected + "__" + protect("_eval_debug_assertions") + selfDecl
+ ";\n");
@ -484,12 +495,12 @@ class EmitCModel final : public EmitCFunc {
putSectionDelimiter("Trace configuration");
// Forward declaration
puts("\nvoid " + topModNameProtected + "__" + protect("traceInitTop") + "("
puts("\nvoid " + topModNameProtected + "__" + protect("trace_init_top") + "("
+ topModNameProtected + "* vlSelf, " + v3Global.opt.traceClassBase()
+ "* tracep);\n");
// Static helper function
puts("\nstatic void " + protect("traceInit") + "(void* voidSelf, "
puts("\nstatic void " + protect("trace_init") + "(void* voidSelf, "
+ v3Global.opt.traceClassBase() + "* tracep, uint32_t code) {\n");
putsDecoration("// Callback from tracep->open()\n");
puts(voidSelfAssign(modp));
@ -502,20 +513,20 @@ class EmitCModel final : public EmitCFunc {
puts("vlSymsp->__Vm_baseCode = code;\n");
puts("tracep->module(vlSymsp->name());\n");
puts("tracep->scopeEscape(' ');\n");
puts(topModNameProtected + "__" + protect("traceInitTop") + "(vlSelf, tracep);\n");
puts(topModNameProtected + "__" + protect("trace_init_top") + "(vlSelf, tracep);\n");
puts("tracep->scopeEscape('.');\n"); // Restore so later traced files won't break
puts("}\n");
// Forward declaration
puts("\nvoid " + topModNameProtected + "__" + protect("traceRegister") + "("
puts("\nvoid " + topModNameProtected + "__" + protect("trace_register") + "("
+ topModNameProtected + "* vlSelf, " + v3Global.opt.traceClassBase()
+ "* tracep);\n");
// ::trace
puts("\nvoid " + topClassName() + "::trace(");
puts(v3Global.opt.traceClassBase() + "C* tfp, int, int) {\n");
puts("tfp->spTrace()->addInitCb(&" + protect("traceInit") + ", &(vlSymsp->TOP));\n");
puts(topModNameProtected + "__" + protect("traceRegister")
puts("tfp->spTrace()->addInitCb(&" + protect("trace_init") + ", &(vlSymsp->TOP));\n");
puts(topModNameProtected + "__" + protect("trace_register")
+ "(&(vlSymsp->TOP), tfp->spTrace());\n");
puts("}\n");
}
@ -584,12 +595,13 @@ class EmitCModel final : public EmitCFunc {
}
if (!m_ofp) {
const string filename = v3Global.opt.makeDir() + "/" + topClassName()
+ "__Dpi_Export_" + cvtToStr(splitFilenumInc() - 1)
+ ".cpp";
string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__Dpi_Export";
filename = m_uniqueNames.get(filename);
filename += ".cpp";
newCFile(filename, /* slow: */ false, /* source: */ true);
m_ofp = v3Global.opt.systemC() ? new V3OutScFile(filename)
: new V3OutCFile(filename);
m_ofp = v3Global.opt.systemC() ? new V3OutScFile{filename}
: new V3OutCFile{filename};
splitSizeReset(); // Reset file size tracking
m_lazyDecls.reset();
m_ofp->putsHeader();
puts(

View File

@ -389,7 +389,7 @@ void EmitCSyms::emitSymHdr() {
puts("\n");
ofp()->putsIntTopInclude();
puts("#include \"verilated_heavy.h\"\n");
puts("#include \"verilated.h\"\n");
if (v3Global.needTraceDumper()) {
puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n");
}
@ -631,7 +631,7 @@ void EmitCSyms::emitSymImp() {
emitSymImpPreamble();
if (v3Global.opt.savable()) {
for (int de = 0; de < 2; ++de) {
for (const bool& de : {false, true}) {
const string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize";
const string funcname = de ? "__Vdeserialize" : "__Vserialize";
const string op = de ? ">>" : "<<";
@ -670,7 +670,7 @@ void EmitCSyms::emitSymImp() {
+ topClassName() + "* modelp)\n");
puts(" : VerilatedSyms{contextp}\n");
puts(" // Setup internal state of the Syms class\n");
puts(" , __Vm_modelp(modelp)\n");
puts(" , __Vm_modelp{modelp}\n");
if (v3Global.opt.mtasks()) {
// TODO -- For now each model creates its own ThreadPool here,
@ -694,9 +694,9 @@ void EmitCSyms::emitSymImp() {
// Note we create N-1 threads in the thread pool. The thread
// that calls eval() becomes the final Nth thread for the
// duration of the eval call.
puts(" , __Vm_threadPoolp(new VlThreadPool(_vm_contextp__, "
puts(" , __Vm_threadPoolp{new VlThreadPool{_vm_contextp__, "
+ cvtToStr(v3Global.opt.threads() - 1) + ", " + cvtToStr(v3Global.opt.profThreads())
+ "))\n");
+ "}}\n");
}
puts(" // Setup module instances\n");
@ -857,13 +857,8 @@ void EmitCSyms::emitSymImp() {
putsQuoted(protect(it->second.m_varBasePretty));
std::string varName;
varName += (protectIf(scopep->nameDotless(), scopep->protect()) + ".");
if (varp->isParam()) {
varName += protect("var_" + varp->name());
} else {
varName += protect(varp->name());
}
varName += protectIf(scopep->nameDotless(), scopep->protect()) + ".";
varName += protect(varp->name());
if (varp->isParam()) {
if (varp->vlEnumType() == "VLVT_STRING") {
@ -945,6 +940,9 @@ void EmitCSyms::emitDpiHdr() {
puts("// Verilator includes this file in all generated .cpp files that use DPI functions.\n");
puts("// Manually include this file where DPI .c import functions are declared to ensure\n");
puts("// the C functions match the expectations of the DPI imports.\n");
ofp()->putsGuard();
puts("\n");
puts("#include \"svdpi.h\"\n");
puts("\n");
@ -975,6 +973,8 @@ void EmitCSyms::emitDpiHdr() {
puts("#ifdef __cplusplus\n");
puts("}\n");
puts("#endif\n");
ofp()->putsEndGuard();
}
//######################################################################

View File

@ -80,7 +80,7 @@ public:
of.puts("\n### Object file lists...\n");
for (int support = 0; support < 3; ++support) {
for (int slow = 0; slow < 2; ++slow) {
for (const bool& slow : {false, true}) {
if (support == 2) {
of.puts("# Global classes, need linked once per executable");
} else if (support) {

View File

@ -831,7 +831,7 @@ void V3EmitV::verilogForTree(AstNode* nodep, std::ostream& os) { EmitVStreamVisi
void V3EmitV::verilogPrefixedTree(AstNode* nodep, std::ostream& os, const string& prefix,
int flWidth, AstSenTree* domainp, bool user3mark) {
EmitVPrefixedVisitor(nodep, os, prefix, flWidth, domainp, user3mark);
EmitVPrefixedVisitor{nodep, os, prefix, flWidth, domainp, user3mark};
}
void V3EmitV::emitvFiles() {
@ -843,7 +843,7 @@ void V3EmitV::emitvFiles() {
V3OutVFile of(vfilep->name());
of.puts("// DESCR"
"IPTION: Verilator generated Verilog\n");
EmitVFileVisitor visitor(vfilep->tblockp(), &of, true, false);
EmitVFileVisitor visitor{vfilep->tblockp(), &of, true, false};
}
}
}
@ -853,5 +853,5 @@ void V3EmitV::debugEmitV(const string& stage) {
const string filename
= v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__" + stage + ".v";
V3OutVFile of(filename);
EmitVFileVisitor visitor(v3Global.rootp(), &of, true, true);
EmitVFileVisitor visitor{v3Global.rootp(), &of, true, true};
}

View File

@ -112,12 +112,67 @@ class EmitXmlFileVisitor final : public AstNVisitor {
putsQuoted(nodep->origName());
outputChildrenEnd(nodep, "instance");
}
virtual void visit(AstNodeIf* nodep) override {
outputTag(nodep, "if");
puts(">\n");
iterateAndNextNull(nodep->op1p());
puts("<begin>\n");
iterateAndNextNull(nodep->op2p());
puts("</begin>\n");
if (nodep->op3p()) {
puts("<begin>\n");
iterateAndNextNull(nodep->op3p());
puts("</begin>\n");
}
puts("</if>\n");
}
virtual void visit(AstWhile* nodep) override {
outputTag(nodep, "while");
puts(">\n");
puts("<begin>\n");
iterateAndNextNull(nodep->op1p());
puts("</begin>\n");
if (nodep->op2p()) {
puts("<begin>\n");
iterateAndNextNull(nodep->op2p());
puts("</begin>\n");
}
if (nodep->op3p()) {
puts("<begin>\n");
iterateAndNextNull(nodep->op3p());
puts("</begin>\n");
}
if (nodep->op4p()) {
puts("<begin>\n");
iterateAndNextNull(nodep->op4p());
puts("</begin>\n");
}
puts("</while>\n");
}
virtual void visit(AstNetlist* nodep) override {
puts("<netlist>\n");
iterateChildren(nodep);
puts("</netlist>\n");
}
virtual void visit(AstConstPool*) override {}
virtual void visit(AstConstPool* nodep) override {
if (!v3Global.opt.xmlOnly()) {
puts("<constpool>\n");
iterateChildren(nodep);
puts("</constpool>\n");
}
}
virtual void visit(AstInitArray* nodep) override {
puts("<initarray>\n");
const AstInitArray::KeyItemMap& map = nodep->map();
for (AstInitArray::KeyItemMap::const_iterator it = map.begin(); it != map.end(); ++it) {
puts("<inititem index=\"");
puts(cvtToStr(it->first));
puts("\">\n");
iterateChildren(it->second);
puts("</inititem>\n");
}
puts("</initarray>\n");
}
virtual void visit(AstNodeModule* nodep) override {
outputTag(nodep, "");
puts(" origName=");
@ -195,6 +250,12 @@ class EmitXmlFileVisitor final : public AstNVisitor {
putsQuoted(nodep->dotted());
outputChildrenEnd(nodep, "");
}
virtual void visit(AstNodeCCall* nodep) override {
outputTag(nodep, "");
puts(" func=");
putsQuoted(nodep->funcp()->name());
outputChildrenEnd(nodep, "");
}
// Data types
virtual void visit(AstBasicDType* nodep) override {
@ -203,6 +264,7 @@ class EmitXmlFileVisitor final : public AstNVisitor {
puts(" left=\"" + cvtToStr(nodep->left()) + "\"");
puts(" right=\"" + cvtToStr(nodep->right()) + "\"");
}
if (nodep->isSigned()) { puts(" signed=\"true\""); }
puts("/>\n");
}
virtual void visit(AstIfaceRefDType* nodep) override {
@ -319,6 +381,7 @@ private:
// VISITORS
virtual void visit(AstConstPool*) override {}
virtual void visit(AstNodeModule* nodep) override {
if (nodep->level() >= 0
&& nodep->level() <= 2) { // ==2 because we don't add wrapper when in XML mode
@ -392,10 +455,10 @@ void V3EmitXml::emitxml() {
}
{
std::stringstream sstr;
ModuleFilesXmlVisitor moduleFilesVisitor(v3Global.rootp(), sstr);
HierCellsXmlVisitor cellsVisitor(v3Global.rootp(), sstr);
ModuleFilesXmlVisitor moduleFilesVisitor{v3Global.rootp(), sstr};
HierCellsXmlVisitor cellsVisitor{v3Global.rootp(), sstr};
of.puts(sstr.str());
}
EmitXmlFileVisitor visitor(v3Global.rootp(), &of);
EmitXmlFileVisitor visitor{v3Global.rootp(), &of};
of.puts("</verilator_xml>\n");
}

View File

@ -417,7 +417,11 @@ inline void v3errorEndFatal(std::ostringstream& sstr) {
#define VL_DEBUG_FUNC \
static int debug() { \
static int level = -1; \
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); \
if (VL_UNLIKELY(level < 0)) { \
const int debugSrcLevel = v3Global.opt.debugSrcLevel(__FILE__); \
if (!v3Global.opt.available()) return debugSrcLevel; \
level = debugSrcLevel; \
} \
return level; \
}

File diff suppressed because it is too large Load Diff

View File

@ -145,7 +145,7 @@ V3FileDependImp dependImp; // Depend implementation class
// V3FileDependImp
inline void V3FileDependImp::writeDepend(const string& filename) {
const std::unique_ptr<std::ofstream> ofp(V3File::new_ofstream(filename));
const std::unique_ptr<std::ofstream> ofp{V3File::new_ofstream(filename)};
if (ofp->fail()) v3fatal("Can't write " << filename);
for (const DependFile& i : m_filenameList) {
@ -178,7 +178,7 @@ inline std::vector<string> V3FileDependImp::getAllDeps() const {
}
inline void V3FileDependImp::writeTimes(const string& filename, const string& cmdlineIn) {
const std::unique_ptr<std::ofstream> ofp(V3File::new_ofstream(filename));
const std::unique_ptr<std::ofstream> ofp{V3File::new_ofstream(filename)};
if (ofp->fail()) v3fatal("Can't write " << filename);
const string cmdline = stripQuotes(cmdlineIn);
@ -213,7 +213,7 @@ inline void V3FileDependImp::writeTimes(const string& filename, const string& cm
}
inline bool V3FileDependImp::checkTimes(const string& filename, const string& cmdlineIn) {
const std::unique_ptr<std::ifstream> ifp(V3File::new_ifstream_nodepend(filename));
const std::unique_ptr<std::ifstream> ifp{V3File::new_ifstream_nodepend(filename)};
if (ifp->fail()) {
UINFO(2, " --check-times failed: no input " << filename << endl);
return false;

View File

@ -38,7 +38,7 @@ public:
return new_ifstream_nodepend(filename);
}
static std::ifstream* new_ifstream_nodepend(const string& filename) {
return new std::ifstream(filename.c_str());
return new std::ifstream{filename.c_str()};
}
static std::ofstream* new_ofstream(const string& filename, bool append = false) {
addTgtDepend(filename);
@ -47,9 +47,9 @@ public:
static std::ofstream* new_ofstream_nodepend(const string& filename, bool append = false) {
createMakeDirFor(filename);
if (append) {
return new std::ofstream(filename.c_str(), std::ios::app);
return new std::ofstream{filename.c_str(), std::ios::app};
} else {
return new std::ofstream(filename.c_str());
return new std::ofstream{filename.c_str()};
}
}
static FILE* new_fopen_w(const string& filename) {

View File

@ -570,7 +570,7 @@ void GateVisitor::optimizeSignals(bool allowMultiIn) {
AstNode* logicp = logicVertexp->nodep();
if (logicVertexp->reducible()) {
// Can we eliminate?
GateOkVisitor okVisitor(logicp, vvertexp->isClock(), false);
GateOkVisitor okVisitor{logicp, vvertexp->isClock(), false};
const bool multiInputs = okVisitor.rhsVarRefs().size() > 1;
// Was it ok?
bool doit = okVisitor.isSimple();
@ -884,7 +884,7 @@ public:
void GateVisitor::optimizeElimVar(AstVarScope* varscp, AstNode* substp, AstNode* consumerp) {
if (debug() >= 5) consumerp->dumpTree(cout, " elimUsePre: ");
GateElimVisitor elimVisitor(consumerp, varscp, substp, nullptr);
GateElimVisitor elimVisitor{consumerp, varscp, substp, nullptr};
if (elimVisitor.didReplace()) {
if (debug() >= 9) consumerp->dumpTree(cout, " elimUseCns: ");
// Caution: Can't let V3Const change our handle to consumerp, such as by
@ -1148,7 +1148,7 @@ private:
UASSERT_OBJ(vvertexp->dedupable(), vvertexp->varScp(),
"GateLogicVertex* visit should have returned nullptr "
"if consumer var vertex is not dedupable.");
GateOkVisitor okVisitor(lvertexp->nodep(), false, true);
GateOkVisitor okVisitor{lvertexp->nodep(), false, true};
if (okVisitor.isSimple()) {
AstVarScope* dupVarScopep = dupVarRefp->varScopep();
GateVarVertex* dupVvertexp
@ -1226,7 +1226,7 @@ public:
void GateVisitor::dedupe() {
AstNode::user2ClearTree();
GateDedupeGraphVisitor deduper(&m_graph);
GateDedupeGraphVisitor deduper{&m_graph};
// Traverse starting from each of the clocks
UINFO(9, "Gate dedupe() clocks:\n");
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
@ -1367,7 +1367,7 @@ public:
void GateVisitor::mergeAssigns() {
UINFO(6, "mergeAssigns\n");
GateMergeAssignsGraphVisitor merger(&m_graph);
GateMergeAssignsGraphVisitor merger{&m_graph};
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
merger.mergeAssignsTree(vvertexp);
@ -1553,7 +1553,7 @@ public:
void GateVisitor::decomposeClkVectors() {
UINFO(9, "Starting clock decomposition" << endl);
AstNode::user2ClearTree();
GateClkDecompGraphVisitor decomposer(&m_graph);
GateClkDecompGraphVisitor decomposer{&m_graph};
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
if (GateVarVertex* vertp = dynamic_cast<GateVarVertex*>(itp)) {
AstVarScope* vsp = vertp->varScp();
@ -1601,8 +1601,8 @@ public:
void V3Gate::gateAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{
GateVisitor visitor(nodep);
GateDeassignVisitor deassign(nodep);
GateVisitor visitor{nodep};
GateDeassignVisitor deassign{nodep};
} // Destruct before checking
V3Global::dumpCheckGlobalTree("gate", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

View File

@ -56,6 +56,19 @@ private:
if (vscp->user2p()) {
return VN_CAST(vscp->user2p(), VarScope);
} else {
// In order to create a __VinpClk* for a signal, it needs to be marked circular.
// The DPI export trigger is never marked circular by V3Order (see comments in
// OrderVisitor::nodeMarkCircular). The only other place where one might mark
// a node circular is in this pass (V3GenClk), if the signal is assigned but was
// previously used as a clock. The DPI export trigger is only ever assigned in
// a DPI export called from outside eval, or from a DPI import, which are not
// discovered by GenClkReadVisitor (note that impure tasks - i.e.: those setting
// non-local variables - cannot be no-inline, see V3Task), hence the DPI export
// trigger should never be marked circular. Note that ordering should still be
// correct as there will be a change detect on any signals set from a DPI export
// that might have dependents scheduled earlier.
UASSERT_OBJ(vscp != v3Global.rootp()->dpiExportTriggerp(), vscp,
"DPI export trigger should not need __VinpClk");
AstVar* varp = vscp->varp();
string newvarname
= "__VinpClk__" + vscp->scopep()->nameDotless() + "__" + varp->name();
@ -147,7 +160,7 @@ private:
{
// Make the new clock signals and replace any activate references
// See rename, it does some AstNode::userClearTree()'s
GenClkRenameVisitor visitor(nodep, m_topModp);
GenClkRenameVisitor visitor{nodep, m_topModp};
}
}
virtual void visit(AstNodeModule* nodep) override {
@ -222,6 +235,6 @@ public:
void V3GenClk::genClkAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ GenClkReadVisitor visitor(nodep); } // Destruct before checking
{ GenClkReadVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("genclk", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

View File

@ -53,8 +53,8 @@ template <typename T> class VRestorer {
public:
explicit VRestorer(T& permr)
: m_ref(permr)
, m_saved(permr) {}
: m_ref{permr}
, m_saved{permr} {}
~VRestorer() { m_ref = m_saved; }
VL_UNCOPYABLE(VRestorer);
};

View File

@ -314,7 +314,7 @@ void V3Graph::dumpDotFilePrefixedAlways(const string& nameComment, bool colorAsS
void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const {
// This generates a file used by graphviz, https://www.graphviz.org
// "hardcoded" parameters:
const std::unique_ptr<std::ofstream> logp(V3File::new_ofstream(filename));
const std::unique_ptr<std::ofstream> logp{V3File::new_ofstream(filename)};
if (logp->fail()) v3fatal("Can't write " << filename);
// Header

View File

@ -122,8 +122,8 @@ public:
// CONSTRUCTORS
DfaEdge(DfaGraph* graphp, DfaVertex* fromp, DfaVertex* top, const DfaInput& input)
: V3GraphEdge{graphp, fromp, top, 1}
, m_input(input)
, m_complement(false) {}
, m_input{input}
, m_complement{false} {}
DfaEdge(DfaGraph* graphp, DfaVertex* fromp, DfaVertex* top, const DfaEdge* copyfrom)
: V3GraphEdge{graphp, fromp, top, copyfrom->weight()}
, m_input{copyfrom->input()}

View File

@ -19,10 +19,17 @@
#include <functional>
#include <iomanip>
#include <iostream>
#include <sstream>
V3Hash::V3Hash(const std::string& val)
: m_value{static_cast<uint32_t>(std::hash<std::string>{}(val))} {}
std::ostream& operator<<(std::ostream& os, const V3Hash& rhs) {
return os << std::hex << std::setw(8) << std::setfill('0') << rhs.value();
return os << 'h' << std::hex << std::setw(8) << std::setfill('0') << rhs.value();
}
std::string V3Hash::toString() const {
std::ostringstream os;
os << *this;
return os.str();
}

View File

@ -46,6 +46,7 @@ public:
// METHODS
uint32_t value() const { return m_value; }
std::string toString() const;
// OPERATORS
// Comparisons

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