Merge from master for release.

This commit is contained in:
Wilson Snyder 2021-07-07 20:48:51 -04:00
commit de408a5eb7
298 changed files with 9421 additions and 6585 deletions

View File

@ -16,128 +16,133 @@ env:
CI_COMMIT: ${{ github.sha }}
CCACHE_COMPRESS: 1
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_LIMIT_MULTIPLE: 0.95
defaults:
run:
shell: bash
working-directory: repo
jobs:
Matrix:
runs-on: ubuntu-latest
name: Generate Build matrix
outputs:
matrix: ${{ steps.generate.outputs.matrix }}
steps:
- uses: actions/checkout@v2
- id: generate
name: Run 'generate_matrix.sh'
run: |
if [ '${{ github.event_name }}' = 'pull_request' ]; then
matrix='[ "ubuntu-20.04" ]'
else
matrix='[ "ubuntu-16.04", "ubuntu-18.04", "ubuntu-20.04" ]'
fi
echo "::set-output name=matrix::$matrix"
Build:
needs: Matrix
build:
strategy:
fail-fast: false
matrix:
os: ${{ fromJson(needs.Matrix.outputs.matrix) }}
debug: [ opt, dbg ]
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04]
compiler:
- { cc: clang, cxx: clang++ }
- { cc: gcc, cxx: g++ }
m32: [0, 1]
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.debug }}
name: Build | ${{ matrix.os }} | ${{ matrix.compiler.cc }} ${{ matrix.m32 && '| -m32' || '' }}
env:
CI_BUILD_STAGE_NAME: build
CI_RUNS_ON: ${{ matrix.os }}
CI_MAKE_SRC_TARGET: ${{ matrix.debug }}
CI_M32: ${{ matrix.m32 }}
CC: ${{ matrix.compiler.cc }}
CXX: ${{ matrix.compiler.cxx }}
CACHE_KEY: build-${{ matrix.os }}-${{ matrix.compiler.cc }}-${{ matrix.debug }}
CCACHE_MAXSIZE: 256Mi # Per build matrix entry
CACHE_BASE_KEY: build-${{ matrix.os }}-${{ matrix.compiler.cc }}-m32=${{ matrix.m32 }}
CCACHE_MAXSIZE: 250M # Per build matrix entry (2000M in total)
VERILATOR_ARCHIVE: verilator-${{ github.sha }}-${{ matrix.os }}-${{ matrix.compiler.cc }}${{ matrix.m32 && '-m32' || '' }}.tar.gz
steps:
- name: Checkout
uses: actions/checkout@v2
with:
path: repo
- name: Cache
- name: Cache $CCACHE_DIR
uses: actions/cache@v2
env:
cache-name: ccache
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
with:
path: ${{ github.workspace }}/.ccache
key: ${{ env.CACHE_KEY }}-${{ env.cache-name }}-${{ github.sha }}
restore-keys: ${{ env.CACHE_KEY }}-${{ env.cache-name }}
path: ${{ env.CCACHE_DIR }}
key: ${{ env.CACHE_KEY }}-${{ github.sha }}
restore-keys: |
${{ env.CACHE_KEY }}-
- name: Install packages for build
run: ./ci/ci-install.bash
- name: Build
run: |
ccache -z
./ci/ci-script.bash
ccache -s
tar cvzf verilator-${{ matrix.os}}-${CI_COMMIT}-${{ matrix.compiler.cc }}-${{ matrix.debug }}.tgz ./bin
run: ./ci/ci-script.bash
- uses: actions/upload-artifact@v2
- name: Tar up repository
working-directory: ${{ github.workspace }}
run: tar --posix -c -z -f ${{ env.VERILATOR_ARCHIVE }} repo
- name: Upload tar archive
uses: actions/upload-artifact@v2
with:
path: verilator-${{ matrix.os}}-${{ env.CI_COMMIT }}-${{ matrix.compiler.cc }}-${{ matrix.debug }}.tgz
path: ${{ github.workspace }}/${{ env.VERILATOR_ARCHIVE }}
name: ${{ env.VERILATOR_ARCHIVE }}
Test:
needs: [ Matrix, Build ]
test:
needs: build
strategy:
fail-fast: false
matrix:
os: ${{ fromJson(needs.Matrix.outputs.matrix) }}
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04]
compiler:
- { cc: clang, cxx: clang++ }
- { cc: gcc, cxx: g++ }
suite:
- dist-vlt-0
- dist-vlt-1
- dist-vlt-2
- vltmt-0
- vltmt-1
m32: [0, 1]
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, vltmt-0, vltmt-1]
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 }}
name: Test | ${{ matrix.os }} | ${{ matrix.compiler.cc }} | ${{ matrix.suite }} ${{ matrix.m32 && '| -m32' || '' }}
env:
CI_BUILD_STAGE_NAME: test
CI_RUNS_ON: ${{ matrix.os }}
CI_M32: ${{ matrix.m32 }}
CC: ${{ matrix.compiler.cc }}
CXX: ${{ matrix.compiler.cxx }}
CACHE_KEY: test-${{ matrix.os }}-${{ matrix.compiler.cc }}-${{ matrix.suite }}
CCACHE_MAXSIZE: 256Mi # Per build matrix entry
CACHE_BASE_KEY: test-${{ matrix.os }}-${{ matrix.compiler.cc }}-m32=${{ matrix.m32 }}-${ matrix.suite }}
CCACHE_MAXSIZE: 64M # Per build matrix entry (2160M in total)
VERILATOR_ARCHIVE: verilator-${{ github.sha }}-${{ matrix.os }}-${{ matrix.compiler.cc }}${{ matrix.m32 && '-m32' || '' }}.tar.gz
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Download tar archive
uses: actions/download-artifact@v2
with:
name: ${{ env.VERILATOR_ARCHIVE }}
path: ${{ github.workspace }}
- name: Cache
- name: Unpack tar archive
working-directory: ${{ github.workspace }}
run: tar -x -z -f ${{ env.VERILATOR_ARCHIVE }}
- name: Cache $CCACHE_DIR
uses: actions/cache@v2
env:
cache-name: ccache
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
with:
path: ${{ github.workspace }}/.ccache
key: ${{ env.CACHE_KEY }}-${{ env.cache-name }}-${{ github.sha }}
restore-keys: ${{ env.CACHE_KEY }}-${{ env.cache-name }}
path: ${{ env.CCACHE_DIR }}
key: ${{ env.CACHE_KEY }}-${{ github.sha }}
restore-keys: |
${{ env.CACHE_KEY }}-
- uses: actions/download-artifact@v2
- name: Install Verilator and test dependencies
run: |
tar xvzf artifact/verilator-${{ matrix.os}}-${CI_COMMIT}-${{ matrix.compiler.cc }}-opt.tgz
tar xvzf artifact/verilator-${{ matrix.os}}-${CI_COMMIT}-${{ matrix.compiler.cc }}-dbg.tgz
rm -rf artifact
./ci/ci-install.bash
- name: Install test dependencies
run: ./ci/ci-install.bash
- name: Test
env:
TESTS: ${{ matrix.suite }}
run: |
ccache -z
./ci/ci-script.bash
ccache -s
run: ./ci/ci-script.bash

View File

@ -11,9 +11,14 @@ on:
env:
CI_OS_NAME: linux
CI_COMMIT: ${{ github.sha }}
CCACHE_COMPRESS: 1
CCACHE_DIR: ${{ github.workspace }}/.ccache
CI_M32: 0
COVERAGE: 1
VERILATOR_ARCHIVE: verilator-coverage-${{ github.sha }}.tar.gz
defaults:
run:
shell: bash
working-directory: repo
jobs:
@ -23,37 +28,28 @@ jobs:
env:
CI_BUILD_STAGE_NAME: build
CI_RUNS_ON: ubuntu-20.04
CACHE_KEY: coverage-build
CCACHE_MAXSIZE: 512Mi
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Cache
uses: actions/cache@v2
env:
cache-name: ccache
with:
path: ${{ github.workspace }}/.ccache
key: ${{ env.CACHE_KEY }}-${{ env.cache-name }}-${{ github.sha }}
restore-keys: ${{ env.CACHE_KEY }}-${{ env.cache-name }}
path: repo
- name: Install packages for build
env:
CI_BUILD_STAGE_NAME: build
run: ./ci/ci-install.bash
- name: Build
run: |
ccache -z
./ci/ci-script.bash
ccache -s
tar cvzf verilator-${CI_COMMIT}-coverage.tgz bin src/obj*/*.o src/obj*/*.gcno
run: ./ci/ci-script.bash
- uses: actions/upload-artifact@v2
- name: Tar up repository
working-directory: ${{ github.workspace }}
run: tar --posix -c -z -f ${{ env.VERILATOR_ARCHIVE }} repo
- name: Upload tar archive
uses: actions/upload-artifact@v2
with:
path: verilator-${{ env.CI_COMMIT }}-coverage.tgz
path: ${{ github.workspace }}/${{ env.VERILATOR_ARCHIVE }}
name: ${{ env.VERILATOR_ARCHIVE }}
Test:
@ -82,34 +78,22 @@ jobs:
env:
CI_BUILD_STAGE_NAME: test
CI_RUNS_ON: ubuntu-20.04
CACHE_KEY: coverage-test-${{ matrix.test }}${{ matrix.num }}
CCACHE_MAXSIZE: 512Mi # Per build matrix entry
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Cache
uses: actions/cache@v2
env:
cache-name: ccache
- name: Download tar archive
uses: actions/download-artifact@v2
with:
path: ${{ github.workspace }}/.ccache
key: ${{ env.CACHE_KEY }}-${{ env.cache-name }}-${{ github.sha }}
restore-keys: ${{ env.CACHE_KEY }}-${{ env.cache-name }}
name: ${{ env.VERILATOR_ARCHIVE }}
path: ${{ github.workspace }}
- uses: actions/download-artifact@v2
- name: Unpack tar archive
working-directory: ${{ github.workspace }}
run: tar -x -z -f ${{ env.VERILATOR_ARCHIVE }}
- name: Install Verilator and test dependencies
run: |
tar xvzf artifact/verilator-${CI_COMMIT}-coverage.tgz
touch src/obj*/*.o src/obj*/*.gcno
./ci/ci-install.bash
- name: Install test dependencies
run: ./ci/ci-install.bash
- name: Test
env:
TESTS: coverage-${{ matrix.test }}${{ matrix.num }}
run: |
ccache -z
./ci/ci-script.bash
ccache -s
run: ./ci/ci-script.bash

View File

@ -16,6 +16,7 @@ jobs:
CI_OS_NAME: linux
CI_RUNS_ON: ubuntu-20.04
CI_COMMIT: ${{ github.sha }}
CI_M32: 0
steps:
- name: Checkout
uses: actions/checkout@v2
@ -26,14 +27,14 @@ jobs:
CI_BUILD_STAGE_NAME: build
run: |
bash ci/ci-install.bash &&
sudo apt-get install clang-10 yapf3 &&
sudo apt-get install clang-format-11 yapf3 &&
git config --global user.email "action@example.com" &&
git config --global user.name "github action"
- name: Format code
run: |
autoconf &&
./configure &&
make -j 2 format CLANGFORMAT=clang-format-10 &&
make -j 2 format CLANGFORMAT=clang-format-11 &&
git status
- name: Push
run: |

29
Changes
View File

@ -8,6 +8,35 @@ The changes in each Verilator version are described below. The
contributors that suggested a given feature are shown in []. Thanks!
Verilator 4.210 2021-07-07
==========================
**Major:**
* Generated code is now emitted as global functions rather than methods. '$c'
contents might need to be updated, see the docs (#3006). [Geza Lore]
* The generated model class instantiated by the user is now an interface
object and no longer the TOP module instance. User code with direct
C++ member access to model internals, including verilator public_flat
items will likely need to be updated. See the manual for instructions:
https://verilator.org/guide/latest/connecting.html#porting-from-pre-4-210
(#3036). [Geza Lore]
**Minor:**
* Add --prof-c to pass profiling to compiler (#3059). [Alexander Grobman]
* Optimize a lot more model variables into function locals (#3027). [Geza Lore]
* Support middle-of-design nested topmodules (#3026). [Dan Petrisko]
* Remove deprecated --no-relative-cfuncs option (#3024). [Geza Lore]
* Remove deprecated --inhibit-sim option (#3035). [Geza Lore]
* Merge const static data globally into a new constant pool (#3013). [Geza Lore]
* Allow configure override of AR program (#2999). [ahouska]
* In XML, show pinIndex information (#2877). [errae233]
* Fix error on unsupported recursive functions (#2957). [Trefor Southwell]
* Fix type parameter specialization when struct names are same (#3055). [7FM]
* Improve speed of table optimization (-OA) pass. [Geza Lore]
Verilator 4.204 2021-06-12
==========================

View File

@ -230,7 +230,7 @@ all_nomsg: verilator_exe $(VL_INST_MAN_FILES)
verilator_exe verilator_bin$(EXEEXT) verilator_bin_dbg$(EXEEXT) verilator_coverage_bin_dbg$(EXEEXT):
@echo ------------------------------------------------------------
@echo "making verilator in src"
$(MAKE) -C src $(OBJCACHE_JOBS) $(CI_MAKE_SRC_TARGET)
$(MAKE) -C src $(OBJCACHE_JOBS)
.PHONY:msg_test
msg_test: all_nomsg
@ -470,13 +470,13 @@ analyzer-include:
format: clang-format yapf format-pl-exec
CLANGFORMAT = clang-format
CLANGFORMAT = clang-format-11
CLANGFORMAT_FLAGS = -i
CLANGFORMAT_FILES = $(CPPCHECK_CPP) $(CPPCHECK_H) $(CPPCHECK_YL) test_regress/t/*.c* test_regress/t/*.h
clang-format:
@$(CLANGFORMAT) --version | egrep 10.0 > /dev/null \
|| echo "*** You are not using clang-format 10.0, indents may differ from master's ***"
@$(CLANGFORMAT) --version | egrep 11.0 > /dev/null \
|| echo "*** You are not using clang-format 11.0, indents may differ from master's ***"
$(CLANGFORMAT) $(CLANGFORMAT_FLAGS) $(CLANGFORMAT_FILES)
PY_PROGRAMS = \
@ -492,6 +492,7 @@ PY_PROGRAMS = \
src/cppcheck_filtered \
src/flexfix \
src/vlcovgen \
test_regress/t/*.pf \
nodist/code_coverage \
nodist/dot_importer \
nodist/fuzzer/actual_fail \

View File

@ -332,7 +332,6 @@ detailed descriptions of these arguments.
--gate-stmts <value> Tune gate optimizer depth
--if-depth <value> Tune IFDEPTH warning
+incdir+<dir> Directory to search for includes
--inhibit-sim Create function to turn off sim
--inline-mult <value> Tune module inlining
-LDFLAGS <flags> Linker pre-object arguments for makefile
--l2-name <value> Verilog scope name of the top module
@ -344,6 +343,7 @@ detailed descriptions of these arguments.
--MMD Create .d dependency files
--MP Create phony dependency targets
--Mdir <directory> Name of output object directory
--no-merge-const-pool Disable merging of different types in const pool
--mod-prefix <topname> Name to prepend to lower classes
--no-clk <signal-name> Prevent marking specified signal as clock
--no-decoration Disable comments and symbol decorations
@ -367,6 +367,7 @@ detailed descriptions of these arguments.
--pipe-filter <command> Filter all input through a script
--pp-comments Show preprocessor comments with -E
--prefix <topname> Name of top level class
--prof-c Compile C++ code with profiling
--prof-cfuncs Name functions for profiling
--prof-threads Enable generating gantt chart data for threads
--protect-key <key> Key for symbol protection
@ -378,7 +379,6 @@ detailed descriptions of these arguments.
-pvalue+<name>=<value> Overwrite toplevel parameter
--quiet-exit Don't print the command on failure
--relative-includes Resolve includes relative to current file
--no-relative-cfuncs Disallow 'this->' in generated functions
--reloop-limit Minimum iterations for forming loops
--report-unoptflat Extra diagnostics for UNOPTFLAT
--rr Run Verilator and record with rr

View File

@ -88,6 +88,7 @@ else:
args.o.write("{:{width}}| {}s\n".format(k,
v.total_seconds(),
width=wnames))
if i > 4: break
if i > 4:
break
args.o.write("#" * 80 + "\n")

View File

@ -202,7 +202,7 @@ verilator_profcfunc - Read gprof report created with --prof-cfuncs
=head1 SYNOPSIS
verilator --prof-cfuncs ....
gcc --ggdb -pg ....
gcc ....
{run executable}
gprof
verilator_profcfuncs gprof.out

View File

@ -23,6 +23,12 @@ fatal() {
echo "ERROR: $(basename "$0"): $1" >&2; exit 1;
}
if [ "$CI_M32" = "0" ]; then
unset CI_M32
elif [ "$CI_M32" != "1" ]; then
fatal "\$CI_M32 must be '0' or '1'";
fi
if [ "$CI_OS_NAME" = "linux" ]; then
MAKE=make
elif [ "$CI_OS_NAME" = "osx" ]; then
@ -36,7 +42,7 @@ fi
install-vcddiff() {
TMP_DIR="$(mktemp -d)"
git clone https://github.com/veripool/vcddiff "$TMP_DIR"
git -C "${TMP_DIR}" checkout 5112f88b7ba8818dce9dfb72619e64a1fc19542c
git -C "${TMP_DIR}" checkout e5664be5fe39d353bf3fcb50aa05214ab7ed4ac4
"$MAKE" -C "${TMP_DIR}"
sudo cp "${TMP_DIR}/vcddiff" /usr/local/bin
}
@ -55,7 +61,7 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
if [ "$COVERAGE" = 1 ]; then
yes yes | sudo cpan -fi Parallel::Forker
fi
if [ "$M32" = 1 ]; then
if [ "$CI_M32" = 1 ]; then
sudo apt-get install gcc-multilib g++-multilib
fi
elif [ "$CI_OS_NAME" = "osx" ]; then
@ -82,7 +88,7 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ]; then
sudo apt-get install libsystemc-dev
fi
if [ "$M32" = 1 ]; then
if [ "$CI_M32" = 1 ]; then
sudo apt-get install lib32z1-dev gcc-multilib g++-multilib
fi
elif [ "$CI_OS_NAME" = "osx" ]; then
@ -101,9 +107,6 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
fi
yes yes | sudo cpan -M $CI_CPAN_REPO -fi Parallel::Forker
install-vcddiff
autoconf
./configure --enable-longtests --enable-ccwarn
else
##############################################################################
# Unknown build stage

View File

@ -21,6 +21,12 @@ fatal() {
echo "ERROR: $(basename "$0"): $1" >&2; exit 1;
}
if [ "$CI_M32" = "0" ]; then
unset CI_M32
elif [ "$CI_M32" != "1" ]; then
fatal "\$CI_M32 must be '0' or '1'";
fi
if [ "$CI_OS_NAME" = "linux" ]; then
export MAKE=make
NPROC=$(nproc)
@ -40,8 +46,10 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
if [ "$COVERAGE" != 1 ]; then
autoconf
./configure --enable-longtests --enable-ccwarn
./configure --enable-longtests --enable-ccwarn ${CI_M32:+--enable-m32}
ccache -z
"$MAKE" -j "$NPROC" -k
ccache -s
if [ "$CI_OS_NAME" = "osx" ]; then
file bin/verilator_bin
file bin/verilator_bin_dbg
@ -61,6 +69,7 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
if [ "$CI_OS_NAME" = "osx" ]; then
export VERILATOR_TEST_NO_GDB=1 # Pain to get GDB to work on OS X
# TODO below may no longer be requried as configure checks for -pg
export VERILATOR_TEST_NO_GPROF=1 # Apple Clang has no -pg
# export PATH="/Applications/gtkwave.app/Contents/Resources/bin:$PATH" # fst2vcd
file bin/verilator_bin
@ -76,13 +85,15 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
"$MAKE" -j "$NPROC" -k
elif [ "$CI_OS_NAME" = "freebsd" ]; then
export VERILATOR_TEST_NO_GDB=1 # Disable for now, ideally should run
# TODO below may no longer be requried as configure checks for -pg
export VERILATOR_TEST_NO_GPROF=1 # gprof is a bit different on FreeBSD, disable
fi
# Run sanitize on Ubuntu 20.04 only
[ "$CI_RUNS_ON" = 'ubuntu-20.04' ] && sanitize='--sanitize' || sanitize=''
[ "$CI_RUNS_ON" = 'ubuntu-20.04' ] && [ "$CI_M32" = "" ] && sanitize='--sanitize' || sanitize=''
# Run the specified test
ccache -z
case $TESTS in
dist-vlt-0)
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=0/3
@ -169,6 +180,8 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
fatal "Unknown test: $TESTS"
;;
esac
ccache -s
# Upload coverage data
if [[ $TESTS == coverage-* ]]; then
bash <(cat ci/coverage-upload.sh) -f nodist/obj_dir/coverage/app_total.info

View File

@ -7,7 +7,7 @@
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[4.204 2021-06-12],
AC_INIT([Verilator],[4.210 2021-07-07],
[https://verilator.org],
[verilator],[https://verilator.org])
# When releasing, also update header of Changes file
@ -35,7 +35,7 @@ AC_ARG_ENABLE([silent-rules],
AC_MSG_CHECKING(whether to perform partial static linking of Verilator binary)
AC_ARG_ENABLE([partial-static],
[AS_HELP_STRING([--disable-partial-static],
[By default, for Verilation peformance, Verilator
[By default, for Verilation performance, Verilator
is linked against some of its dependencies
statically. Use this to link the Verilator binary
fully dynamically.])],
@ -52,7 +52,7 @@ AC_MSG_CHECKING(whether to use tcmalloc)
AC_ARG_ENABLE([tcmalloc],
[AS_HELP_STRING([--enable-tcmalloc],
[Use libtcmalloc_minimal for faster dynamic memory
management in Verilator binary@<:@default=check@:>@])],
management in Verilator binary @<:@default=check@:>@])],
[case "${enableval}" in
yes) CFG_WITH_TCMALLOC=yes ;;
no) CFG_WITH_TCMALLOC=no ;;
@ -61,6 +61,34 @@ AC_ARG_ENABLE([tcmalloc],
[CFG_WITH_TCMALLOC=check;])
AC_MSG_RESULT($CFG_WITH_TCMALLOC)
# Flag to enable -m32 build
AC_MSG_CHECKING(whether to use -m32)
AC_ARG_ENABLE([m32],
[AS_HELP_STRING([--enable-m32],
[Use -m32 for all compilation and link,
including Verialtor and generated models.])],
[case "${enableval}" in
yes) CFG_ENABLE_M32=yes ;;
no) CFG_ENABLE_M32=no ;;
*) AC_MSG_ERROR([bad value '${enableval}' for --enable-m32]) ;;
esac],
CFG_ENABLE_M32=no)
AC_MSG_RESULT($CFG_ENABLE_M32)
# Flag to enable coverage build
AC_MSG_CHECKING(whether to build for coverage collection)
AC_ARG_ENABLE([coverage],
[AS_HELP_STRING([--enable-coverage],
[Build Verilator for code coverage collection.
For developers only.])],
[case "${enableval}" in
yes) CFG_ENABLE_COVERAGE=yes ;;
no) CFG_ENABLE_COVERAGE=no ;;
*) AC_MSG_ERROR([bad value '${enableval}' for --enable-coverage]) ;;
esac],
CFG_ENABLE_COVERAGE=no)
AC_MSG_RESULT($CFG_ENABLE_COVERAGE)
# Special Substitutions - CFG_WITH_DEFENV
AC_MSG_CHECKING(whether to use hardcoded paths)
AC_ARG_ENABLE([defenv],
@ -135,6 +163,11 @@ AC_RUN_IFELSE(
AC_MSG_RESULT(no);AC_MSG_ERROR([a working C++ compiler is required]),
AC_MSG_RESULT(yes))
AC_CHECK_PROG(AR,ar,ar)
if test "x$AR" = "x" ; then
AC_MSG_ERROR([Cannot find "ar" in your PATH, please install it])
fi
AC_PATH_PROG(PERL,perl)
if test "x$PERL" = "x" ; then
AC_MSG_ERROR([Cannot find "perl" in your PATH, please install it])
@ -211,24 +244,34 @@ AC_DEFUN([_MY_CXX_CHECK_FLAG],
CXXFLAGS="$ACO_SAVE_CXXFLAGS"
])
AC_DEFUN([_MY_CXX_CHECK_IFELSE],
[# _MY_CXX_CHECK_IFELSE(option,action-if-supported,action-if-not-supported)
# Check if compiler supports specific option. If it does,
# do action-if-supported, otherwise do action-if-not-supported
_MY_CXX_CHECK_FLAG($1)
if test "$_my_result" = "yes" ; then
true
$2
else
true
$3
fi
])
AC_DEFUN([_MY_CXX_CHECK_SET],
[# _MY_CXX_CHECK_SET(variable,flag) -- Check if compiler supports specific options
# If it does, set variable to flag, only if not previously set
[# _MY_CXX_CHECK_SET(variable,option)
# Check if compiler supports specific option. If it does,
# set variable to option, only if not previously set.
if test "$$1" = ""; then
_MY_CXX_CHECK_FLAG($2)
if test "$_my_result" = "yes" ; then
$1="$2"
fi
_MY_CXX_CHECK_IFELSE($2, $1="$2")
fi
])
AC_DEFUN([_MY_CXX_CHECK_OPT],
[# _MY_CXX_CHECK_OPT(flag) -- Check if compiler supports specific options
# If it does, append flag to variable
_MY_CXX_CHECK_FLAG($2)
if test "$_my_result" = "yes" ; then
$1="$$1 $2"
fi
[# _MY_CXX_CHECK_OPT(variable,option)
# Check if compiler supports specific option. If it does,
# append option to variable
_MY_CXX_CHECK_IFELSE($2, $1="$$1 $2")
])
AC_DEFUN([_MY_LDLIBS_CHECK_FLAG],
@ -270,6 +313,37 @@ AC_DEFUN([_MY_LDLIBS_CHECK_OPT],
_MY_LDLIBS_CHECK_IFELSE($2, $1="$$1 $2")
])
# When using -m32. Check this first as later checks may fail with the -m32 flag.
if test "$CFG_ENABLE_M32" = "yes"; then
_MY_CXX_CHECK_IFELSE(
-m32,
[CXX="$CXX -m32"],
[AC_MSG_ERROR([--enable-m32 was given but compiler does not support -m32])])
fi
# Similarly, add the coverage flags early as they influence later checks.
if test "$CFG_ENABLE_COVERAGE" = "yes"; then
_MY_CXX_CHECK_OPT(CXX,--coverage)
# Otherwise inline may not show as uncovered
# If we use this then e.g. verilated.h functions properly show up
# if unused.
# However, VerilatedSerialize::write then changes from covered
# to uncovered (in G++ 9.3.0) even with all inlining turned off.
# Having false negative coverage is more effort then missing negatives.
# Also this seems to explode the runtime (since a lot more data).
# _MY_CXX_CHECK_OPT(CXX,-fkeep-inline-functions)
# Otherwise static may not show as uncovered
_MY_CXX_CHECK_OPT(CXX,-fkeep-static-functions)
# Exceptions can pollute the branch coverage data
_MY_CXX_CHECK_OPT(CXX,-fno-exceptions)
# Define-out some impossible stuff
_MY_CXX_CHECK_OPT(CXX,-DVL_GCOV)
fi
# Compiler flags to enable profiling
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_PROFILE,-pg)
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
@ -322,6 +396,18 @@ _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_PARSER,-Wno-parentheses-equality)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_PARSER,-Wno-unused)
AC_SUBST(CFG_CXXFLAGS_PARSER)
# Flags for compiling the debug version of Verilator (in addition to above CFG_CXXFLAGS_SRC)
if test "$CFG_ENABLE_COVERAGE" = "no"; then # Do not optimize for the coverage build
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DEBUG,-Og)
fi
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DEBUG,-ggdb)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DEBUG,-gz)
AC_SUBST(CFG_CXXFLAGS_DEBUG)
# Flags for linking the debug version of Verilator (in addition to above CFG_LDFLAGS_SRC)
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_DEBUG,-gz)
AC_SUBST(CFG_LDFLAGS_DEBUG)
# Flags for Verilated makefile
# For example, -Wno-div-by-zero isn't in 4.1.2
# Random code often does / 0. Unfortunately VL_DIV_I(0,0) will warn

View File

@ -20,6 +20,7 @@ Driss Hafdi
Edgar E. Iglesias
Eric Rippey
Fan Shupei
Felix Yan
Garrett Smith
Geza Lore
Gianfranco Costamagna

View File

@ -1,16 +1,25 @@
/*Verilator blue #008fd7; set in html*/
/* Verilator blue #008fd7; set in html */
.wy-side-nav-search > div.version {
color: #111;
color: #111;
}
.fa-github::before, .icon-github::before {
content: "";
.fa-github::before,
.icon-github::before {
content: "";
}
.fa-home::before, .icon-home::before {
content: "";
.fa-home::before,
.icon-home::before {
content: "";
}
.fa-arrow-circle-right::before, .icon-circle-arrow-right::before {
content: "";
.fa-arrow-circle-right::before,
.icon-circle-arrow-right::before {
content: "";
}
.fa-arrow-circle-left::before, .icon-circle-arrow-left::before {
content: "";
.fa-arrow-circle-left::before,
.icon-circle-arrow-left::before {
content: "";
}

View File

@ -7,22 +7,86 @@
Connecting to Verilated Models
******************************
Structure of the Verilated Model
================================
Verilator outputs a :file:`{prefix}.h` header file which defines a class
named :code:`{prefix}` which represents the generated model the user is
supposed to instantiate. This model class defines the interface of the
Verilated model.
Verilator will additionally create a :file:`{prefix}.cpp` file, together
with additional .h and .cpp files for internals. See the :file:`examples`
directory in the kit for examples. See :ref:`Files Read/Written` for
information on all the files Verilator might output.
The output of Verilator will contain a :file:`{prefix}.mk` file that may be
used with Make to build a :file:`{prefix}__ALL.a` library with all required
objects in it.
The generated model class file manages all internal state required by the
model, and exposes the following interface that allows interaction with the
model:
* Top level IO ports are exposed as references to the appropriate internal
equivalents.
* Public top level module instances are exposed as pointers to allow access
to :code:`/* verilator public */` items.
* The root of the design hierarchy (as in SystemVerilog :code:`$root`) is
exposed via the :code:`rootp` member pointer to allow access to model
internals, including :code:`/* verilator public_flat */` items.
.. _Porting from pre 4.210:
Model interface changes in version 4.210
------------------------------------------
Starting from version 4.210, the model class is an interface object.
Up until Verilator version 4.204 inclusive, the generated model class was
also the instance of the top level instance in the design hierarchy (what
you would refer to with :code:`$root` in SystemVerilog). This meant that
all internal variables that were implemented by Verilator in the root scope
were accessible as members of the model class itself. Note there were often
many such variable due to module inlining, including :code:`/* verilator
public_flat */` items.
This means that user code that accesses internal signals in the model
(likely including :code:`/* verilator public_flat */` signals, as they are
often inlined into the root scope) will need to be updated as follows:
* No change required for accessing top level IO signals. These are directly
accessible in the model class via references.
* No change required for accessing :code:`/* verilator public */` items.
These are directly accessible via sub-module pointers in the model class.
* Accessing any other internal members, including
:code:`/* verilator public_flat */` items requires the following changes:
* Additionally include :file:`{prefix}___024root.h`. This header defines
type of the :code:`rootp` pointer within the model class. Note the
:code:`__024` substring is the Verilator escape sequence for the
:code:`$` character, i.e.: :code:`rootp` points to the Verilated
SystemVerilog :code:`$root` scope.
* Replace :code:`modelp->internal->member->lookup` references with
:code:`modelp->rootp->internal->member->lookup` references, which
contain one additional indirection via the :code:`rootp` pointer.
.. _Connecting to C++:
Connecting to C++
=================
Verilator creates a :file:`{prefix}.h` and :file:`{prefix}.cpp` file for
the top level module, together with additional .h and .cpp files for
internals. See the :file:`examples` directory in the kit for examples. See
:ref:`Files Read/Written` for information on all the files it writes.
After the model is created, there will be a :file:`{prefix}.mk` file that
may be used with Make to produce a :file:`{prefix}__ALL.a` file with all
required objects in it.
The user must write a C++ wrapper and main loop for the simulation, to link
with the Verilated model. Here is a simple example:
In C++ output mode (:vlopt:`--cc`), the Verilator generated model class is a
simple C++ class. The user must write a C++ wrapper and main loop for the
simulation, which instantiates the model class, and link with the Verilated
model. Here is a simple example:
.. code-block:: C++
@ -30,7 +94,7 @@ with the Verilated model. Here is a simple example:
#include <iostream> // Need std::cout
#include "Vtop.h" // From Verilating "top.v"
Vtop *top; // Instantiation of module
Vtop *top; // Instantiation of model
vluint64_t main_time = 0; // Current simulation time
// This is a 64-bit integer to reduce wrap over issues and
@ -45,7 +109,7 @@ with the Verilated model. Here is a simple example:
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv); // Remember args
top = new Vtop; // Create instance
top = new Vtop; // Create model
top->reset_l = 0; // Set some inputs
@ -70,17 +134,18 @@ with the Verilated model. Here is a simple example:
}
Note signals are read and written as member variables of the model. You
call the :code:`eval()` method to evaluate the model. When the simulation
is complete call the :code:`final()` method to execute any SystemVerilog
final blocks, and complete any assertions. See :ref:`Evaluation Loop`.
Note top level IO signals are read and written as members of the model. You
call the :code:`eval()` method to evaluate the model. When the simulation is
complete call the :code:`final()` method to execute any SystemVerilog final
blocks, and complete any assertions. See :ref:`Evaluation Loop`.
Connecting to SystemC
=====================
Verilator will convert the top level module to a SC_MODULE. This module
will attach directly into a SystemC netlist as an instantiation.
In SystemC output mode (:vlopt:`--sc`), the Verilator generated model class
is a SystemC SC_MODULE. This module will attach directly into a SystemC
netlist as an instantiation.
The SC_MODULE gets the same pinout as the Verilog module, with the
following type conversions: Pins of a single bit become bool. Pins 2-32
@ -88,9 +153,9 @@ bits wide become uint32_t's. Pins 33-64 bits wide become sc_bv's or
vluint64_t's depending on the :vlopt:`--no-pins64` option. Wider pins
become sc_bv's. (Uints simulate the fastest so are used where possible.)
Lower modules are not pure SystemC code. This is a feature, as using the
SystemC pin interconnect scheme everywhere would reduce performance by an
order of magnitude.
Model internals, including lower level sub-modules are not pure SystemC
code. This is a feature, as using the SystemC pin interconnect scheme
everywhere would reduce performance by an order of magnitude.
Direct Programming Interface (DPI)

View File

@ -11,14 +11,6 @@ C++11 compiler support
require C++14 or newer compilers for both compiling Verilator and
compiling Verilated models no sooner than January 2022.
No-Relative-Cfuncs Option
The :vlopt:`--no-relative-cfuncs` option is not be required by any C++11
compliant compiler and is planned for removal no sooner than July 2021.
Inhibit-Sim Option
The :vlopt:`--inhibit-sim` option is planned for removal no sooner than
July 2021.
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

@ -527,14 +527,6 @@ Summary:
See :vlopt:`-y`.
.. option:: --inhibit-sim
Rarely needed and deprecated. Create a :code:`inhibitSim(bool)`
function to enable and disable evaluation. This allows an upper level
testbench to disable modules that are not important in a given
simulation, without needing to recompile or change the SystemC modules
instantiated.
.. option:: --inline-mult <value>
Tune the inlining of modules. The default value of 2000 specifies that up
@ -633,6 +625,13 @@ Summary:
The directory is created if it does not exist and the parent directories
exist; otherwise manually create the Mdir before calling Verilator.
.. option:: --no-merge-const-pool
Rarely needed. In order to minimize cache footprint, values of different
data type, that are yet emitted identically in C++ are merged in the
constant pool. This option disables this and causes every constant pool
entry with a distinct data type to be emitted separately.
.. option:: --mod-prefix <topname>
Specifies the name to prepend to all lower level classes. Defaults to
@ -654,10 +653,6 @@ Summary:
Backward compatible alias for :vlopt:`--pins-bv 33 <--pins-bv>`.
.. option:: --no-relative-cfuncs
Deprecated.
.. option:: --no-skip-identical =item --skip-identical
Rarely needed. Disables or enables skipping execution of Verilator if
@ -808,6 +803,13 @@ Summary:
prepended to the name of the :vlopt:`--top` option, or V prepended to
the first Verilog filename passed on the command line.
.. option:: --prof-c
When compiling the C++ code, enable the compiler's profiling flag
(e.g. :code:`g++ -pg`). See :ref:`Profiling`.
Using :vlopt:`--prof-cfuncs` also enables :vlopt:`prof-c`.
.. option:: --prof-cfuncs
Modify the created C++ functions to support profiling. The functions
@ -818,6 +820,8 @@ Summary:
came from. This allows gprof or oprofile reports to be correlated with
the original Verilog source statements. See :ref:`Profiling`.
Using :vlopt:`--prof-cfuncs` also enables :vlopt:`prof-c`.
.. option:: --prof-threads
Enable gantt chart data collection for threaded builds. See :ref:`Thread

View File

@ -33,7 +33,12 @@ or "`ifdef`"'s may break other tools.
that returns up to a 32-bit number (without a trailing ;). This can be
used to call C++ functions from your Verilog code.
String arguments will be put directly into the output C++ code.
String arguments will be put directly into the output C++ code, except
the word 'this' (i.e.: the object pointer) might be replaced with a
different pointer as Verilator might implement logic with non-member
functions. For this reason, any references to class members must be made
via an explicit 'this->' pointer dereference.
Expression arguments will have the code to evaluate the expression
inserted. Thus to call a C++ function, :code:`$c("func(",a,")")` will
result in :code:`func(a)` in the output C++ code. For input arguments,

View File

@ -54,32 +54,38 @@ For --cc/--sc, it creates:
- Arguments for hierarchical Verilation (from --make gmake)
* - *{prefix}*\ _hierCMakeArgs.f
- Arguments for hierarchical Verilation (from --make cmake)
* - *{prefix}*\ .cpp
- Top level C++ file
* - *{prefix}{__n}*\ .cpp
- Additional top C++ files (from --output-split)
* - *{prefix}*\ .h
- Top level header
* - *{prefix}*\ __Slow\ *{__n}*\ .cpp
- Model header
* - *{prefix}*\ .cpp
- Model C++ file
* - *{prefix}*\ ___024root.h
- Top level (SystemVerilog $root) internal header file
* - *{prefix}*\ ___024root.cpp
- Top level (SystemVerilog $root) internal C++ file
* - *{prefix}*___024root*{__n}*\ .cpp
- Additional top level internal C++ files (from --output-split)
* - *{prefix}*\ ___024root__Slow\ *{__n}*\ .cpp
- Infrequent cold routines
* - *{prefix}*\ __Dpi.cpp
- DPI import and export wrappers (from --dpi)
* - *{prefix}*\ ___024root__Trace{__n}*\ .cpp
- Wave file generation code (from --trace)
* - *{prefix}*\ ___024root__Trace__Slow{__n}*\ .cpp
- Wave file generation code (from --trace)
* - *{prefix}*\ __Dpi.h
- DPI import and export declarations (from --dpi)
* - *{prefix}*\ __Dpi.cpp
- Global DPI export wrappers (from --dpi)
* - *{prefix}*\ __Dpi_Export\ *{__n}\ .cpp
- DPI export wrappers scoped to this particular model (from --dpi)
* - *{prefix}*\ __Inlines.h
- Inline support functions
* - *{prefix}*\ __Syms.cpp
- Global symbol table C++
* - *{prefix}*\ __Syms.h
- Global symbol table header
* - *{prefix}*\ __Trace{__n}*\ .cpp
- Wave file generation code (from --trace)
* - *{prefix}*\ __Trace__Slow{__n}*\ .cpp
- Wave file generation code (from --trace)
* - *{prefix}{each_verilog_module}*\ .cpp
- Lower level internal C++ files
* - *{prefix}*\ __Syms.cpp
- Global symbol table C++
* - *{prefix}{each_verilog_module}*\ .h
- Lower level internal header files
* - *{prefix}{each_verilog_module}*\ .cpp
- Lower level internal C++ files
* - *{prefix}{each_verilog_module}{__n}*\ .cpp
- Additional lower C++ files (from --output-split)

View File

@ -116,7 +116,7 @@ Those developing Verilator itself may also want these (see internals.rst):
::
sudo apt-get install gdb graphviz cmake clang clang-format gprof lcov
sudo apt-get install gdb graphviz cmake clang clang-format-11 gprof lcov
sudo pip3 install sphinx sphinx_rtd_theme breathe
cpan install Pod::Perldoc
cpan install Parallel::Forker

View File

@ -272,8 +272,6 @@ profiled C++ code functions.
To use profiling:
#. Use Verilator's :vlopt:`--prof-cfuncs`.
#. Use Verilator's :vlopt:`-CFLAGS "-g -pg" <-CFLAGS>` to pass the
profiling flags through to GCC/Clang.
#. Build and run the simulation model.
#. The model will create gmon.out.
#. Run :command:`gprof` to see where in the C++ code the time is spent.

View File

@ -502,7 +502,7 @@ Some of the code implementing passes is extremely repetitive, and must be
implemented for each sub-class of ``AstNode``. However, while repetitive,
there is more variability than can be handled in C++ macros.
In Verilator this is implemented by using a Perl script, ``astgen`` to
In Verilator this is implemented by using a script, ``astgen`` to
pre-process the C++ code. For example in ``V3Const.cpp`` this is used to
implement the ``visit()`` functions for each binary operation using the
``TREEOP`` macro.
@ -750,8 +750,8 @@ test. For run-time tests, this is followed by a call to the ``execute``
subroutine. Both of these functions can optionally be provided with a hash
table as argument specifying additional options.
The test driver assumes by default that the source Verilog file name
matches the PERL driver name. So a test whose driver is
The driver.pl script assumes by default that the source Verilog file name
matches the test script name. So a test whose driver is
``t/t_mytest.pl`` will expect a Verilog source file ``t/t_mytest.v``.
This can be changed using the ``top_filename`` subroutine, for example

View File

@ -215,7 +215,7 @@ std::string _vl_string_vprintf(const char* formatp, va_list ap) VL_MT_SAFE {
char* bufp = new char[len + 1];
VL_VSNPRINTF(bufp, len + 1, formatp, ap);
std::string out = std::string(bufp, len);
const std::string out = std::string(bufp, len);
delete[] bufp;
return out;
}
@ -246,7 +246,7 @@ void VL_DBG_MSGF(const char* formatp, ...) VL_MT_SAFE {
// includes that otherwise would be required in every Verilated module
va_list ap;
va_start(ap, formatp);
std::string out = _vl_string_vprintf(formatp, ap);
const std::string out = _vl_string_vprintf(formatp, ap);
va_end(ap);
// printf("-imm-V{t%d,%" VL_PRI64 "d}%s", VL_THREAD_ID(), _vl_dbg_sequence_number(),
// out.c_str());
@ -261,7 +261,7 @@ void VL_DBG_MSGF(const char* formatp, ...) VL_MT_SAFE {
void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE {
va_list ap;
va_start(ap, formatp);
std::string out = _vl_string_vprintf(formatp, ap);
const std::string out = _vl_string_vprintf(formatp, ap);
va_end(ap);
VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { //
VL_PRINTF("%s", out.c_str());
@ -301,7 +301,7 @@ vluint64_t vl_rand64() VL_MT_SAFE {
if (VL_COUNTONES_I(t_state[1]) < 10) t_state[1] = ~t_state[1];
}
// Xoroshiro128+ algorithm
vluint64_t result = t_state[0] + t_state[1];
const vluint64_t result = t_state[0] + t_state[1];
t_state[1] ^= t_state[0];
t_state[0] = (((t_state[0] << 55) | (t_state[0] >> 9)) ^ t_state[1] ^ (t_state[1] << 14));
t_state[1] = (t_state[1] << 36) | (t_state[1] >> 28);
@ -356,7 +356,7 @@ WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
//===========================================================================
// Debug
void _vl_debug_print_w(int lbits, WDataInP iwp) VL_MT_SAFE {
void _vl_debug_print_w(int lbits, const WDataInP iwp) VL_MT_SAFE {
VL_PRINTF_MT(" Data: w%d: ", lbits);
for (int i = VL_WORDS_I(lbits) - 1; i >= 0; --i) VL_PRINTF_MT("%08x ", iwp[i]);
VL_PRINTF_MT("\n");
@ -365,24 +365,24 @@ void _vl_debug_print_w(int lbits, WDataInP iwp) VL_MT_SAFE {
//===========================================================================
// Slow math
WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp,
WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, const WDataInP lwp, const WDataInP rwp,
bool is_modulus) VL_MT_SAFE {
// See Knuth Algorithm D. Computes u/v = q.r
// This isn't massively tuned, as wide division is rare
// for debug see V3Number version
// Requires clean input
int words = VL_WORDS_I(lbits);
const int words = VL_WORDS_I(lbits);
for (int i = 0; i < words; ++i) owp[i] = 0;
// Find MSB and check for zero.
int umsbp1 = VL_MOSTSETBITP1_W(words, lwp); // dividend
int vmsbp1 = VL_MOSTSETBITP1_W(words, rwp); // divisor
const int umsbp1 = VL_MOSTSETBITP1_W(words, lwp); // dividend
const int vmsbp1 = VL_MOSTSETBITP1_W(words, rwp); // divisor
if (VL_UNLIKELY(vmsbp1 == 0) // rwp==0 so division by zero. Return 0.
|| VL_UNLIKELY(umsbp1 == 0)) { // 0/x so short circuit and return 0
return owp;
}
int uw = VL_WORDS_I(umsbp1); // aka "m" in the algorithm
int vw = VL_WORDS_I(vmsbp1); // aka "n" in the algorithm
const int uw = VL_WORDS_I(umsbp1); // aka "m" in the algorithm
const int vw = VL_WORDS_I(vmsbp1); // aka "n" in the algorithm
if (vw == 1) { // Single divisor word breaks rest of algorithm
vluint64_t k = 0;
@ -408,8 +408,8 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp,
// Algorithm requires divisor MSB to be set
// Copy and shift to normalize divisor so MSB of vn[vw-1] is set
int s = 31 - VL_BITBIT_I(vmsbp1 - 1); // shift amount (0...31)
vluint32_t shift_mask = s ? 0xffffffff : 0; // otherwise >> 32 won't mask the value
const int s = 31 - VL_BITBIT_I(vmsbp1 - 1); // shift amount (0...31)
const vluint32_t shift_mask = s ? 0xffffffff : 0; // otherwise >> 32 won't mask the value
for (int i = vw - 1; i > 0; --i) {
vn[i] = (rwp[i] << s) | (shift_mask & (rwp[i - 1] >> (32 - s)));
}
@ -429,8 +429,8 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp,
// Main loop
for (int j = uw - vw; j >= 0; --j) {
// Estimate
vluint64_t unw64 = (static_cast<vluint64_t>(un[j + vw]) << 32ULL
| static_cast<vluint64_t>(un[j + vw - 1]));
const vluint64_t unw64 = (static_cast<vluint64_t>(un[j + vw]) << 32ULL
| static_cast<vluint64_t>(un[j + vw - 1]));
vluint64_t qhat = unw64 / static_cast<vluint64_t>(vn[vw - 1]);
vluint64_t rhat = unw64 - qhat * static_cast<vluint64_t>(vn[vw - 1]);
@ -478,15 +478,15 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp,
}
}
WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp,
WDataInP rwp) VL_MT_SAFE {
WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, const WDataInP lwp,
const WDataInP rwp) VL_MT_SAFE {
// obits==lbits, rbits can be different
owp[0] = 1;
for (int i = 1; i < VL_WORDS_I(obits); i++) owp[i] = 0;
// cppcheck-suppress variableScope
WData powstore[VL_MULS_MAX_WORDS]; // Fixed size, as MSVC++ doesn't allow [words] here
WData lastpowstore[VL_MULS_MAX_WORDS]; // Fixed size, as MSVC++ doesn't allow [words] here
WData lastoutstore[VL_MULS_MAX_WORDS]; // Fixed size, as MSVC++ doesn't allow [words] here
VlWide<VL_MULS_MAX_WORDS> powstore; // Fixed size, as MSVC++ doesn't allow [words] here
VlWide<VL_MULS_MAX_WORDS> lastpowstore; // Fixed size, as MSVC++ doesn't allow [words] here
VlWide<VL_MULS_MAX_WORDS> lastoutstore; // Fixed size, as MSVC++ doesn't allow [words] here
// cppcheck-suppress variableScope
VL_ASSIGN_W(obits, powstore, lwp);
for (int bit = 0; bit < rbits; bit++) {
@ -501,13 +501,13 @@ WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp,
}
return owp;
}
WDataOutP VL_POW_WWQ(int obits, int lbits, int rbits, WDataOutP owp, WDataInP lwp,
WDataOutP VL_POW_WWQ(int obits, int lbits, int rbits, WDataOutP owp, const WDataInP lwp,
QData rhs) VL_MT_SAFE {
WData rhsw[VL_WQ_WORDS_E];
VlWide<VL_WQ_WORDS_E> rhsw;
VL_SET_WQ(rhsw, rhs);
return VL_POW_WWW(obits, lbits, rbits, owp, lwp, rhsw);
}
QData VL_POW_QQW(int, int, int rbits, QData lhs, WDataInP rwp) VL_MT_SAFE {
QData VL_POW_QQW(int, int, int rbits, QData lhs, const WDataInP rwp) VL_MT_SAFE {
// Skip check for rhs == 0, as short-circuit doesn't save time
if (VL_UNLIKELY(lhs == 0)) return 0;
QData power = lhs;
@ -519,11 +519,11 @@ QData VL_POW_QQW(int, int, int rbits, QData lhs, WDataInP rwp) VL_MT_SAFE {
return out;
}
WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp, WDataInP rwp,
bool lsign, bool rsign) VL_MT_SAFE {
WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, const WDataInP lwp,
const WDataInP rwp, bool lsign, bool rsign) VL_MT_SAFE {
// obits==lbits, rbits can be different
if (rsign && VL_SIGN_W(rbits, rwp)) {
int words = VL_WORDS_I(obits);
const int words = VL_WORDS_I(obits);
VL_ZERO_W(obits, owp);
EData lor = 0; // 0=all zeros, ~0=all ones, else mix
for (int i = 1; i < (words - 1); ++i) { lor |= lwp[i]; }
@ -545,13 +545,13 @@ WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp, W
}
return VL_POW_WWW(obits, rbits, rbits, owp, lwp, rwp);
}
WDataOutP VL_POWSS_WWQ(int obits, int lbits, int rbits, WDataOutP owp, WDataInP lwp, QData rhs,
bool lsign, bool rsign) VL_MT_SAFE {
WData rhsw[VL_WQ_WORDS_E];
WDataOutP VL_POWSS_WWQ(int obits, int lbits, int rbits, WDataOutP owp, const WDataInP lwp,
QData rhs, bool lsign, bool rsign) VL_MT_SAFE {
VlWide<VL_WQ_WORDS_E> rhsw;
VL_SET_WQ(rhsw, rhs);
return VL_POWSS_WWW(obits, lbits, rbits, owp, lwp, rhsw, lsign, rsign);
}
QData VL_POWSS_QQW(int obits, int, int rbits, QData lhs, WDataInP rwp, bool lsign,
QData VL_POWSS_QQW(int obits, int, int rbits, QData lhs, const WDataInP rwp, bool lsign,
bool rsign) VL_MT_SAFE {
// Skip check for rhs == 0, as short-circuit doesn't save time
if (rsign && VL_SIGN_W(rbits, rwp)) {
@ -571,23 +571,23 @@ QData VL_POWSS_QQW(int obits, int, int rbits, QData lhs, WDataInP rwp, bool lsig
return VL_POW_QQW(obits, rbits, rbits, lhs, rwp);
}
double VL_ITOR_D_W(int lbits, WDataInP lwp) VL_PURE {
double VL_ITOR_D_W(int lbits, const WDataInP lwp) VL_PURE {
int ms_word = VL_WORDS_I(lbits) - 1;
for (; !lwp[ms_word] && ms_word > 0;) --ms_word;
if (ms_word == 0) return static_cast<double>(lwp[0]);
if (ms_word == 1) return static_cast<double>(VL_SET_QW(lwp));
// We need 53 bits of mantissa, which might mean looking at 3 words
// namely ms_word, ms_word-1 and ms_word-2
EData ihi = lwp[ms_word];
EData imid = lwp[ms_word - 1];
EData ilo = lwp[ms_word - 2];
double hi = static_cast<double>(ihi) * std::exp2(2 * VL_EDATASIZE);
double mid = static_cast<double>(imid) * std::exp2(VL_EDATASIZE);
double lo = static_cast<double>(ilo);
double d = (hi + mid + lo) * std::exp2(VL_EDATASIZE * (ms_word - 2));
const EData ihi = lwp[ms_word];
const EData imid = lwp[ms_word - 1];
const EData ilo = lwp[ms_word - 2];
const double hi = static_cast<double>(ihi) * std::exp2(2 * VL_EDATASIZE);
const double mid = static_cast<double>(imid) * std::exp2(VL_EDATASIZE);
const double lo = static_cast<double>(ilo);
const double d = (hi + mid + lo) * std::exp2(VL_EDATASIZE * (ms_word - 2));
return d;
}
double VL_ISTOR_D_W(int lbits, WDataInP lwp) VL_PURE {
double VL_ISTOR_D_W(int lbits, const WDataInP lwp) VL_PURE {
if (!VL_SIGN_W(lbits, lwp)) return VL_ITOR_D_W(lbits, lwp);
vluint32_t pos[VL_MULS_MAX_WORDS + 1]; // Fixed size, as MSVC++ doesn't allow [words] here
VL_NEGATE_W(VL_WORDS_I(lbits), pos, lwp);
@ -599,13 +599,13 @@ double VL_ISTOR_D_W(int lbits, WDataInP lwp) VL_PURE {
// Formatting
// Output a string representation of a wide number
std::string VL_DECIMAL_NW(int width, WDataInP lwp) VL_MT_SAFE {
int maxdecwidth = (width + 3) * 4 / 3;
std::string VL_DECIMAL_NW(int width, const WDataInP lwp) VL_MT_SAFE {
const int maxdecwidth = (width + 3) * 4 / 3;
// Or (maxdecwidth+7)/8], but can't have more than 4 BCD bits per word
WData bcd[VL_VALUE_STRING_MAX_WIDTH / 4 + 2];
VlWide<VL_VALUE_STRING_MAX_WIDTH / 4 + 2> bcd;
VL_ZERO_RESET_W(maxdecwidth, bcd);
WData tmp[VL_VALUE_STRING_MAX_WIDTH / 4 + 2];
WData tmp2[VL_VALUE_STRING_MAX_WIDTH / 4 + 2];
VlWide<VL_VALUE_STRING_MAX_WIDTH / 4 + 2> tmp;
VlWide<VL_VALUE_STRING_MAX_WIDTH / 4 + 2> tmp2;
int from_bit = width - 1;
// Skip all leading zeros
for (; from_bit >= 0 && !(VL_BITRSHIFT_W(lwp, from_bit) & 1); --from_bit) {}
@ -648,25 +648,25 @@ std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t w
if (std::numeric_limits<T>::is_integer) {
constexpr int b = 128;
constexpr int w = VL_WORDS_I(b);
WData tmp0[w], tmp1[w], tmp2[w], tmp3[w];
VlWide<w> tmp0, tmp1, tmp2, tmp3;
WDataInP shifted = VL_EXTEND_WQ(b, 0, tmp0, static_cast<QData>(ld));
if (shift < 0) {
WDataInP pow10 = VL_EXTEND_WQ(b, 0, tmp1, vl_time_pow10(-shift));
const WDataInP pow10 = VL_EXTEND_WQ(b, 0, tmp1, vl_time_pow10(-shift));
shifted = VL_DIV_WWW(b, tmp2, shifted, pow10);
} else {
WDataInP pow10 = VL_EXTEND_WQ(b, 0, tmp1, vl_time_pow10(shift));
const WDataInP pow10 = VL_EXTEND_WQ(b, 0, tmp1, vl_time_pow10(shift));
shifted = VL_MUL_W(w, tmp2, shifted, pow10);
}
WDataInP fracDigitsPow10 = VL_EXTEND_WQ(b, 0, tmp3, vl_time_pow10(fracDigits));
WDataInP integer = VL_DIV_WWW(b, tmp0, shifted, fracDigitsPow10);
WDataInP frac = VL_MODDIV_WWW(b, tmp1, shifted, fracDigitsPow10);
WDataInP max64Bit
const WDataInP fracDigitsPow10 = VL_EXTEND_WQ(b, 0, tmp3, vl_time_pow10(fracDigits));
const WDataInP integer = VL_DIV_WWW(b, tmp0, shifted, fracDigitsPow10);
const WDataInP frac = VL_MODDIV_WWW(b, tmp1, shifted, fracDigitsPow10);
const WDataInP max64Bit
= VL_EXTEND_WQ(b, 0, tmp2, std::numeric_limits<vluint64_t>::max()); // breaks shifted
if (VL_GT_W(w, integer, max64Bit)) {
WDataOutP v = VL_ASSIGN_W(b, tmp3, integer); // breaks fracDigitsPow10
WData zero[w], ten[w];
VlWide<w> zero, ten;
VL_ZERO_W(b, zero);
VL_EXTEND_WI(b, 0, ten, 10);
char buf[128]; // 128B is obviously long enough to represent 128bit integer in decimal
@ -674,9 +674,9 @@ std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t w
*ptr = '\0';
while (VL_GT_W(w, v, zero)) {
--ptr;
WDataInP mod = VL_MODDIV_WWW(b, tmp2, v, ten); // breaks max64Bit
const WDataInP mod = VL_MODDIV_WWW(b, tmp2, v, ten); // breaks max64Bit
*ptr = "0123456789"[VL_SET_QW(mod)];
WData divided[w];
VlWide<w> divided;
VL_DIV_WWW(b, divided, v, ten);
VL_ASSIGN_W(b, v, divided);
}
@ -801,7 +801,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
case 'g':
case '^': { // Realtime
const int lbits = va_arg(ap, int);
double d = va_arg(ap, double);
const double d = va_arg(ap, double);
if (lbits) {} // UNUSED - always 64
if (fmt == '^') { // Realtime
if (!widthSet) width = Verilated::threadContextp()->impp()->timeFormatWidth();
@ -818,7 +818,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
// Deal with all read-and-print somethings
const int lbits = va_arg(ap, int);
QData ld = 0;
WData qlwp[VL_WQ_WORDS_E];
VlWide<VL_WQ_WORDS_E> qlwp;
WDataInP lwp = nullptr;
if (lbits <= VL_QUADSIZE) {
ld = VL_VA_ARG_Q_(ap, lbits);
@ -834,7 +834,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
}
switch (fmt) {
case 'c': {
IData charval = ld & 0xff;
const IData charval = ld & 0xff;
output += static_cast<char>(charval);
break;
}
@ -842,7 +842,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
std::string field;
for (; lsb >= 0; --lsb) {
lsb = (lsb / 8) * 8; // Next digit
IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xff;
const IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xff;
field += (charval == 0) ? ' ' : charval;
}
std::string padding;
@ -860,7 +860,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
append = t_tmp;
} else {
if (VL_SIGN_E(lbits, lwp[VL_WORDS_I(lbits) - 1])) {
WData neg[VL_VALUE_STRING_MAX_WIDTH / 4 + 2];
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);
} else {
@ -957,7 +957,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
}
break;
default: { // LCOV_EXCL_START
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
@ -982,11 +982,11 @@ static inline void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE {
floc -= 8;
}
}
static inline int _vl_vsss_peek(FILE* fp, int& floc, WDataInP fromp,
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) {
int data = std::fgetc(fp);
const int data = std::fgetc(fp);
if (data == EOF) return EOF;
ungetc(data, fp);
return data;
@ -1000,16 +1000,17 @@ static inline int _vl_vsss_peek(FILE* fp, int& floc, WDataInP fromp,
}
}
}
static inline void _vl_vsss_skipspace(FILE* fp, int& floc, WDataInP fromp,
static inline void _vl_vsss_skipspace(FILE* fp, int& floc, const WDataInP fromp,
const std::string& fstr) VL_MT_SAFE {
while (true) {
int c = _vl_vsss_peek(fp, floc, fromp, fstr);
const int c = _vl_vsss_peek(fp, floc, fromp, fstr);
if (c == EOF || !std::isspace(c)) return;
_vl_vsss_advance(fp, floc);
}
}
static inline void _vl_vsss_read_str(FILE* fp, int& floc, WDataInP fromp, const std::string& fstr,
char* tmpp, const char* acceptp) VL_MT_SAFE {
static inline void _vl_vsss_read_str(FILE* fp, int& floc, const WDataInP fromp,
const std::string& fstr, char* tmpp,
const char* acceptp) VL_MT_SAFE {
// Read into tmp, consisting of characters from acceptp list
char* cp = tmpp;
while (true) {
@ -1023,8 +1024,9 @@ static inline void _vl_vsss_read_str(FILE* fp, int& floc, WDataInP fromp, const
*cp++ = '\0';
// VL_DBG_MSGF(" _read got='"<<tmpp<<"'\n");
}
static inline char* _vl_vsss_read_bin(FILE* fp, int& floc, WDataInP fromp, const std::string& fstr,
char* beginp, std::size_t n, bool inhibit = false) {
static inline char* _vl_vsss_read_bin(FILE* fp, int& floc, const WDataInP fromp,
const std::string& fstr, char* beginp, std::size_t n,
bool inhibit = false) {
// Variant of _vl_vsss_read_str using the same underlying I/O functions but optimized
// specifically for block reads of N bytes (read operations are not demarcated by
// whitespace). In the fp case, except descriptor to have been opened in binary mode.
@ -1074,7 +1076,7 @@ static inline void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const
}
IData _vl_vsscanf(FILE* fp, // If a fscanf
int fbits, WDataInP fromp, // Else if a sscanf
int fbits, const WDataInP fromp, // Else if a sscanf
const std::string& fstr, // if a sscanf to string
const char* formatp, va_list ap) VL_MT_SAFE {
// Read a Verilog $sscanf/$fscanf style format into the output list
@ -1119,7 +1121,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
// Deal with all read-and-scan somethings
// Note LSBs are preserved if there's an overflow
const int obits = inIgnore ? 0 : va_arg(ap, int);
WData qowp[VL_WQ_WORDS_E];
VlWide<VL_WQ_WORDS_E> qowp;
VL_SET_WQ(qowp, 0ULL);
WDataOutP owp = qowp;
if (obits == -1) { // string
@ -1240,7 +1242,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
break;
}
default: { // LCOV_EXCL_START
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
@ -1282,7 +1284,7 @@ FILE* VL_CVT_I_FP(IData lhs) VL_MT_SAFE {
return Verilated::threadContextp()->impp()->fdToFp(lhs);
}
void _vl_vint_to_string(int obits, char* destoutp, WDataInP sourcep) VL_MT_SAFE {
void _vl_vint_to_string(int obits, char* destoutp, const WDataInP sourcep) VL_MT_SAFE {
// See also VL_DATA_TO_STRING_NW
int lsb = obits - 1;
bool start = true;
@ -1303,7 +1305,7 @@ void _vl_vint_to_string(int obits, char* destoutp, WDataInP sourcep) VL_MT_SAFE
void _vl_string_to_vint(int obits, void* destp, size_t srclen, const char* srcp) VL_MT_SAFE {
// Convert C string to Verilog format
size_t bytes = VL_BYTES_I(obits);
const size_t bytes = VL_BYTES_I(obits);
char* op = reinterpret_cast<char*>(destp);
if (srclen > bytes) srclen = bytes; // Don't overflow destination
size_t i = 0;
@ -1315,7 +1317,7 @@ static IData getLine(std::string& str, IData fpi, size_t maxLen) VL_MT_SAFE {
str.clear();
// While threadsafe, each thread can only access different file handles
FILE* fp = VL_CVT_I_FP(fpi);
FILE* const fp = VL_CVT_I_FP(fpi);
if (VL_UNLIKELY(!fp)) return 0;
// We don't use fgets, as we must read \0s.
@ -1331,7 +1333,7 @@ static IData getLine(std::string& str, IData fpi, size_t maxLen) VL_MT_SAFE {
IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE {
std::string str;
const IData bytes = VL_BYTES_I(obits);
IData got = getLine(str, fpi, bytes);
const IData got = getLine(str, fpi, bytes);
if (VL_UNLIKELY(str.empty())) return 0;
@ -1351,7 +1353,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
IData ret = errno;
const IData ret = errno;
outputr = std::string(::std::strerror(ret));
return ret;
}
@ -1474,47 +1476,47 @@ void VL_FWRITEF(IData fpi, const char* formatp, ...) VL_MT_SAFE {
IData VL_FSCANF_IX(IData fpi, const char* formatp, ...) VL_MT_SAFE {
// While threadsafe, each thread can only access different file handles
FILE* fp = VL_CVT_I_FP(fpi);
FILE* const fp = VL_CVT_I_FP(fpi);
if (VL_UNLIKELY(!fp)) return 0;
va_list ap;
va_start(ap, formatp);
IData got = _vl_vsscanf(fp, 0, nullptr, "", formatp, ap);
const IData got = _vl_vsscanf(fp, 0, nullptr, "", formatp, ap);
va_end(ap);
return got;
}
IData VL_SSCANF_IIX(int lbits, IData ld, const char* formatp, ...) VL_MT_SAFE {
WData fnw[VL_WQ_WORDS_E];
VlWide<VL_WQ_WORDS_E> fnw;
VL_SET_WI(fnw, ld);
va_list ap;
va_start(ap, formatp);
IData got = _vl_vsscanf(nullptr, lbits, fnw, "", formatp, ap);
const IData got = _vl_vsscanf(nullptr, lbits, fnw, "", formatp, ap);
va_end(ap);
return got;
}
IData VL_SSCANF_IQX(int lbits, QData ld, const char* formatp, ...) VL_MT_SAFE {
WData fnw[VL_WQ_WORDS_E];
VlWide<VL_WQ_WORDS_E> fnw;
VL_SET_WQ(fnw, ld);
va_list ap;
va_start(ap, formatp);
IData got = _vl_vsscanf(nullptr, lbits, fnw, "", formatp, ap);
const IData got = _vl_vsscanf(nullptr, lbits, fnw, "", formatp, ap);
va_end(ap);
return got;
}
IData VL_SSCANF_IWX(int lbits, WDataInP lwp, const char* formatp, ...) VL_MT_SAFE {
IData VL_SSCANF_IWX(int lbits, const WDataInP lwp, const char* formatp, ...) VL_MT_SAFE {
va_list ap;
va_start(ap, formatp);
IData got = _vl_vsscanf(nullptr, lbits, lwp, "", formatp, ap);
const IData got = _vl_vsscanf(nullptr, lbits, lwp, "", formatp, ap);
va_end(ap);
return got;
}
IData VL_SSCANF_INX(int, const std::string& ld, const char* formatp, ...) VL_MT_SAFE {
va_list ap;
va_start(ap, formatp);
IData got = _vl_vsscanf(nullptr, ld.length() * 8, nullptr, ld, formatp, ap);
const IData got = _vl_vsscanf(nullptr, ld.length() * 8, nullptr, ld, formatp, ap);
va_end(ap);
return got;
}
@ -1522,7 +1524,7 @@ IData VL_SSCANF_INX(int, const std::string& ld, const char* formatp, ...) VL_MT_
IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi, IData start,
IData count) VL_MT_SAFE {
// While threadsafe, each thread can only access different file handles
FILE* fp = VL_CVT_I_FP(fpi);
FILE* const fp = VL_CVT_I_FP(fpi);
if (VL_UNLIKELY(!fp)) return 0;
if (count > (array_size - (start - array_lsb))) count = array_size - (start - array_lsb);
// Prep for reading
@ -1572,14 +1574,14 @@ IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi
}
IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE {
WData lhsw[VL_WQ_WORDS_E];
VlWide<VL_WQ_WORDS_E> lhsw;
VL_SET_WQ(lhsw, lhs);
return VL_SYSTEM_IW(VL_WQ_WORDS_E, lhsw);
}
IData VL_SYSTEM_IW(int lhswords, WDataInP lhsp) VL_MT_SAFE {
IData VL_SYSTEM_IW(int lhswords, const WDataInP lhsp) VL_MT_SAFE {
char filenamez[VL_TO_STRING_MAX_WORDS * VL_EDATASIZE + 1];
_vl_vint_to_string(lhswords * VL_EDATASIZE, filenamez, lhsp);
int code = std::system(filenamez); // Yes, std::system() is threadsafe
const int code = std::system(filenamez); // Yes, std::system() is threadsafe
return code >> 8; // Want exit status
}
@ -1707,7 +1709,7 @@ std::string VL_TO_STRING(CData lhs) { return VL_SFORMATF_NX("'h%0x", 8, lhs); }
std::string VL_TO_STRING(SData lhs) { return VL_SFORMATF_NX("'h%0x", 16, lhs); }
std::string VL_TO_STRING(IData lhs) { return VL_SFORMATF_NX("'h%0x", 32, lhs); }
std::string VL_TO_STRING(QData lhs) { return VL_SFORMATF_NX("'h%0x", 64, lhs); }
std::string VL_TO_STRING_W(int words, WDataInP obj) {
std::string VL_TO_STRING_W(int words, const WDataInP obj) {
return VL_SFORMATF_NX("'h%0x", words * VL_EDATASIZE, obj);
}
@ -1722,7 +1724,7 @@ std::string VL_TOUPPER_NN(const std::string& ld) VL_MT_SAFE {
return out;
}
std::string VL_CVT_PACK_STR_NW(int lwords, WDataInP lwp) 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];
int obits = lwords * VL_EDATASIZE;
@ -1804,7 +1806,7 @@ static const char* formatBinary(int nBits, vluint32_t bits) {
static VL_THREAD_LOCAL char t_buf[64];
for (int i = 0; i < nBits; i++) {
bool isOne = bits & (1 << (nBits - 1 - i));
const bool isOne = bits & (1 << (nBits - 1 - i));
t_buf[i] = (isOne ? '1' : '0');
}
t_buf[nBits] = '\0';
@ -1885,7 +1887,7 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) {
// Check for hex or binary digits as file format requests
else if (std::isxdigit(c) || (!reading_addr && (c == 'x' || c == 'X'))) {
c = std::tolower(c);
int value
const int value
= (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0'));
if (reading_addr) {
// Decode @ addresses
@ -1914,12 +1916,13 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) {
return false; // EOF
}
void VlReadMem::setData(void* valuep, const std::string& rhs) {
QData shift = m_hex ? 4ULL : 1ULL;
const QData shift = m_hex ? 4ULL : 1ULL;
bool innum = false;
// Shift value in
for (const auto& i : rhs) {
char c = std::tolower(i);
int value = (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0'));
const char c = std::tolower(i);
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);
if (!innum) *datap = 0;
@ -2001,9 +2004,9 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
}
} else if (m_bits <= 64) {
const QData* datap = reinterpret_cast<const QData*>(valuep);
vluint64_t value = VL_MASK_Q(m_bits) & *datap;
vluint32_t lo = value & 0xffffffff;
vluint32_t hi = value >> 32;
const vluint64_t value = VL_MASK_Q(m_bits) & *datap;
const vluint32_t lo = value & 0xffffffff;
const vluint32_t hi = value >> 32;
if (m_hex) {
fprintf(m_fp, memhFormat(m_bits - 32), hi);
fprintf(m_fp, "%08x\n", lo);
@ -2012,7 +2015,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
fprintf(m_fp, "%s\n", formatBinary(32, lo));
}
} else {
WDataInP datap = reinterpret_cast<WDataInP>(valuep);
const WDataInP datap = reinterpret_cast<WDataInP>(valuep);
// output as a sequence of VL_EDATASIZE'd words
// from MSB to LSB. Mask off the MSB word which could
// contain junk above the top of valid data.
@ -2022,7 +2025,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
EData data = datap[word_idx];
if (first) {
data &= VL_MASK_E(m_bits);
int top_word_nbits = VL_BITBIT_E(m_bits - 1) + 1;
const int top_word_nbits = VL_BITBIT_E(m_bits - 1) + 1;
if (m_hex) {
fprintf(m_fp, memhFormat(top_word_nbits), data);
} else {
@ -2108,7 +2111,7 @@ void VL_WRITEMEM_N(bool hex, // Hex format, else binary
if (VL_UNLIKELY(!wmem.isOpen())) return;
for (QData addr = start; addr <= end; ++addr) {
QData row_offset = addr - array_lsb;
const QData row_offset = addr - array_lsb;
if (bits <= 8) {
const CData* datap = &(reinterpret_cast<const CData*>(memp))[row_offset];
wmem.print(addr, false, datap);
@ -2122,8 +2125,8 @@ void VL_WRITEMEM_N(bool hex, // Hex format, else binary
const QData* datap = &(reinterpret_cast<const QData*>(memp))[row_offset];
wmem.print(addr, false, datap);
} else {
WDataInP memDatap = reinterpret_cast<WDataInP>(memp);
WDataInP datap = &memDatap[row_offset * VL_WORDS_I(bits)];
const WDataInP memDatap = reinterpret_cast<WDataInP>(memp);
const WDataInP datap = &memDatap[row_offset * VL_WORDS_I(bits)];
wmem.print(addr, false, datap);
}
}
@ -2351,7 +2354,7 @@ void VerilatedContext::timeprecision(int value) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
m_s.m_timeprecision = value;
#ifdef SYSTEMC_VERSION
sc_time sc_res = sc_get_time_resolution();
const sc_time sc_res = sc_get_time_resolution();
int sc_prec = 99;
if (sc_res == sc_time(1, SC_SEC)) {
sc_prec = 0;
@ -2373,7 +2376,7 @@ void VerilatedContext::timeprecision(int value) VL_MT_SAFE {
<< ". Suggest use 'sc_set_time_resolution(" << vl_time_str(value)
<< ")', or Verilator '--timescale-override " << vl_time_str(sc_prec) << "/"
<< vl_time_str(sc_prec) << "'";
std::string msgs = msg.str();
const std::string msgs = msg.str();
VL_FATAL_MT("", 0, "", msgs.c_str());
}
#endif
@ -2434,7 +2437,7 @@ std::string VerilatedContextImp::argPlusMatch(const char* prefixp)
VL_MT_SAFE_EXCLUDES(m_argMutex) {
const VerilatedLockGuard lock(m_argMutex);
// Note prefixp does not include the leading "+"
size_t len = std::strlen(prefixp);
const size_t len = std::strlen(prefixp);
if (VL_UNLIKELY(!m_args.m_argVecLoaded)) {
m_args.m_argVecLoaded = true; // Complain only once
VL_FATAL_MT("unknown", 0, "",
@ -2511,7 +2514,7 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
}
bool VerilatedContextImp::commandArgVlValue(const std::string& arg, const std::string& prefix,
std::string& valuer) {
size_t len = prefix.length();
const size_t len = prefix.length();
if (0 == std::strncmp(prefix.c_str(), arg.c_str(), len)) {
valuer = arg.substr(len);
return true;
@ -2528,7 +2531,7 @@ void VerilatedContext::randSeed(int val) VL_MT_SAFE {
// and so the rand seed's mutex must also be static
const VerilatedLockGuard lock(VerilatedContextImp::s().s_randMutex);
m_s.m_randSeed = val;
vluint64_t newEpoch = VerilatedContextImp::s().s_randSeedEpoch + 1;
const vluint64_t newEpoch = VerilatedContextImp::s().s_randSeedEpoch + 1;
// Obververs must see new epoch AFTER seed updated
#ifdef VL_THREADED
std::atomic_signal_fence(std::memory_order_release);
@ -2623,7 +2626,7 @@ const char* Verilated::catName(const char* n1, const char* n2, int scopet,
// Used by symbol table creation to make module names
static VL_THREAD_LOCAL char* t_strp = nullptr;
static VL_THREAD_LOCAL size_t t_len = 0;
size_t newlen
const size_t newlen
= std::strlen(n1) + std::strlen(n2) + std::strlen(delimiter) + (scopet > 0 ? 2 : 1);
if (VL_UNLIKELY(!t_strp || newlen > t_len)) {
if (t_strp) delete[] t_strp;
@ -2726,8 +2729,8 @@ 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
std::string msg = (std::string("Testbench C set input '") + signame
+ "' to value that overflows what the signal's width can fit");
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
}
@ -2889,8 +2892,8 @@ void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, boo
va_list ap;
va_start(ap, dims);
for (int i = 0; i < dims; ++i) {
int msb = va_arg(ap, int);
int lsb = va_arg(ap, int);
const int msb = va_arg(ap, int);
const int lsb = va_arg(ap, int);
if (i == 0) {
var.m_packed.m_left = msb;
var.m_packed.m_right = lsb;
@ -2921,18 +2924,20 @@ 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
std::string msg = (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.");
const std::string msg
= (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());
return nullptr;
}
void* VerilatedScope::exportFindError(int funcnum) const {
// Slowpath - Called only when find has failed
std::string msg = (std::string("Testbench C called '") + VerilatedImp::exportName(funcnum)
+ "' but this DPI export function exists only in other scopes, not scope '"
+ name() + "'");
const std::string msg
= (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());
return nullptr;
}

File diff suppressed because it is too large Load Diff

View File

@ -9,17 +9,18 @@
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
######################################################################
PERL = @PERL@
PYTHON3 = @PYTHON3@
AR = @AR@
CXX = @CXX@
LINK = @CXX@
AR = ar
RANLIB = ranlib
OBJCACHE ?= @OBJCACHE@
PERL = @PERL@
PYTHON3 = @PYTHON3@
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
CFG_WITH_LONGTESTS = @CFG_WITH_LONGTESTS@
# Compiler flags to enable profiling
CFG_CXXFLAGS_PROFILE = @CFG_CXXFLAGS_PROFILE@
# Select newest language
CFG_CXXFLAGS_STD_NEWEST = @CFG_CXXFLAGS_STD_NEWEST@
# Select oldest language (for Verilator internal testing only)
@ -104,6 +105,14 @@ OPT_FAST = -Os
# to change this as the library is small, but can have significant speed impact.
OPT_GLOBAL = -Os
#######################################################################
##### Profile builds
ifeq ($(VM_PROFC),1)
CPPFLAGS += $(CFG_CXXFLAGS_PROFILE)
LDFLAGS += $(CFG_CXXFLAGS_PROFILE)
endif
#######################################################################
##### SystemC builds

View File

@ -193,7 +193,7 @@ private:
// We used to backup and split on only .'s but it seems better to be verbose
// and not assume . is the separator
std::string prefix = std::string(a, apre - a);
const std::string prefix = std::string(a, apre - a);
// Scan backward to last mismatch
const char* apost = a + std::strlen(a) - 1;
@ -251,7 +251,7 @@ private:
public:
// PUBLIC METHODS
void forcePerInstance(bool flag) VL_MT_SAFE_EXCLUDES(m_mutex) {
void forcePerInstance(const bool flag) VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce();
const VerilatedLockGuard lock(m_mutex);
m_forcePerInstance = flag;
@ -261,7 +261,7 @@ public:
const VerilatedLockGuard lock(m_mutex);
clearGuts();
}
void clearNonMatch(const char* matchp) VL_MT_SAFE_EXCLUDES(m_mutex) {
void clearNonMatch(const char* const matchp) VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce();
const VerilatedLockGuard lock(m_mutex);
if (matchp && matchp[0]) {
@ -288,7 +288,7 @@ public:
assert(!m_insertp);
m_insertp = itemp;
}
void insertf(const char* filenamep, int lineno) VL_MT_SAFE_EXCLUDES(m_mutex) {
void insertf(const char* const filenamep, const int lineno) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock(m_mutex);
m_insertFilenamep = filenamep;
m_insertLineno = lineno;

View File

@ -79,19 +79,19 @@ void svPutBitselLogic(svLogicVecVal* dp, int bit, svLogic s) {
void svGetPartselBit(svBitVecVal* dp, const svBitVecVal* sp, int lsb, int width) {
// See also VL_SEL_WWI
int msb = lsb + width - 1;
int word_shift = VL_BITWORD_I(lsb);
const int msb = lsb + width - 1;
const int word_shift = VL_BITWORD_I(lsb);
if (VL_BITBIT_I(lsb) == 0) {
// Just a word extract
for (int i = 0; i < VL_WORDS_I(width); ++i) dp[i] = sp[i + word_shift];
} else {
int loffset = lsb & VL_SIZEBITS_I;
int nbitsfromlow = 32 - loffset; // bits that end up in lword (know loffset!=0)
const int loffset = lsb & VL_SIZEBITS_I;
const int nbitsfromlow = 32 - loffset; // bits that end up in lword (know loffset!=0)
// Middle words
int words = VL_WORDS_I(msb - lsb + 1);
const int words = VL_WORDS_I(msb - lsb + 1);
for (int i = 0; i < words; ++i) {
dp[i] = sp[i + word_shift] >> loffset;
int upperword = i + word_shift + 1;
const int upperword = i + word_shift + 1;
if (upperword <= static_cast<int>(VL_BITWORD_I(msb))) {
dp[i] |= sp[upperword] << nbitsfromlow;
}
@ -101,20 +101,20 @@ void svGetPartselBit(svBitVecVal* dp, const svBitVecVal* sp, int lsb, int width)
dp[VL_WORDS_I(width) - 1] &= VL_MASK_I(width);
}
void svGetPartselLogic(svLogicVecVal* dp, const svLogicVecVal* sp, int lsb, int width) {
int msb = lsb + width - 1;
int word_shift = VL_BITWORD_I(lsb);
const int msb = lsb + width - 1;
const int word_shift = VL_BITWORD_I(lsb);
if (VL_BITBIT_I(lsb) == 0) {
// Just a word extract
for (int i = 0; i < VL_WORDS_I(width); ++i) dp[i] = sp[i + word_shift];
} else {
int loffset = lsb & VL_SIZEBITS_I;
int nbitsfromlow = 32 - loffset; // bits that end up in lword (know loffset!=0)
const int loffset = lsb & VL_SIZEBITS_I;
const int nbitsfromlow = 32 - loffset; // bits that end up in lword (know loffset!=0)
// Middle words
int words = VL_WORDS_I(msb - lsb + 1);
const int words = VL_WORDS_I(msb - lsb + 1);
for (int i = 0; i < words; ++i) {
dp[i].aval = sp[i + word_shift].aval >> loffset;
dp[i].bval = sp[i + word_shift].bval >> loffset;
int upperword = i + word_shift + 1;
const int upperword = i + word_shift + 1;
if (upperword <= static_cast<int>(VL_BITWORD_I(msb))) {
dp[i].aval |= sp[upperword].aval << nbitsfromlow;
dp[i].bval |= sp[upperword].bval << nbitsfromlow;
@ -127,22 +127,22 @@ void svGetPartselLogic(svLogicVecVal* dp, const svLogicVecVal* sp, int lsb, int
}
void svPutPartselBit(svBitVecVal* dp, const svBitVecVal s, int lbit, int width) {
// See also _vl_insert_WI
int hbit = lbit + width - 1;
int hoffset = VL_BITBIT_I(hbit);
int loffset = VL_BITBIT_I(lbit);
const int hbit = lbit + width - 1;
const int hoffset = VL_BITBIT_I(hbit);
const int loffset = VL_BITBIT_I(lbit);
if (hoffset == VL_SIZEBITS_I && loffset == 0) {
// Fast and common case, word based insertion
dp[VL_BITWORD_I(lbit)] = s;
} else {
int hword = VL_BITWORD_I(hbit);
int lword = VL_BITWORD_I(lbit);
const int hword = VL_BITWORD_I(hbit);
const int lword = VL_BITWORD_I(lbit);
if (hword == lword) { // know < 32 bits because above checks it
IData insmask = (VL_MASK_I(hoffset - loffset + 1)) << loffset;
const IData insmask = (VL_MASK_I(hoffset - loffset + 1)) << loffset;
dp[lword] = (dp[lword] & ~insmask) | ((s << loffset) & insmask);
} else {
IData hinsmask = (VL_MASK_I(hoffset - 0 + 1)) << 0;
IData linsmask = (VL_MASK_I(31 - loffset + 1)) << loffset;
int nbitsonright = 32 - loffset; // bits that end up in lword
const IData hinsmask = (VL_MASK_I(hoffset - 0 + 1)) << 0;
const IData linsmask = (VL_MASK_I(31 - loffset + 1)) << loffset;
const int nbitsonright = 32 - loffset; // bits that end up in lword
dp[lword] = (dp[lword] & ~linsmask) | ((s << loffset) & linsmask);
dp[hword] = (dp[hword] & ~hinsmask) | ((s >> nbitsonright) & hinsmask);
}
@ -150,24 +150,24 @@ void svPutPartselBit(svBitVecVal* dp, const svBitVecVal s, int lbit, int width)
}
// cppcheck-suppress passedByValue
void svPutPartselLogic(svLogicVecVal* dp, const svLogicVecVal s, int lbit, int width) {
int hbit = lbit + width - 1;
int hoffset = VL_BITBIT_I(hbit);
int loffset = VL_BITBIT_I(lbit);
const int hbit = lbit + width - 1;
const int hoffset = VL_BITBIT_I(hbit);
const int loffset = VL_BITBIT_I(lbit);
if (hoffset == VL_SIZEBITS_I && loffset == 0) {
// Fast and common case, word based insertion
dp[VL_BITWORD_I(lbit)].aval = s.aval;
dp[VL_BITWORD_I(lbit)].bval = s.bval;
} else {
int hword = VL_BITWORD_I(hbit);
int lword = VL_BITWORD_I(lbit);
const int hword = VL_BITWORD_I(hbit);
const int lword = VL_BITWORD_I(lbit);
if (hword == lword) { // know < 32 bits because above checks it
IData insmask = (VL_MASK_I(hoffset - loffset + 1)) << loffset;
const IData insmask = (VL_MASK_I(hoffset - loffset + 1)) << loffset;
dp[lword].aval = (dp[lword].aval & ~insmask) | ((s.aval << loffset) & insmask);
dp[lword].bval = (dp[lword].bval & ~insmask) | ((s.bval << loffset) & insmask);
} else {
IData hinsmask = (VL_MASK_I(hoffset - 0 + 1)) << 0;
IData linsmask = (VL_MASK_I(31 - loffset + 1)) << loffset;
int nbitsonright = 32 - loffset; // bits that end up in lword
const IData hinsmask = (VL_MASK_I(hoffset - 0 + 1)) << 0;
const IData linsmask = (VL_MASK_I(31 - loffset + 1)) << loffset;
const int nbitsonright = 32 - loffset; // bits that end up in lword
dp[lword].aval = (dp[lword].aval & ~linsmask) | ((s.aval << loffset) & linsmask);
dp[lword].bval = (dp[lword].bval & ~linsmask) | ((s.bval << loffset) & linsmask);
dp[hword].aval = (dp[hword].aval & ~hinsmask) | ((s.aval >> nbitsonright) & hinsmask);
@ -279,7 +279,7 @@ static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s,
case VLVT_UINT16: d[0] = *(reinterpret_cast<SData*>(datap)); return;
case VLVT_UINT32: d[0] = *(reinterpret_cast<IData*>(datap)); return;
case VLVT_UINT64: {
WData lwp[2];
VlWide<2> lwp;
VL_SET_WQ(lwp, *(reinterpret_cast<QData*>(datap)));
d[0] = lwp[0];
d[1] = lwp[1];
@ -316,7 +316,7 @@ static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandl
d[0].bval = 0;
return;
case VLVT_UINT64: {
WData lwp[2];
VlWide<2> lwp;
VL_SET_WQ(lwp, *(reinterpret_cast<QData*>(datap)));
d[0].aval = lwp[0];
d[0].bval = 0;
@ -460,13 +460,13 @@ void svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int
switch (varp->udims()) {
case 1: _vl_svPutBitArrElemVecVal(d, s, 1, indx1, 0, 0); break;
case 2: {
int indx2 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
_vl_svPutBitArrElemVecVal(d, s, 2, indx1, indx2, 0);
break;
}
case 3: {
int indx2 = va_arg(ap, int);
int indx3 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
const int indx3 = va_arg(ap, int);
_vl_svPutBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
break;
}
@ -492,13 +492,13 @@ void svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s,
switch (varp->udims()) {
case 1: _vl_svPutLogicArrElemVecVal(d, s, 1, indx1, 0, 0); break;
case 2: {
int indx2 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
_vl_svPutLogicArrElemVecVal(d, s, 2, indx1, indx2, 0);
break;
}
case 3: {
int indx2 = va_arg(ap, int);
int indx3 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
const int indx3 = va_arg(ap, int);
_vl_svPutLogicArrElemVecVal(d, s, 3, indx1, indx2, indx3);
break;
}
@ -528,13 +528,13 @@ void svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1,
switch (varp->udims()) {
case 1: _vl_svGetBitArrElemVecVal(d, s, 1, indx1, 0, 0); break;
case 2: {
int indx2 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
_vl_svGetBitArrElemVecVal(d, s, 2, indx1, indx2, 0);
break;
}
case 3: {
int indx2 = va_arg(ap, int);
int indx3 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
const int indx3 = va_arg(ap, int);
_vl_svGetBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
break;
}
@ -559,13 +559,13 @@ void svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int in
switch (varp->udims()) {
case 1: _vl_svGetLogicArrElemVecVal(d, s, 1, indx1, 0, 0); break;
case 2: {
int indx2 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
_vl_svGetLogicArrElemVecVal(d, s, 2, indx1, indx2, 0);
break;
}
case 3: {
int indx2 = va_arg(ap, int);
int indx3 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
const int indx3 = va_arg(ap, int);
_vl_svGetLogicArrElemVecVal(d, s, 3, indx1, indx2, indx3);
break;
}
@ -592,13 +592,13 @@ svBit svGetBitArrElem(const svOpenArrayHandle s, int indx1, ...) {
switch (varp->udims()) {
case 1: out = _vl_svGetBitArrElem(s, 1, indx1, 0, 0, 0); break;
case 2: {
int indx2 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
out = _vl_svGetBitArrElem(s, 2, indx1, indx2, 0, 0);
break;
}
case 3: {
int indx2 = va_arg(ap, int);
int indx3 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
const int indx3 = va_arg(ap, int);
out = _vl_svGetBitArrElem(s, 3, indx1, indx2, indx3, 0);
break;
}
@ -625,13 +625,13 @@ svLogic svGetLogicArrElem(const svOpenArrayHandle s, int indx1, ...) {
switch (varp->udims()) {
case 1: out = _vl_svGetBitArrElem(s, 1, indx1, 0, 0, 0); break;
case 2: {
int indx2 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
out = _vl_svGetBitArrElem(s, 2, indx1, indx2, 0, 0);
break;
}
case 3: {
int indx2 = va_arg(ap, int);
int indx3 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
const int indx3 = va_arg(ap, int);
out = _vl_svGetBitArrElem(s, 3, indx1, indx2, indx3, 0);
break;
}
@ -660,13 +660,13 @@ void svPutBitArrElem(const svOpenArrayHandle d, svBit value, int indx1, ...) {
switch (varp->udims()) {
case 1: _vl_svPutBitArrElem(d, value, 1, indx1, 0, 0, 0); break;
case 2: {
int indx2 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
_vl_svPutBitArrElem(d, value, 2, indx1, indx2, 0, 0);
break;
}
case 3: {
int indx2 = va_arg(ap, int);
int indx3 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
const int indx3 = va_arg(ap, int);
_vl_svPutBitArrElem(d, value, 3, indx1, indx2, indx3, 0);
break;
}
@ -691,13 +691,13 @@ void svPutLogicArrElem(const svOpenArrayHandle d, svLogic value, int indx1, ...)
switch (varp->udims()) {
case 1: _vl_svPutBitArrElem(d, value, 1, indx1, 0, 0, 0); break;
case 2: {
int indx2 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
_vl_svPutBitArrElem(d, value, 2, indx1, indx2, 0, 0);
break;
}
case 3: {
int indx2 = va_arg(ap, int);
int indx3 = va_arg(ap, int);
const int indx2 = va_arg(ap, int);
const int indx3 = va_arg(ap, int);
_vl_svPutBitArrElem(d, value, 3, indx1, indx2, indx3, 0);
break;
}

View File

@ -28,6 +28,7 @@
#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"
@ -37,7 +38,7 @@
// Convert svBitVecVal to Verilator internal data
static inline void VL_SET_W_SVBV(int obits, WDataOutP owp, const svBitVecVal* lwp) VL_MT_SAFE {
int words = VL_WORDS_I(obits);
const int words = VL_WORDS_I(obits);
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i];
owp[words - 1] = lwp[words - 1] & VL_MASK_I(obits);
}
@ -47,20 +48,20 @@ static inline QData VL_SET_Q_SVBV(const svBitVecVal* lwp) VL_MT_SAFE {
static inline IData VL_SET_I_SVBV(const svBitVecVal* lwp) VL_MT_SAFE { return lwp[0]; }
// Convert Verilator internal data to svBitVecVal
static inline void VL_SET_SVBV_W(int obits, svBitVecVal* owp, WDataInP lwp) VL_MT_SAFE {
int words = VL_WORDS_I(obits);
static inline void VL_SET_SVBV_W(int obits, svBitVecVal* owp, const WDataInP lwp) VL_MT_SAFE {
const int words = VL_WORDS_I(obits);
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i];
owp[words - 1] = lwp[words - 1] & VL_MASK_I(obits);
}
static inline void VL_SET_SVBV_I(int, svBitVecVal* owp, IData ld) VL_MT_SAFE { owp[0] = ld; }
static inline void VL_SET_SVBV_Q(int, svBitVecVal* owp, QData ld) VL_MT_SAFE {
static inline void VL_SET_SVBV_I(int, svBitVecVal* owp, const IData ld) VL_MT_SAFE { owp[0] = ld; }
static inline void VL_SET_SVBV_Q(int, svBitVecVal* owp, const QData ld) VL_MT_SAFE {
VL_SET_WQ(owp, ld);
}
// Convert svLogicVecVal to Verilator internal data
// Note these functions ignore X/Z in svLogicVecVal
static inline void VL_SET_W_SVLV(int obits, WDataOutP owp, const svLogicVecVal* lwp) VL_MT_SAFE {
int words = VL_WORDS_I(obits);
const int words = VL_WORDS_I(obits);
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i].aval;
owp[words - 1] = lwp[words - 1].aval & VL_MASK_I(obits);
}
@ -71,18 +72,18 @@ static inline IData VL_SET_I_SVLV(const svLogicVecVal* lwp) VL_MT_SAFE { return
// Convert Verilator internal data to svLogicVecVal
// Note these functions never create X/Z in svLogicVecVal
static inline void VL_SET_SVLV_W(int obits, svLogicVecVal* owp, WDataInP lwp) VL_MT_SAFE {
int words = VL_WORDS_I(obits);
static inline void VL_SET_SVLV_W(int obits, svLogicVecVal* owp, const WDataInP lwp) VL_MT_SAFE {
const int words = VL_WORDS_I(obits);
for (int i = 0; i < words; ++i) owp[i].bval = 0;
for (int i = 0; i < words - 1; ++i) owp[i].aval = lwp[i];
owp[words - 1].aval = lwp[words - 1] & VL_MASK_I(obits);
}
static inline void VL_SET_SVLV_I(int, svLogicVecVal* owp, IData ld) VL_MT_SAFE {
static inline void VL_SET_SVLV_I(int, svLogicVecVal* owp, const IData ld) VL_MT_SAFE {
owp[0].aval = ld;
owp[0].bval = 0;
}
static inline void VL_SET_SVLV_Q(int, svLogicVecVal* owp, QData ld) VL_MT_SAFE {
WData lwp[2];
static inline void VL_SET_SVLV_Q(int, svLogicVecVal* owp, const QData ld) VL_MT_SAFE {
VlWide<2> lwp;
VL_SET_WQ(lwp, ld);
owp[0].aval = lwp[0];
owp[0].bval = 0;

View File

@ -150,7 +150,7 @@ void VerilatedFst::emitTimeChange(vluint64_t timeui) { fstWriterEmitTimeChange(m
void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, vluint32_t elements,
unsigned int minValbits, const char** itemNamesp,
const char** itemValuesp) {
fstEnumHandle enumNum
const fstEnumHandle enumNum
= fstWriterCreateEnumTable(m_fst, name, elements, minValbits, itemNamesp, itemValuesp);
m_local2fstdtype[dtypenum] = enumNum;
}
@ -206,7 +206,7 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
std::string name_str = name_ss.str();
if (dtypenum > 0) {
fstEnumHandle enumNum = m_local2fstdtype[dtypenum];
const fstEnumHandle enumNum = m_local2fstdtype[dtypenum];
fstWriterEmitEnumTableRef(m_fst, enumNum);
}

View File

@ -42,9 +42,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.
sc_time t1sec(1, SC_SEC);
const sc_time t1sec(1, SC_SEC);
if (t1sec.to_default_time_units() != 0) {
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

@ -43,7 +43,7 @@ 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, WDataInP obj);
extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
//===================================================================
// Shuffle RNG
@ -89,19 +89,24 @@ public:
};
//===================================================================
/// Verilog wide unpacked bit container.
/// 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> class VlWide final {
EData m_storage[T_Words];
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
public:
// CONSTRUCTORS
// cppcheck-suppress uninitVar
VlWide() = default;
~VlWide() = default;
@ -111,10 +116,8 @@ public:
// OPERATOR METHODS
VlWide& operator=(const VlWide&) = default;
VlWide& operator=(VlWide&&) = default;
const EData& operator[](size_t index) const { return m_storage[index]; };
EData& operator[](size_t index) { return m_storage[index]; };
operator WDataOutP() { return &m_storage[0]; }
operator WDataInP() const { return &m_storage[0]; }
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]; }
@ -128,7 +131,8 @@ public:
// 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(WDataInP inp, const VlWide<T_Words>&) {
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);
}
@ -792,32 +796,27 @@ void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
VlWriteMem wmem(hex, bits, filename, start, end);
if (VL_UNLIKELY(!wmem.isOpen())) return;
for (const auto& i : obj) {
QData addr = i.first;
const QData addr = i.first;
if (addr >= start && addr <= end) wmem.print(addr, true, &(i.second));
}
}
//===================================================================
/// Verilog packed array container
/// 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> class VlUnpacked final {
private:
// TYPES
using Array = std::array<T_Value, T_Depth>;
public:
using const_iterator = typename Array::const_iterator;
private:
template <class T_Value, std::size_t T_Depth> struct VlUnpacked final {
// MEMBERS
Array m_array; // Contents of the packed array
// This should be the only data member, otherwise generated static initializers need updating
T_Value m_storage[T_Depth]; // Contents of the unpacked array
public:
// CONSTRUCTORS
VlUnpacked() = default;
~VlUnpacked() = default;
@ -828,18 +827,18 @@ public:
// METHODS
// Raw access
WData* data() { return &m_array[0]; }
const WData* data() const { return &m_array[0]; }
WData* data() { return &m_storage[0]; }
const WData* data() const { return &m_storage[0]; }
T_Value& operator[](size_t index) { return m_array[index]; };
const T_Value& operator[](size_t index) const { return m_array[index]; };
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_array[i]);
out += comma + VL_TO_STRING(m_storage[i]);
comma = ", ";
}
return out + "} ";
@ -878,16 +877,16 @@ static inline bool VL_CAST_DYNAMIC(VlClassRef<T> in, VlClassRef<U>& outr) {
//======================================================================
// Conversion functions
extern std::string VL_CVT_PACK_STR_NW(int lwords, WDataInP lwp) VL_MT_SAFE;
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 {
WData lw[VL_WQ_WORDS_E];
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 {
WData lw[VL_WQ_WORDS_E];
VlWide<VL_WQ_WORDS_E> lw;
VL_SET_WI(lw, lhs);
return VL_CVT_PACK_STR_NW(1, lw);
}
@ -926,31 +925,31 @@ extern void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suff
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 {
WData rwp[2]; // WData must always be at least 2
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 {
WData rwp[2]; // WData must always be at least 2
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 {
WData rwp[2];
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 {
WData rwp[2];
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 {
WData rwp[2];
VlWide<2> rwp;
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
if (got) rdr = VL_CVT_D_Q(VL_SET_QW(rwp));
return got;

View File

@ -72,9 +72,10 @@ bool VerilatedDeserialize::readDiffers(const void* __restrict datap,
VerilatedDeserialize& VerilatedDeserialize::readAssert(const void* __restrict datap,
size_t size) VL_MT_UNSAFE_ONE {
if (VL_UNLIKELY(readDiffers(datap, size))) {
std::string fn = filename();
std::string msg = "Can't deserialize save-restore file as was made from different model: "
+ filename();
const std::string fn = filename();
const std::string msg
= "Can't deserialize save-restore file as was made from different model: "
+ filename();
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
// Die before we close() as close would infinite loop
}
@ -90,8 +91,8 @@ void VerilatedSerialize::header() VL_MT_UNSAFE_ONE {
void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE {
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, std::strlen(VLTSAVE_HEADER_STR)))) {
std::string fn = filename();
std::string msg
const std::string fn = filename();
const std::string msg
= std::string(
"Can't deserialize; file has wrong header signature, or file not found: ")
+ filename();
@ -109,9 +110,10 @@ void VerilatedSerialize::trailer() VL_MT_UNSAFE_ONE {
void VerilatedDeserialize::trailer() VL_MT_UNSAFE_ONE {
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_TRAILER_STR, std::strlen(VLTSAVE_TRAILER_STR)))) {
std::string fn = filename();
std::string msg = std::string("Can't deserialize; file has wrong end-of-file signature: ")
+ filename();
const std::string fn = filename();
const std::string msg
= 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
}
@ -192,10 +194,10 @@ void VerilatedSave::flush() VL_MT_UNSAFE_ONE {
if (VL_UNLIKELY(!isOpen())) return;
vluint8_t* wp = m_bufp;
while (true) {
ssize_t remaining = (m_cp - wp);
const ssize_t remaining = (m_cp - wp);
if (remaining == 0) break;
errno = 0;
ssize_t got = ::write(m_fd, wp, remaining);
const ssize_t got = ::write(m_fd, wp, remaining);
if (got > 0) {
wp += got;
} else if (VL_UNCOVERABLE(got < 0)) {
@ -223,17 +225,17 @@ void VerilatedRestore::fill() VL_MT_UNSAFE_ONE {
m_cp = m_bufp; // Reset buffer
// Read into buffer starting at m_endp
while (true) {
ssize_t remaining = (m_bufp + bufferSize() - m_endp);
const ssize_t remaining = (m_bufp + bufferSize() - m_endp);
if (remaining == 0) break;
errno = 0;
ssize_t got = ::read(m_fd, m_endp, remaining);
const ssize_t got = ::read(m_fd, m_endp, remaining);
if (got > 0) {
m_endp += got;
} else if (VL_UNCOVERABLE(got < 0)) {
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);
const std::string msg = std::string(__FUNCTION__) + ": " + std::strerror(errno);
VL_FATAL_MT("", 0, "", msg.c_str());
close();
break;

View File

@ -236,43 +236,43 @@ public:
//=============================================================================
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, vluint64_t& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint64_t& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint64_t& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, vluint32_t& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint32_t& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint32_t& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, vluint16_t& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint16_t& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint16_t& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, vluint8_t& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint8_t& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint8_t& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, bool& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const bool& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, bool& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, double& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const double& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, double& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, float& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const float& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, float& rhs) {
@ -295,11 +295,11 @@ VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhs
template <class T_Key, class T_Value>
VerilatedSerialize& operator<<(VerilatedSerialize& os, VlAssocArray<T_Key, T_Value>& rhs) {
os << rhs.atDefault();
vluint32_t len = rhs.size();
const vluint32_t len = rhs.size();
os << len;
for (const auto& i : rhs) {
T_Key index = i.first; // Copy to get around const_iterator
T_Value value = i.second;
const T_Key index = i.first; // Copy to get around const_iterator
const T_Value value = i.second;
os << index << value;
}
return os;

View File

@ -152,30 +152,34 @@ public:
const VerilatedRange& unpacked() const { return m_unpacked[0]; }
// DPI accessors
int left(int dim) const {
return dim == 0 ? m_packed.left()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].left() : 0;
return dim == 0 ? m_packed.left()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].left()
: 0;
}
int right(int dim) const {
return dim == 0 ? m_packed.right()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].right() : 0;
return dim == 0 ? m_packed.right()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].right()
: 0;
}
int low(int dim) const {
return dim == 0 ? m_packed.low()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].low() : 0;
return dim == 0 ? m_packed.low()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].low()
: 0;
}
int high(int dim) const {
return dim == 0 ? m_packed.high()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].high() : 0;
return dim == 0 ? m_packed.high()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].high()
: 0;
}
int increment(int dim) const {
return dim == 0
? m_packed.increment()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].increment() : 0;
return dim == 0 ? m_packed.increment()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].increment()
: 0;
}
int elements(int dim) const {
return dim == 0
? m_packed.elements()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].elements() : 0;
return dim == 0 ? m_packed.elements()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].elements()
: 0;
}
// Total size in bytes (note DPI limited to 4GB)
size_t totalSize() const;

View File

@ -74,7 +74,7 @@ void VlWorkerThread::workerLoop() {
if (VL_UNLIKELY(m_exiting.load(std::memory_order_acquire))) break;
if (VL_LIKELY(work.m_fnp)) {
work.m_fnp(work.m_evenCycle, work.m_sym);
work.m_fnp(work.m_selfp, work.m_evenCycle);
work.m_fnp = nullptr;
}
}
@ -93,7 +93,7 @@ void VlWorkerThread::startWorker(VlWorkerThread* workerp) {
VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads, bool profiling)
: m_profiling{profiling} {
// --threads N passes nThreads=N-1, as the "main" threads counts as 1
unsigned cpus = std::thread::hardware_concurrency();
const unsigned cpus = std::thread::hardware_concurrency();
if (cpus < nThreads + 1) {
static int warnedOnce = 0;
if (!warnedOnce++) {
@ -149,7 +149,7 @@ void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
const VerilatedLockGuard lk(m_mutex);
VL_DEBUG_IF(VL_DBG_MSGF("+prof+threads writing to '%s'\n", filenamep););
FILE* fp = std::fopen(filenamep, "w");
FILE* const fp = std::fopen(filenamep, "w");
if (VL_UNLIKELY(!fp)) {
VL_FATAL_MT(filenamep, 0, "", "+prof+threads+file file not writable");
// cppcheck-suppress resourceLeak // bug, doesn't realize fp is nullptr

View File

@ -48,12 +48,12 @@
#endif
// clang-format on
// VlMTaskVertex and VlThreadpool will work with multiple symbol table types.
// VlMTaskVertex and VlThreadpool will work with multiple model class types.
// Since the type is opaque to VlMTaskVertex and VlThreadPool, represent it
// as a void* here.
using VlThrSymTab = void*;
using VlSelfP = void*;
using VlExecFnp = void (*)(bool, VlThrSymTab);
using VlExecFnp = void (*)(VlSelfP, bool);
// Track dependencies for a single MTask.
class VlMTaskVertex final {
@ -99,19 +99,19 @@ public:
// false while it's still waiting on more dependencies.
inline bool signalUpstreamDone(bool evenCycle) {
if (evenCycle) {
vluint32_t upstreamDepsDone
const vluint32_t upstreamDepsDone
= 1 + m_upstreamDepsDone.fetch_add(1, std::memory_order_release);
assert(upstreamDepsDone <= m_upstreamDepCount);
return (upstreamDepsDone == m_upstreamDepCount);
} else {
vluint32_t upstreamDepsDone_prev
const vluint32_t upstreamDepsDone_prev
= m_upstreamDepsDone.fetch_sub(1, std::memory_order_release);
assert(upstreamDepsDone_prev > 0);
return (upstreamDepsDone_prev == 1);
}
}
inline bool areUpstreamDepsDone(bool evenCycle) const {
vluint32_t target = evenCycle ? m_upstreamDepCount : 0;
const vluint32_t target = evenCycle ? m_upstreamDepCount : 0;
return m_upstreamDepsDone.load(std::memory_order_acquire) == target;
}
inline void waitUntilUpstreamDone(bool evenCycle) const {
@ -177,15 +177,15 @@ private:
// TYPES
struct ExecRec {
VlExecFnp m_fnp; // Function to execute
VlThrSymTab m_sym; // Symbol table to execute
VlSelfP m_selfp; // Symbol table to execute
bool m_evenCycle; // Even/odd for flag alternation
ExecRec()
: m_fnp{nullptr}
, m_sym{nullptr}
, m_selfp{nullptr}
, m_evenCycle{false} {}
ExecRec(VlExecFnp fnp, bool evenCycle, VlThrSymTab sym)
ExecRec(VlExecFnp fnp, VlSelfP selfp, bool evenCycle)
: m_fnp{fnp}
, m_sym{sym}
, m_selfp{selfp}
, m_evenCycle{evenCycle} {}
};
@ -237,13 +237,13 @@ public:
m_ready.erase(m_ready.begin());
m_ready_size.fetch_sub(1, std::memory_order_relaxed);
}
inline void wakeUp() { addTask(nullptr, false, nullptr); }
inline void addTask(VlExecFnp fnp, bool evenCycle, VlThrSymTab sym)
inline void wakeUp() { addTask(nullptr, nullptr, false); }
inline void addTask(VlExecFnp fnp, VlSelfP selfp, bool evenCycle)
VL_MT_SAFE_EXCLUDES(m_mutex) {
bool notify;
{
const VerilatedLockGuard lk(m_mutex);
m_ready.emplace_back(fnp, evenCycle, sym);
m_ready.emplace_back(fnp, selfp, evenCycle);
m_ready_size.fetch_add(1, std::memory_order_relaxed);
notify = m_waiting;
}

View File

@ -129,7 +129,7 @@ void VerilatedVcd::openNextImp(bool incFilename) {
if (incFilename) {
// Find _0000.{ext} in filename
std::string name = m_filename;
size_t pos = name.rfind('.');
const size_t pos = name.rfind('.');
if (pos > 8 && 0 == std::strncmp("_cat", name.c_str() + pos - 8, 4)
&& std::isdigit(name.c_str()[pos - 4]) && std::isdigit(name.c_str()[pos - 3])
&& std::isdigit(name.c_str()[pos - 2]) && std::isdigit(name.c_str()[pos - 1])) {
@ -299,10 +299,10 @@ void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
if (VL_UNLIKELY(!isOpen())) return;
char* wp = m_wrBufp;
while (true) {
ssize_t remaining = (m_writep - wp);
const ssize_t remaining = (m_writep - wp);
if (remaining == 0) break;
errno = 0;
ssize_t got = m_filep->write(wp, remaining);
const ssize_t got = m_filep->write(wp, remaining);
if (got > 0) {
wp += got;
m_wroteBytes += got;
@ -352,7 +352,7 @@ void VerilatedVcd::dumpHeader() {
printStr("$version Generated by VerilatedVcd $end\n");
printStr("$date ");
{
time_t tick = time(nullptr);
const time_t tick = time(nullptr);
tm ticktm;
VL_LOCALTIME_R(&tick, &ticktm);
constexpr int bufsize = 50;

View File

@ -233,11 +233,11 @@ public:
// Inside dumping routines, dump one signal if it has changed.
// We do want to inline these to avoid calls when the value did not change.
inline void chgBit(vluint32_t code, const vluint32_t newval) {
vluint32_t diff = oldp(code)[0] ^ newval;
const vluint32_t diff = oldp(code)[0] ^ newval;
if (VL_UNLIKELY(diff)) fullBit(code, newval);
}
inline void chgBus(vluint32_t code, const vluint32_t newval, int bits) {
vluint32_t diff = oldp(code)[0] ^ newval;
const vluint32_t diff = oldp(code)[0] ^ newval;
if (VL_UNLIKELY(diff)) {
if (VL_UNLIKELY(bits == 32 || (diff & ((1U << bits) - 1)))) {
fullBus(code, newval, bits);
@ -245,7 +245,7 @@ public:
}
}
inline void chgQuad(vluint32_t code, const vluint64_t newval, int bits) {
vluint64_t diff = (*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval;
const vluint64_t diff = (*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval;
if (VL_UNLIKELY(diff)) {
if (VL_UNLIKELY(bits == 64 || (diff & ((1ULL << bits) - 1)))) {
fullQuad(code, newval, bits);
@ -270,7 +270,7 @@ public:
}
}
inline void chgTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
const vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
if (VL_UNLIKELY(diff)) {
// Verilator 3.510 and newer provide clean input, so the below
// is only for back compatibility
@ -281,7 +281,7 @@ public:
}
inline void chgTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri,
int bits) {
vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
const vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
if (VL_UNLIKELY(diff)) {
if (VL_UNLIKELY(bits == 32 || (diff & ((1U << bits) - 1)))) {
fullTriBus(code, newval, newtri, bits);
@ -290,8 +290,8 @@ public:
}
inline void chgTriQuad(vluint32_t code, const vluint64_t newval, const vluint64_t newtri,
int bits) {
vluint64_t diff = (((*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval)
| ((*(reinterpret_cast<vluint64_t*>(oldp(code + 1)))) ^ newtri));
const vluint64_t diff = (((*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval)
| ((*(reinterpret_cast<vluint64_t*>(oldp(code + 1)))) ^ newtri));
if (VL_UNLIKELY(diff)) {
if (VL_UNLIKELY(bits == 64 || (diff & ((1ULL << bits) - 1)))) {
fullTriQuad(code, newval, newtri, bits);

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.
sc_time t1sec(1, SC_SEC);
const sc_time t1sec(1, SC_SEC);
if (t1sec.to_default_time_units() != 0) {
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

@ -310,7 +310,7 @@ public:
return dynamic_cast<VerilatedVpioVar*>(reinterpret_cast<VerilatedVpio*>(h));
}
vluint32_t mask() const { return m_mask.u32; }
vluint8_t mask_byte(int idx) { return m_mask.u8[idx & 3]; }
vluint8_t mask_byte(int idx) const { return m_mask.u8[idx & 3]; }
vluint32_t entSize() const { return m_entSize; }
vluint32_t index() const { return m_index; }
virtual vluint32_t type() const override {
@ -365,7 +365,7 @@ public:
virtual vluint32_t type() const override { return vpiIterator; }
virtual vpiHandle dovpi_scan() override {
if (VL_LIKELY(m_scopep->varsp())) {
VerilatedVarNameMap* varsp = m_scopep->varsp();
const VerilatedVarNameMap* const varsp = m_scopep->varsp();
if (VL_UNLIKELY(!m_started)) {
m_it = varsp->begin();
m_started = true;
@ -564,7 +564,7 @@ public:
}
static void callTimedCbs() VL_MT_UNSAFE_ONE {
assertOneCheck();
QData time = VL_TIME_Q();
const QData time = VL_TIME_Q();
for (auto it = s().m_timedCbs.begin(); it != s().m_timedCbs.end();) {
if (VL_UNLIKELY(it->first.first <= time)) {
VerilatedVpiCbHolder& ho = it->second;
@ -587,7 +587,7 @@ public:
if (VL_LIKELY(it != s().m_timedCbs.cend())) return it->first.first;
return ~0ULL; // maxquad
}
static bool callCbs(vluint32_t reason) VL_MT_UNSAFE_ONE {
static bool callCbs(const vluint32_t reason) VL_MT_UNSAFE_ONE {
VpioCbList& cbObjList = s().m_cbObjLists[reason];
bool called = false;
if (cbObjList.empty()) return called;
@ -619,7 +619,7 @@ public:
const auto last = std::prev(cbObjList.end()); // prevent looping over newly added elements
for (auto it = cbObjList.begin(); true;) {
// cbReasonRemove sets to nullptr, so we know on removal the old end() will still exist
bool was_last = it == last;
const bool was_last = it == last;
if (VL_UNLIKELY(it->invalid())) { // Deleted earlier, cleanup
it = cbObjList.erase(it);
if (was_last) break;
@ -715,7 +715,7 @@ public:
void resetError() { m_flag = false; }
static void vpi_unsupported() {
// Not supported yet
p_vpi_error_info error_info_p = VerilatedVpiImp::error_info()->getError();
const p_vpi_error_info error_info_p = VerilatedVpiImp::error_info()->getError();
if (error_info_p) {
VL_FATAL_MT(error_info_p->file, error_info_p->line, "", error_info_p->message);
return;
@ -1300,9 +1300,9 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
case cbAfterDelay: {
QData time = 0;
if (cb_data_p->time) time = VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low);
QData abstime = VL_TIME_Q() + time;
const QData abstime = VL_TIME_Q() + time;
vluint64_t id = VerilatedVpiImp::nextCallbackId();
VerilatedVpioTimedCb* vop = new VerilatedVpioTimedCb{id, abstime};
VerilatedVpioTimedCb* const vop = new VerilatedVpioTimedCb{id, abstime};
VerilatedVpiImp::cbTimedAdd(id, cb_data_p, abstime);
return vop->castVpiHandle();
}
@ -1316,8 +1316,8 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
case cbEnterInteractive: // FALLTHRU // NOP, but need to return handle, so make object
case cbExitInteractive: // FALLTHRU // NOP, but need to return handle, so make object
case cbInteractiveScopeChange: { // FALLTHRU // NOP, but need to return handle, so make object
vluint64_t id = VerilatedVpiImp::nextCallbackId();
VerilatedVpioReasonCb* vop = new VerilatedVpioReasonCb{id, cb_data_p->reason};
const vluint64_t id = VerilatedVpiImp::nextCallbackId();
VerilatedVpioReasonCb* const vop = new VerilatedVpioReasonCb{id, cb_data_p->reason};
VerilatedVpiImp::cbReasonAdd(id, cb_data_p);
return vop->castVpiHandle();
}
@ -1332,7 +1332,7 @@ PLI_INT32 vpi_remove_cb(vpiHandle cb_obj) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_remove_cb %p\n", cb_obj););
VerilatedVpiImp::assertOneCheck();
VL_VPI_ERROR_RESET_();
VerilatedVpio* vop = VerilatedVpio::castp(cb_obj);
VerilatedVpio* const vop = VerilatedVpio::castp(cb_obj);
if (VL_UNLIKELY(!vop)) return 0;
return vop->dovpi_remove_cb();
}
@ -1355,7 +1355,7 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle_by_name %s %p\n", namep, scope););
const VerilatedVar* varp = nullptr;
const VerilatedScope* scopep;
VerilatedVpioScope* voScopep = VerilatedVpioScope::castp(scope);
const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope);
std::string scopeAndName = namep;
if (voScopep) {
scopeAndName = std::string(voScopep->fullname()) + "." + namep;
@ -1373,7 +1373,7 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
}
const char* baseNamep = scopeAndName.c_str();
std::string scopename;
const char* dotp = std::strrchr(namep, '.');
const char* const dotp = std::strrchr(namep, '.');
if (VL_LIKELY(dotp)) {
baseNamep = dotp + 1;
scopename = std::string(namep, dotp - namep);
@ -1407,7 +1407,7 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) {
// Memory words are not indexable
VerilatedVpioMemoryWord* vop = VerilatedVpioMemoryWord::castp(object);
if (VL_UNLIKELY(vop)) return nullptr;
VerilatedVpioVar* varop = VerilatedVpioVar::castp(object);
const VerilatedVpioVar* const varop = VerilatedVpioVar::castp(object);
if (VL_LIKELY(varop)) {
if (varop->varp()->dims() < 2) return nullptr;
if (VL_LIKELY(varop->varp()->unpacked().left() >= varop->varp()->unpacked().right())) {
@ -1496,7 +1496,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
VL_VPI_ERROR_RESET_();
switch (type) {
case vpiMemoryWord: {
VerilatedVpioVar* vop = VerilatedVpioVar::castp(object);
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
if (vop->varp()->dims() < 2) return nullptr;
if (vop->varp()->dims() > 2) {
@ -1508,7 +1508,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
return (new VerilatedVpioMemoryWordIter(object, vop->varp()))->castVpiHandle();
}
case vpiRange: {
VerilatedVpioVar* vop = VerilatedVpioVar::castp(object);
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
if (vop->varp()->dims() < 2) return nullptr;
// Unsupported is multidim list
@ -1521,15 +1521,15 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
return ((new VerilatedVpioRangeIter(vop->rangep()))->castVpiHandle());
}
case vpiReg: {
VerilatedVpioScope* vop = VerilatedVpioScope::castp(object);
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
return ((new VerilatedVpioVarIter(vop->scopep()))->castVpiHandle());
}
case vpiModule: {
VerilatedVpioModule* vop = VerilatedVpioModule::castp(object);
const VerilatedHierarchyMap* map = VerilatedImp::hierarchyMap();
const VerilatedScope* mod = vop ? vop->scopep() : nullptr;
const auto it = vlstd::as_const(map)->find(const_cast<VerilatedScope*>(mod));
const VerilatedVpioModule* const vop = VerilatedVpioModule::castp(object);
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
const VerilatedScope* const modp = vop ? vop->scopep() : nullptr;
const auto it = vlstd::as_const(map)->find(const_cast<VerilatedScope*>(modp));
if (it == map->end()) return nullptr;
return ((new VerilatedVpioModuleIter(it->second))->castVpiHandle());
}
@ -1560,30 +1560,30 @@ PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) {
return Verilated::threadContextp()->timeprecision();
}
case vpiTimeUnit: {
VerilatedVpioScope* vop = VerilatedVpioScope::castp(object);
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
if (!vop)
return Verilated::threadContextp()->timeunit(); // Null asks for global, not unlikely
return vop->scopep()->timeunit();
}
case vpiType: {
VerilatedVpio* vop = VerilatedVpio::castp(object);
const VerilatedVpio* const vop = VerilatedVpio::castp(object);
if (VL_UNLIKELY(!vop)) return 0;
return vop->type();
}
case vpiDirection: {
// By forthought, the directions already are vpi enumerated
VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object);
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
if (VL_UNLIKELY(!vop)) return 0;
return vop->varp()->vldir();
}
case vpiScalar: // FALLTHRU
case vpiVector: {
VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object);
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
if (VL_UNLIKELY(!vop)) return 0;
return (property == vpiVector) ^ (vop->varp()->dims() == 0);
}
case vpiSize: {
VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object);
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
if (VL_UNLIKELY(!vop)) return 0;
return vop->size();
}
@ -1602,7 +1602,7 @@ PLI_INT64 vpi_get64(PLI_INT32 /*property*/, vpiHandle /*object*/) {
PLI_BYTE8* vpi_get_str(PLI_INT32 property, vpiHandle object) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_str %d %p\n", property, object););
VerilatedVpiImp::assertOneCheck();
VerilatedVpio* vop = VerilatedVpio::castp(object);
const VerilatedVpio* const vop = VerilatedVpio::castp(object);
VL_VPI_ERROR_RESET_();
if (VL_UNLIKELY(!vop)) return nullptr;
switch (property) {
@ -1725,7 +1725,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
__FILE__, __LINE__, "",
"vpi_get_value with more than VL_MULS_MAX_WORDS; increase and recompile");
}
WDataInP datap = (reinterpret_cast<EData*>(varDatap));
const WDataInP datap = (reinterpret_cast<EData*>(varDatap));
for (int i = 0; i < words; ++i) {
t_out[i].aval = datap[i];
t_out[i].bval = 0;
@ -1756,7 +1756,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
} else if (valuep->format == vpiOctStrVal) {
valuep->value.str = t_outStr;
int chars = (varp->packed().elements() + 2) / 3;
int bytes = VL_BYTES_I(varp->packed().elements());
const int bytes = VL_BYTES_I(varp->packed().elements());
CData* datap = (reinterpret_cast<CData*>(varDatap));
int i;
if (chars > t_outStrSz) {
@ -1770,7 +1770,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
chars = t_outStrSz;
}
for (i = 0; i < chars; ++i) {
div_t idx = div(i * 3, 8);
const div_t idx = div(i * 3, 8);
int val = datap[idx.quot];
if ((idx.quot + 1) < bytes) {
// if the next byte is valid or that in
@ -1832,7 +1832,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
if (i == (chars - 1)) {
// most signifcant char, mask off non existant bits when vector
// size is not a multiple of 4
unsigned int rem = varp->packed().elements() & 3;
const unsigned int rem = varp->packed().elements() & 3;
if (rem) {
// generate bit mask & zero non existant bits
val &= (1 << rem) - 1;
@ -1862,7 +1862,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
bytes = t_outStrSz;
}
for (i = 0; i < bytes; ++i) {
char val = datap[bytes - i - 1];
const char val = datap[bytes - i - 1];
// other simulators replace [leading?] zero chars with spaces, replicate here.
t_outStr[i] = val ? val : ' ';
}
@ -1896,7 +1896,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) {
if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) {
vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname());
return;
} else if (VerilatedVpioParam* vop = VerilatedVpioParam::castp(object)) {
} 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)) {
@ -1920,7 +1920,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_put_value with nullptr value pointer");
return nullptr;
}
if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) {
if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) {
VL_DEBUG_IF_PLI(
VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n", vop->fullname(),
valuep->format, valuep->value.integer);
@ -1953,7 +1953,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
valuep->value.vector[1].aval & vop->mask(), valuep->value.vector[0].aval);
return object;
} else if (vop->varp()->vltype() == VLVT_WDATA) {
int words = VL_WORDS_I(vop->varp()->packed().elements());
const int words = VL_WORDS_I(vop->varp()->packed().elements());
WDataOutP datap = (reinterpret_cast<EData*>(vop->varDatap()));
for (int i = 0; i < words; ++i) {
datap[i] = valuep->value.vector[i].aval;
@ -1962,8 +1962,8 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
return object;
}
} else if (valuep->format == vpiBinStrVal) {
int bits = vop->varp()->packed().elements();
int len = std::strlen(valuep->value.str);
const int bits = vop->varp()->packed().elements();
const int len = std::strlen(valuep->value.str);
CData* datap = (reinterpret_cast<CData*>(vop->varDatap()));
for (int i = 0; i < bits; ++i) {
char set = (i < len) ? (valuep->value.str[len - i - 1] == '1') : 0;
@ -1977,9 +1977,9 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
}
return object;
} else if (valuep->format == vpiOctStrVal) {
int chars = (vop->varp()->packed().elements() + 2) / 3;
int bytes = VL_BYTES_I(vop->varp()->packed().elements());
int len = std::strlen(valuep->value.str);
const int chars = (vop->varp()->packed().elements() + 2) / 3;
const int bytes = VL_BYTES_I(vop->varp()->packed().elements());
const int len = std::strlen(valuep->value.str);
CData* datap = (reinterpret_cast<CData*>(vop->varDatap()));
div_t idx;
datap[0] = 0; // reset zero'th byte
@ -2029,7 +2029,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
} else if (valuep->format == vpiDecStrVal) {
char remainder[16];
unsigned long long val;
int success = std::sscanf(valuep->value.str, "%30llu%15s", &val, remainder);
const int success = std::sscanf(valuep->value.str, "%30llu%15s", &val, remainder);
if (success < 1) {
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Parsing failed for '%s' as value %s for %s",
__func__, valuep->value.str,
@ -2057,12 +2057,12 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
return object;
}
} else if (valuep->format == vpiHexStrVal) {
int chars = (vop->varp()->packed().elements() + 3) >> 2;
const int chars = (vop->varp()->packed().elements() + 3) >> 2;
CData* datap = (reinterpret_cast<CData*>(vop->varDatap()));
char* val = valuep->value.str;
// skip hex ident if one is detected at the start of the string
if (val[0] == '0' && (val[1] == 'x' || val[1] == 'X')) val += 2;
int len = std::strlen(val);
const int len = std::strlen(val);
for (int i = 0; i < chars; ++i) {
char hex;
// compute hex digit value
@ -2097,8 +2097,8 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
datap[(chars - 1) >> 1] &= vop->mask_byte((chars - 1) >> 1);
return object;
} else if (valuep->format == vpiStringVal) {
int bytes = VL_BYTES_I(vop->varp()->packed().elements());
int len = std::strlen(valuep->value.str);
const int bytes = VL_BYTES_I(vop->varp()->packed().elements());
const int len = std::strlen(valuep->value.str);
CData* datap = (reinterpret_cast<CData*>(vop->varDatap()));
for (int i = 0; i < bytes; ++i) {
// prepend with 0 values before placing string the least significant bytes
@ -2120,11 +2120,11 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname());
return nullptr;
} else if (VerilatedVpioParam* vop = VerilatedVpioParam::castp(object)) {
} else if (const VerilatedVpioParam* const vop = VerilatedVpioParam::castp(object)) {
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Ignoring vpi_put_value to vpiParameter: %s",
__func__, vop->fullname());
return nullptr;
} else if (VerilatedVpioConst* vop = VerilatedVpioConst::castp(object)) {
} else if (const VerilatedVpioConst* const vop = VerilatedVpioConst::castp(object)) {
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Ignoring vpi_put_value to vpiConstant: %s",
__func__, vop->fullname());
return nullptr;
@ -2153,8 +2153,8 @@ void vpi_get_time(vpiHandle object, p_vpi_time time_p) {
return;
}
if (time_p->type == vpiSimTime) {
QData qtime = VL_TIME_Q();
WData itime[2];
const QData qtime = VL_TIME_Q();
VlWide<2> itime;
VL_SET_WQ(itime, qtime);
time_p->low = itime[0];
time_p->high = itime[1];
@ -2162,9 +2162,9 @@ void vpi_get_time(vpiHandle object, p_vpi_time time_p) {
} else if (time_p->type == vpiScaledRealTime) {
double dtime = VL_TIME_D();
if (VerilatedVpioScope* vop = VerilatedVpioScope::castp(object)) {
int scalePow10
const int scalePow10
= Verilated::threadContextp()->timeprecision() - vop->scopep()->timeunit();
double scale = vl_time_multiplier(scalePow10); // e.g. 0.0001
const double scale = vl_time_multiplier(scalePow10); // e.g. 0.0001
dtime *= scale;
}
time_p->real = dtime;
@ -2198,7 +2198,7 @@ PLI_INT32 vpi_mcd_printf(PLI_UINT32 mcd, PLI_BYTE8* formatp, ...) {
VL_VPI_ERROR_RESET_();
va_list ap;
va_start(ap, formatp);
int chars = vpi_mcd_vprintf(mcd, formatp, ap);
const int chars = vpi_mcd_vprintf(mcd, formatp, ap);
va_end(ap);
return chars;
}
@ -2208,7 +2208,7 @@ PLI_INT32 vpi_printf(PLI_BYTE8* formatp, ...) {
VL_VPI_ERROR_RESET_();
va_list ap;
va_start(ap, formatp);
int chars = vpi_vprintf(formatp, ap);
const int chars = vpi_vprintf(formatp, ap);
va_end(ap);
return chars;
}
@ -2221,11 +2221,11 @@ PLI_INT32 vpi_vprintf(PLI_BYTE8* formatp, va_list ap) {
PLI_INT32 vpi_mcd_vprintf(PLI_UINT32 mcd, PLI_BYTE8* format, va_list ap) {
VerilatedVpiImp::assertOneCheck();
FILE* fp = VL_CVT_I_FP(mcd);
FILE* const fp = VL_CVT_I_FP(mcd);
VL_VPI_ERROR_RESET_();
// cppcheck-suppress nullPointer
if (VL_UNLIKELY(!fp)) return 0;
int chars = vfprintf(fp, format, ap);
const int chars = vfprintf(fp, format, ap);
return chars;
}
@ -2238,7 +2238,7 @@ PLI_INT32 vpi_flush(void) {
PLI_INT32 vpi_mcd_flush(PLI_UINT32 mcd) {
VerilatedVpiImp::assertOneCheck();
FILE* fp = VL_CVT_I_FP(mcd);
FILE* const fp = VL_CVT_I_FP(mcd);
VL_VPI_ERROR_RESET_();
if (VL_UNLIKELY(!fp)) return 1;
std::fflush(fp);
@ -2255,7 +2255,7 @@ PLI_INT32 vpi_chk_error(p_vpi_error_info error_info_p) {
// executing vpi_chk_error does not reset error
// error_info_p can be nullptr, so only return level in that case
VerilatedVpiImp::assertOneCheck();
p_vpi_error_info _error_info_p = VerilatedVpiImp::error_info()->getError();
p_vpi_error_info const _error_info_p = VerilatedVpiImp::error_info()->getError();
if (error_info_p && _error_info_p) *error_info_p = *_error_info_p;
if (!_error_info_p) return 0; // no error occured
return _error_info_p->level; // return error severity level

View File

@ -47,7 +47,7 @@ public:
static bool callValueCbs() VL_MT_UNSAFE_ONE;
/// Call callbacks of arbitrary types.
/// User wrapper code should call this from their main loops.
static bool callCbs(vluint32_t reason) VL_MT_UNSAFE_ONE;
static bool callCbs(const vluint32_t reason) VL_MT_UNSAFE_ONE;
/// Returns time of the next registered VPI callback, or
/// ~(0ULL) if none are registered
static QData cbNextDeadline() VL_MT_UNSAFE_ONE;

View File

@ -38,23 +38,7 @@ def test():
ci_fold_start("configure")
print("Stage 1: configure (coverage on)")
run("autoconf")
cxx_flags = (
"--coverage"
# Otherwise inline may not show as uncovered
# If we use this then e.g. verilated.h functions properly show up
# if unused.
# However, VerilatedSerialize::write then changes from covered
# to uncovered (in G++ 9.3.0) even with all inlining turned off.
# Having false negative coverage is more effort then missing negatives.
# Also this seems to explode the runtime (since a lot more data).
# + " -fkeep-inline-functions"
# Otherwise static may not show as uncovered
+ " -fkeep-static-functions"
# Exceptions can pollute the branch coverage data
+ " -fno-exceptions"
# Define-out some impossible stuff
+ " -DVL_GCOV")
run("./configure --enable-longtests CXX='g++ " + cxx_flags + "'")
run("./configure --enable-longtests --enable-coverage CXX=g++")
ci_fold_end()
if Args.stage_enabled[2]:

View File

@ -77,13 +77,11 @@ TGT = ../../verilator_bin$(EXEEXT)
#################
ifeq ($(VL_DEBUG),)
# Optimize
COPT = -O2
CPPFLAGS += -O2
else
# Debug
COPT = -ggdb -DVL_DEBUG -D_GLIBCXX_DEBUG
# Debug & Profile:
#LDFLAGS += -pg -g
#COPT = -ggdb -pg -g
CPPFLAGS += @CFG_CXXFLAGS_DEBUG@ -DVL_DEBUG -D_GLIBCXX_DEBUG
LDFLAGS += @CFG_LDFLAGS_DEBUG@
endif
#################
@ -96,7 +94,6 @@ LIBS = $(CFG_LIBS) -lm
CPPFLAGS += -MMD
CPPFLAGS += -I. -I$(bldsrc) -I$(srcdir) -I$(incdir) -I../../include
#CPPFLAGS += -DVL_LEAK_CHECKS # If running valgrind or other hunting tool
CPPFLAGS += $(COPT)
CPPFLAGS += -MP # Only works on recent GCC versions
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
CPPFLAGS += -W -Wall $(CFG_CXXFLAGS_WEXTRA) $(CFG_CXXFLAGS_SRC) -Werror
@ -183,10 +180,14 @@ RAW_OBJS = \
V3Descope.o \
V3DupFinder.o \
V3EmitC.o \
V3EmitCBase.o \
V3EmitCConstPool.o \
V3EmitCFunc.o \
V3EmitCInlines.o \
V3EmitCSyms.o \
V3EmitCMake.o \
V3EmitCMain.o \
V3EmitCMake.o \
V3EmitCModel.o \
V3EmitCSyms.o \
V3EmitMk.o \
V3EmitV.o \
V3EmitXml.o \

View File

@ -474,11 +474,10 @@ private:
if (!m_scopeFinalp) {
m_scopeFinalp = new AstCFunc(
nodep->fileline(), "_final_" + m_namer.scopep()->nameDotless(), m_namer.scopep());
m_scopeFinalp->argTypes(EmitCBaseVisitor::symClassVar());
m_scopeFinalp->addInitsp(
new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign() + "\n"));
m_scopeFinalp->dontCombine(true);
m_scopeFinalp->formCallTree(true);
m_scopeFinalp->isStatic(false);
m_scopeFinalp->isLoose(true);
m_scopeFinalp->slow(true);
m_namer.scopep()->addActivep(m_scopeFinalp);
}

View File

@ -163,7 +163,7 @@ private:
} else {
++m_statAsNotImm;
}
bool force = VN_IS(nodep, AssertIntrinsic);
const bool force = VN_IS(nodep, AssertIntrinsic);
if (passsp) passsp = newIfAssertOn(passsp, force);
if (failsp) failsp = newIfAssertOn(failsp, force);
if (!failsp) failsp = newFireAssertUnchecked(nodep, "'assert' failed.");
@ -231,7 +231,7 @@ private:
} while (ifp);
AstNode* newifp = nodep->cloneTree(false);
bool allow_none = nodep->unique0Pragma();
const bool allow_none = nodep->unique0Pragma();
// Empty case means no property
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::BitFalse());
@ -305,7 +305,7 @@ private:
// Empty case means no property
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::BitFalse());
bool allow_none = has_default || nodep->unique0Pragma();
const bool allow_none = has_default || nodep->unique0Pragma();
AstNode* ohot
= (allow_none
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))

View File

@ -119,7 +119,7 @@ string AstNode::encodeName(const string& namein) {
// a user identifier nor a temp we create in Verilator.
// We also do *NOT* use __DOT__ etc, as we search for those
// in some replacements, and don't want to mangle the user's names.
unsigned val = pos[0] & 0xff; // Mask to avoid sign extension
const unsigned val = pos[0] & 0xff; // Mask to avoid sign extension
std::stringstream hex;
hex << std::setfill('0') << std::setw(2) << std::hex << val;
out += "__0" + hex.str();
@ -932,7 +932,9 @@ AstNode* AstNode::iterateSubtreeReturnEdits(AstNVisitor& v) {
void AstNode::cloneRelinkTree() {
// private: Cleanup clone() operation on whole tree. Publicly call cloneTree() instead.
for (AstNode* nodep = this; nodep; nodep = nodep->m_nextp) {
if (m_dtypep && m_dtypep->clonep()) m_dtypep = m_dtypep->clonep();
if (nodep->m_dtypep && nodep->m_dtypep->clonep()) {
nodep->m_dtypep = nodep->m_dtypep->clonep();
}
nodep->cloneRelink();
if (nodep->m_op1p) nodep->m_op1p->cloneRelinkTree();
if (nodep->m_op2p) nodep->m_op2p->cloneRelinkTree();
@ -1045,7 +1047,7 @@ void AstNode::dumpTreeFileGdb(const AstNode* nodep, // LCOV_EXCL_START
cout << "<nullptr>" << endl;
return;
}
string filename = filenamep ? filenamep : v3Global.debugFilename("debug.tree", 98);
const string filename = filenamep ? filenamep : v3Global.debugFilename("debug.tree", 98);
v3Global.rootp()->dumpTreeFile(filename);
} // LCOV_EXCL_STOP
@ -1178,7 +1180,7 @@ string AstNode::locationStr() const {
str += modp->hierName();
return str;
} else if ((nvrp = VN_CAST_CONST(backp, NodeVarRef))) {
string prettyName = nvrp->prettyName();
const string prettyName = nvrp->prettyName();
// VarRefs have not been flattened yet and do not contain location information
if (prettyName != nvrp->name()) {
str += prettyName;

View File

@ -26,6 +26,7 @@
#include "V3Global.h"
#include <cmath>
#include <type_traits>
#include <unordered_set>
#include "V3Ast__gen_classes.h" // From ./astgen
@ -155,6 +156,7 @@ public:
return (m_e == READWRITE) ? VAccess(m_e) : (m_e == WRITE ? VAccess(READ) : VAccess(WRITE));
}
bool isReadOnly() const { return m_e == READ; } // False with READWRITE
bool isWriteOnly() const { return m_e == WRITE; } // False with READWRITE
bool isReadOrRW() const { return m_e == READ || m_e == READWRITE; }
bool isWriteOrRW() const { return m_e == WRITE || m_e == READWRITE; }
bool isRW() const { return m_e == READWRITE; }
@ -457,6 +459,7 @@ public:
// Internal types for mid-steps
SCOPEPTR,
CHARPTR,
MTASKSTATE,
// Unsigned and two state; fundamental types
UINT32,
UINT64,
@ -467,18 +470,19 @@ public:
};
enum en m_e;
const char* ascii() const {
static const char* const names[] = {
"%E-unk", "bit", "byte", "chandle", "event", "int", "integer",
"logic", "longint", "real", "shortint", "time", "string", "VerilatedScope*",
"char*", "IData", "QData", "LOGIC_IMPLICIT", " MAX"};
static const char* const names[]
= {"%E-unk", "bit", "byte", "chandle", "event",
"int", "integer", "logic", "longint", "real",
"shortint", "time", "string", "VerilatedScope*", "char*",
"VlMTaskState", "IData", "QData", "LOGIC_IMPLICIT", " MAX"};
return names[m_e];
}
const char* dpiType() const {
static const char* const names[]
= {"%E-unk", "svBit", "char", "void*", "char", "int",
"%E-integer", "svLogic", "long long", "double", "short", "%E-time",
"const char*", "dpiScope", "const char*", "IData", "QData", "%E-logic-implicit",
" MAX"};
= {"%E-unk", "svBit", "char", "void*", "char",
"int", "%E-integer", "svLogic", "long long", "double",
"short", "%E-time", "const char*", "dpiScope", "const char*",
"%E-mtaskstate", "IData", "QData", "%E-logic-implct", " MAX"};
return names[m_e];
}
static void selfTest() {
@ -511,6 +515,7 @@ public:
case STRING: return 64; // opaque // Just the pointer, for today
case SCOPEPTR: return 0; // opaque
case CHARPTR: return 0; // opaque
case MTASKSTATE: return 0; // opaque
case UINT32: return 32;
case UINT64: return 64;
default: return 0;
@ -549,11 +554,13 @@ public:
|| m_e == DOUBLE || m_e == SHORTINT || m_e == UINT32 || m_e == UINT64);
}
bool isOpaque() const { // IE not a simple number we can bit optimize
return (m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR || m_e == DOUBLE);
return (m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR || m_e == MTASKSTATE
|| m_e == DOUBLE);
}
bool isDouble() const { return m_e == DOUBLE; }
bool isEventValue() const { return m_e == EVENTVALUE; }
bool isString() const { return m_e == STRING; }
bool isMTaskState() const { return m_e == MTASKSTATE; }
};
inline bool operator==(const AstBasicDTypeKwd& lhs, const AstBasicDTypeKwd& rhs) {
return lhs.m_e == rhs.m_e;
@ -1136,10 +1143,14 @@ public:
explicit VNUser(void* p) { m_u.up = p; }
~VNUser() = default;
// Casters
WidthVP* c() const { return reinterpret_cast<WidthVP*>(m_u.up); }
VSymEnt* toSymEnt() const { return reinterpret_cast<VSymEnt*>(m_u.up); }
AstNode* toNodep() const { return reinterpret_cast<AstNode*>(m_u.up); }
V3GraphVertex* toGraphVertex() const { return reinterpret_cast<V3GraphVertex*>(m_u.up); }
template <class T> //
typename std::enable_if<std::is_pointer<T>::value, T>::type to() const {
return reinterpret_cast<T>(m_u.up);
}
WidthVP* c() const { return to<WidthVP*>(); }
VSymEnt* toSymEnt() const { return to<VSymEnt*>(); }
AstNode* toNodep() const { return to<AstNode*>(); }
V3GraphVertex* toGraphVertex() const { return to<V3GraphVertex*>(); }
int toInt() const { return m_u.ui; }
static VNUser fromInt(int i) { return VNUser(i); }
};
@ -1687,6 +1698,7 @@ public:
AstNodeDType* findSigned32DType() { return findBasicDType(AstBasicDTypeKwd::INTEGER); }
AstNodeDType* findUInt32DType() { return findBasicDType(AstBasicDTypeKwd::UINT32); }
AstNodeDType* findUInt64DType() { return findBasicDType(AstBasicDTypeKwd::UINT64); }
AstNodeDType* findCHandleDType() { return findBasicDType(AstBasicDTypeKwd::CHANDLE); }
AstNodeDType* findVoidDType() const;
AstNodeDType* findQueueIndexDType() const;
AstNodeDType* findBitDType(int width, int widthMin, VSigning numeric) const;
@ -1868,7 +1880,7 @@ public:
virtual void dump(std::ostream& str) const override;
virtual bool hasDType() const override { return true; }
virtual string emitVerilog() = 0; /// Format string for verilog writing; see V3EmitV
// For documentation on emitC format see EmitCStmts::emitOpName
// For documentation on emitC format see EmitCFunc::emitOpName
virtual string emitC() = 0;
virtual string emitSimpleOperator() { return ""; } // "" means not ok to use
virtual bool emitCheckMaxWords() { return false; } // Check VL_MULS_MAX_WORDS
@ -2272,9 +2284,7 @@ private:
AstVarScope* m_varScopep = nullptr; // Varscope for hierarchy
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
string m_name; // Name of variable
string m_hiernameToProt; // Scope converted into name-> for emitting
string m_hiernameToUnprot; // Scope converted into name-> for emitting
bool m_hierThis = false; // Hiername points to "this" function
string m_selfPointer; // Output code object pointer (e.g.: 'this')
protected:
AstNodeVarRef(AstType t, FileLine* fl, const string& name, const VAccess& access)
@ -2306,13 +2316,9 @@ public:
void varp(AstVar* varp);
AstVarScope* varScopep() const { return m_varScopep; }
void varScopep(AstVarScope* varscp) { m_varScopep = varscp; }
string hiernameToProt() const { return m_hiernameToProt; }
void hiernameToProt(const string& hn) { m_hiernameToProt = hn; }
string hiernameToUnprot() const { return m_hiernameToUnprot; }
void hiernameToUnprot(const string& hn) { m_hiernameToUnprot = hn; }
string hiernameProtect() const;
bool hierThis() const { return m_hierThis; }
void hierThis(bool flag) { m_hierThis = flag; }
string selfPointer() const { return m_selfPointer; }
void selfPointer(const string& value) { m_selfPointer = value; }
string selfPointerProtect(bool useSelfForThis) const;
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
// Know no children, and hot function, so skip iterator for speed
@ -2615,8 +2621,7 @@ 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_hiernameToProt;
string m_hiernameToUnprot;
string m_selfPointer; // Output code object pointer (e.g.: 'this')
string m_argTypes;
protected:
@ -2642,11 +2647,9 @@ public:
virtual bool isPure() const override;
virtual bool isOutputter() const override { return !isPure(); }
AstCFunc* funcp() const { return m_funcp; }
string hiernameToProt() const { return m_hiernameToProt; }
void hiernameToProt(const string& hn) { m_hiernameToProt = hn; }
string hiernameToUnprot() const { return m_hiernameToUnprot; }
void hiernameToUnprot(const string& hn) { m_hiernameToUnprot = hn; }
string hiernameProtect() const;
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

View File

@ -18,16 +18,18 @@
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3EmitCBase.h"
#include "V3File.h"
#include "V3Global.h"
#include "V3Graph.h"
#include "V3Hasher.h"
#include "V3PartitionGraph.h" // Just for mtask dumping
#include "V3String.h"
#include "V3EmitCBase.h"
#include "V3AstNodes__gen_macros.h" // Generated by 'astgen'
#include <iomanip>
#include <iterator>
#include <vector>
//======================================================================
@ -55,8 +57,19 @@ void AstNodeVarRef::cloneRelink() {
if (m_varp && m_varp->clonep()) m_varp = m_varp->clonep();
}
string AstNodeVarRef::hiernameProtect() const {
return hiernameToUnprot() + VIdProtect::protectWordsIf(hiernameToProt(), protect());
string AstNodeVarRef::selfPointerProtect(bool useSelfForThis) const {
const string& sp
= useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer();
return VIdProtect::protectWordsIf(sp, protect());
}
void AstAddrOfCFunc::cloneRelink() {
if (m_funcp && m_funcp->clonep()) m_funcp = m_funcp->clonep();
}
const char* AstAddrOfCFunc::broken() const {
BROKEN_RTN(m_funcp && !m_funcp->brokeExists());
return nullptr;
}
int AstNodeSel::bitConst() const {
@ -108,8 +121,10 @@ const char* AstNodeCCall::broken() const {
return nullptr;
}
bool AstNodeCCall::isPure() const { return funcp()->pure(); }
string AstNodeCCall::hiernameProtect() const {
return hiernameToUnprot() + VIdProtect::protectWordsIf(hiernameToProt(), protect());
string AstNodeCCall::selfPointerProtect(bool useSelfForThis) const {
const string& sp
= useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer();
return VIdProtect::protectWordsIf(sp, protect());
}
void AstNodeCond::numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
@ -216,9 +231,9 @@ AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
}
AstExecGraph::AstExecGraph(FileLine* fileline)
: ASTGEN_SUPER_ExecGraph(fileline) {
m_depGraphp = new V3Graph;
}
: ASTGEN_SUPER_ExecGraph(fileline)
, m_depGraphp{new V3Graph} {}
AstExecGraph::~AstExecGraph() { VL_DO_DANGLING(delete m_depGraphp, m_depGraphp); }
AstNode* AstInsideRange::newAndFromInside(AstNode* exprp, AstNode* lhsp, AstNode* rhsp) {
@ -260,8 +275,16 @@ AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) {
return nullptr;
}
AstNetlist::AstNetlist()
: ASTGEN_SUPER_Netlist(new FileLine(FileLine::builtInFilename()))
, m_typeTablep{new AstTypeTable(fileline())}
, m_constPoolp{new AstConstPool(fileline())} {
addMiscsp(m_typeTablep);
addMiscsp(m_constPoolp);
}
void AstNetlist::timeprecisionMerge(FileLine*, const VTimescale& value) {
VTimescale prec = v3Global.opt.timeComputePrec(value);
const VTimescale prec = v3Global.opt.timeComputePrec(value);
if (prec.isNone() || prec == m_timeprecision) {
} else if (m_timeprecision.isNone()) {
m_timeprecision = prec;
@ -314,13 +337,15 @@ string AstVar::verilogKwd() const {
}
}
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc) const {
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc,
bool asRef) const {
UASSERT_OBJ(!forReturn, this,
"Internal data is never passed as return, but as first argument");
string ostatic;
if (isStatic() && namespc.empty()) ostatic = "static ";
bool isRef = isDpiOpenArray() || (forFunc && (isWritable() || direction().isRefOrConstRef()));
const bool isRef = isDpiOpenArray()
|| (forFunc && (isWritable() || direction().isRefOrConstRef())) || asRef;
if (forFunc && isReadOnly() && isRef) ostatic = ostatic + "const ";
@ -335,7 +360,7 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string&
string AstVar::vlEnumType() const {
string arg;
AstBasicDType* bdtypep = basicp();
bool strtype = bdtypep && bdtypep->keyword() == AstBasicDTypeKwd::STRING;
const bool strtype = bdtypep && bdtypep->keyword() == AstBasicDTypeKwd::STRING;
if (bdtypep && bdtypep->keyword() == AstBasicDTypeKwd::CHARPTR) {
return "VLVT_PTR";
} else if (bdtypep && bdtypep->keyword() == AstBasicDTypeKwd::SCOPEPTR) {
@ -610,13 +635,6 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) {
return nullptr;
}
string AstVar::mtasksString() const {
std::ostringstream os;
os << "all: ";
for (const auto& id : m_mtaskIds) os << id << " ";
return os.str();
}
class AstNodeDType::CTypeRecursed final {
public:
string m_type; // The base type, e.g.: "Foo_t"s
@ -624,15 +642,22 @@ public:
string render(const string& name, bool isRef) const {
string out;
out += m_type;
if (name != "") out += " ";
out += isRef ? "(&" + name + ")" : name;
if (!name.empty()) out += " ";
if (isRef) {
if (!m_dims.empty()) out += "(";
out += "&";
out += name;
if (!m_dims.empty()) out += ")";
} else {
out += name;
}
out += m_dims;
return out;
}
};
string AstNodeDType::cType(const string& name, bool forFunc, bool isRef) const {
CTypeRecursed info = cTypeRecurse(false);
const CTypeRecursed info = cTypeRecurse(false);
return info.render(name, isRef);
}
@ -672,10 +697,12 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
info.m_type = "const char*";
} else if (bdtypep->keyword() == AstBasicDTypeKwd::SCOPEPTR) {
info.m_type = "const VerilatedScope*";
} else if (bdtypep->keyword() == AstBasicDTypeKwd::DOUBLE) {
} else if (bdtypep->keyword().isDouble()) {
info.m_type = "double";
} else if (bdtypep->keyword() == AstBasicDTypeKwd::STRING) {
} else if (bdtypep->keyword().isString()) {
info.m_type = "std::string";
} else if (bdtypep->keyword().isMTaskState()) {
info.m_type = "VlMTaskVertex";
} else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width
info.m_type = "CData" + bitvec;
} else if (dtypep->widthMin() <= 16) {
@ -864,6 +891,11 @@ bool AstSenTree::hasCombo() const {
return false;
}
AstTypeTable::AstTypeTable(FileLine* fl)
: ASTGEN_SUPER_TypeTable(fl) {
for (int i = 0; i < AstBasicDTypeKwd::_ENUM_MAX; ++i) m_basicps[i] = nullptr;
}
void AstTypeTable::clearCache() {
// When we mass-change widthMin in V3WidthCommit, we need to correct the table.
// Just clear out the maps; the search functions will be used to rebuild the map
@ -958,6 +990,115 @@ AstBasicDType* AstTypeTable::findInsertSameDType(AstBasicDType* nodep) {
return nodep;
}
AstConstPool::AstConstPool(FileLine* fl)
: ASTGEN_SUPER_ConstPool(fl)
, m_modp{new AstModule(fl, "@CONST-POOL@")}
, m_scopep{new AstScope(fl, m_modp, "@CONST-POOL@", nullptr, nullptr)} {
addOp1p(m_modp);
m_modp->addStmtp(m_scopep);
}
AstVarScope* AstConstPool::createNewEntry(const string& name, AstNode* initp) {
FileLine* const fl = initp->fileline();
AstVar* const varp = new AstVar(fl, AstVarType::MODULETEMP, name, initp->dtypep());
varp->isConst(true);
varp->isStatic(true);
varp->valuep(initp->cloneTree(false));
m_modp->addStmtp(varp);
AstVarScope* const varScopep = new AstVarScope(fl, m_scopep, varp);
m_scopep->addVarp(varScopep);
return varScopep;
}
static bool sameInit(const AstInitArray* ap, const AstInitArray* bp) {
// Unpacked array initializers must have equivalent values
// Note, sadly we can't just call ap->sameTree(pb), because both:
// - the dtypes might be different instances
// - the default/inititem children might be in different order yet still yield the same table
// See note in AstInitArray::same about the same. This function instead compares by initializer
// value, rather than by tree structure.
const AstUnpackArrayDType* const aDTypep = VN_CAST(ap->dtypep(), UnpackArrayDType);
const AstUnpackArrayDType* const bDTypep = VN_CAST(bp->dtypep(), UnpackArrayDType);
UASSERT_STATIC(aDTypep, "Bad type in array initializer");
UASSERT_STATIC(bDTypep, "Bad type in array initializer");
if (!aDTypep->subDTypep()->sameTree(bDTypep->subDTypep())) { // Element types differ
return false;
}
if (!aDTypep->rangep()->sameTree(bDTypep->rangep())) { // Ranges differ
return false;
}
// Compare initializer arrays by value. Note this is only called when they hash the same,
// so they likely run at most once per call to 'AstConstPool::findTable'.
const uint32_t size = aDTypep->elementsConst();
for (uint32_t n = 0; n < size; ++n) {
const AstNode* const valAp = ap->getIndexDefaultedValuep(n);
const AstNode* const valBp = bp->getIndexDefaultedValuep(n);
if (!valAp->sameTree(valBp)) { return false; }
}
return true;
}
AstVarScope* AstConstPool::findTable(AstInitArray* initp) {
AstNode* const defaultp = initp->defaultp();
// Verify initializer is well formed
UASSERT_OBJ(VN_IS(initp->dtypep(), UnpackArrayDType), initp,
"Const pool table must have AstUnpackArrayDType dtype");
UASSERT_OBJ(!defaultp || VN_IS(defaultp, Const), initp,
"Const pool table default must be Const");
for (AstNode* nodep = initp->initsp(); nodep; nodep = nodep->nextp()) {
AstNode* const valuep = VN_CAST(nodep, InitItem)->valuep();
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);
for (auto it = er.first; it != er.second; ++it) {
AstVarScope* const varScopep = it->second;
const AstInitArray* const init2p = VN_CAST(varScopep->varp()->valuep(), InitArray);
if (sameInit(initp, init2p)) {
return varScopep; // Found identical table
}
}
// No such table yet, create it.
string name = "TABLE_";
name += cvtToHex(hash);
name += "_";
name += cvtToStr(std::distance(er.first, er.second));
AstVarScope* const varScopep = createNewEntry(name, initp);
m_tables.emplace(hash, varScopep);
return varScopep;
}
static bool sameInit(const AstConst* ap, const AstConst* bp) {
// Similarly to the AstInitArray comparison, we ignore the dtype instance, so long as they
// are compatible. For now, we assume the dtype is not relevant, as we only call this from
// V3Prelim which is a late pass.
// Compare initializers by value. This checks widths as well.
return ap->num().isCaseEq(bp->num());
}
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);
for (auto it = er.first; it != er.second; ++it) {
AstVarScope* const varScopep = it->second;
const AstConst* const init2p = VN_CAST(varScopep->varp()->valuep(), Const);
if (sameInit(initp, init2p)
&& (mergeDType || varScopep->dtypep()->sameTree(initp->dtypep()))) {
return varScopep; // Found identical constant
}
}
// No such constant yet, create it.
string name = "CONST_";
name += cvtToHex(hash);
name += "_";
name += cvtToStr(std::distance(er.first, er.second));
AstVarScope* const varScopep = createNewEntry(name, initp);
m_consts.emplace(hash, varScopep);
return varScopep;
}
//======================================================================
// Special walking tree inserters
@ -1119,8 +1260,9 @@ const char* AstClassPackage::broken() const {
return nullptr;
}
void AstClass::insertCache(AstNode* nodep) {
bool doit = (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|| (VN_IS(nodep, NodeFTask) && !VN_CAST(nodep, NodeFTask)->isExternProto()));
const bool doit
= (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|| (VN_IS(nodep, NodeFTask) && !VN_CAST(nodep, NodeFTask)->isExternProto()));
if (doit) {
if (m_members.find(nodep->name()) != m_members.end()) {
nodep->v3error("Duplicate declaration of member name: " << nodep->prettyNameQ());
@ -1316,10 +1458,10 @@ void AstRefDType::dump(std::ostream& str) const {
if (!s_recursing) { // Prevent infinite dump if circular typedefs
s_recursing = true;
str << " -> ";
if (typedefp()) {
typedefp()->dump(str);
} else if (subDTypep()) {
subDTypep()->dump(str);
if (const auto subp = typedefp()) {
subp->dump(str);
} else if (const auto subp = subDTypep()) {
subp->dump(str);
}
s_recursing = false;
}
@ -1364,7 +1506,8 @@ void AstNodeArrayDType::dump(std::ostream& str) const {
}
string AstPackArrayDType::prettyDTypeName() const {
std::ostringstream os;
os << subDTypep()->prettyDTypeName() << declRange();
if (const auto subp = subDTypep()) os << subp->prettyDTypeName();
os << declRange();
return os.str();
}
string AstUnpackArrayDType::prettyDTypeName() const {
@ -1679,14 +1822,11 @@ void AstCFunc::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (slow()) str << " [SLOW]";
if (pure()) str << " [PURE]";
if (isStatic().unknown()) {
str << " [STATICU]";
} else if (isStatic().trueUnknown()) {
str << " [STATIC]";
}
if (dpiImport()) str << " [DPII]";
if (dpiExport()) str << " [DPIX]";
if (dpiExportWrapper()) str << " [DPIXWR]";
if (isStatic()) str << " [STATIC]";
if (dpiExportDispatcher()) str << " [DPIED]";
if (dpiExportImpl()) str << " [DPIEI]";
if (dpiImportPrototype()) str << " [DPIIP]";
if (dpiImportWrapper()) str << " [DPIIW]";
if (isConstructor()) str << " [CTOR]";
if (isDestructor()) str << " [DTOR]";
if (isVirtual()) str << " [VIRT]";

View File

@ -685,7 +685,7 @@ public:
refDTypep(nullptr);
setOp2p(rangep);
dtypep(nullptr); // V3Width will resolve
int width = subDTypep()->width() * rangep->elementsConst();
const int width = subDTypep()->width() * rangep->elementsConst();
widthForce(width, width);
}
AstPackArrayDType(FileLine* fl, AstNodeDType* dtp, AstRange* rangep)
@ -693,7 +693,7 @@ public:
refDTypep(dtp);
setOp2p(rangep);
dtypep(this);
int width = subDTypep()->width() * rangep->elementsConst();
const int width = subDTypep()->width() * rangep->elementsConst();
widthForce(width, width);
}
ASTNODE_NODE_FUNCS(PackArrayDType)
@ -848,7 +848,7 @@ private:
numeric(numer);
if (!rangep && (wantwidth || wantwidthmin >= 0)) { // Constant width
if (wantwidth > 1) m.m_nrange.init(wantwidth - 1, 0, false);
int wmin = wantwidthmin >= 0 ? wantwidthmin : wantwidth;
const int wmin = wantwidthmin >= 0 ? wantwidthmin : wantwidth;
widthForce(wantwidth, wmin);
} else if (!rangep) { // Set based on keyword properties
// V3Width will pull from this width
@ -1889,6 +1889,10 @@ private:
VDirection m_direction; // Direction input/output etc
VDirection m_declDirection; // Declared direction input/output etc
AstBasicDTypeKwd m_declKwd; // Keyword at declaration time
VLifetime m_lifetime; // Lifetime
VVarAttrClocker m_attrClocker;
MTaskIdSet m_mtaskIds; // MTaskID's that read or write this var
int m_pinNum = 0; // For XML, if non-zero the connection pin number
bool m_ansi : 1; // ANSI port list variable (for dedup check)
bool m_declTyped : 1; // Declared as type (for dedup check)
bool m_tristate : 1; // Inout or triwire or trireg
@ -1925,9 +1929,6 @@ private:
bool m_overridenParam : 1; // Overridden parameter by #(...) or defparam
bool m_trace : 1; // Trace this variable
bool m_isLatched : 1; // Not assigned in all control paths of combo always
VLifetime m_lifetime; // Lifetime
VVarAttrClocker m_attrClocker;
MTaskIdSet m_mtaskIds; // MTaskID's that read or write this var
void init() {
m_ansi = false;
@ -2058,7 +2059,8 @@ public:
string dpiArgType(bool named, bool forReturn) const; // Return DPI-C type for argument
string dpiTmpVarType(const string& varName) const;
// Return Verilator internal type for argument: CData, SData, IData, WData
string vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc = "") const;
string vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc = "",
bool asRef = false) const;
string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc
string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc
string vlPropDecl(const string& propName) const; // Return VerilatorVarProps declaration
@ -2070,7 +2072,8 @@ public:
// (Slow) recurse down to find basic data type (Note don't need virtual -
// AstVar isn't a NodeDType)
AstBasicDType* basicp() const { return subDTypep()->basicp(); }
// op3 = Initial value that never changes (static const)
// op3 = Initial value that never changes (static const), or constructor argument for
// MTASKSTATE variables
AstNode* valuep() const { return op3p(); }
// It's valuep(), not constp(), as may be more complicated than an AstConst
void valuep(AstNode* nodep) { setOp3p(nodep); }
@ -2149,7 +2152,6 @@ public:
}
bool isClassMember() const { return varType() == AstVarType::MEMBER; }
bool isStatementTemp() const { return (varType() == AstVarType::STMTTEMP); }
bool isMovableToBlock() const { return (varType() == AstVarType::BLOCKTEMP || isFuncLocal()); }
bool isXTemp() const { return (varType() == AstVarType::XTEMP); }
bool isParam() const {
return (varType() == AstVarType::LPARAM || varType() == AstVarType::GPARAM);
@ -2226,7 +2228,8 @@ public:
void addProducingMTaskId(int id) { m_mtaskIds.insert(id); }
void addConsumingMTaskId(int id) { m_mtaskIds.insert(id); }
const MTaskIdSet& mtaskIds() const { return m_mtaskIds; }
string mtasksString() const;
void pinNum(int id) { m_pinNum = id; }
int pinNum() const { return m_pinNum; }
};
class AstDefParam final : public AstNode {
@ -2388,8 +2391,7 @@ public:
if (varScopep()) {
return (varScopep() == samep->varScopep() && access() == samep->access());
} else {
return (hiernameToProt() == samep->hiernameToProt()
&& hiernameToUnprot() == samep->hiernameToUnprot()
return (selfPointer() == samep->selfPointer()
&& varp()->name() == samep->varp()->name() && access() == samep->access());
}
}
@ -2397,9 +2399,8 @@ public:
if (varScopep()) {
return (varScopep() == samep->varScopep());
} else {
return (hiernameToProt() == samep->hiernameToProt()
&& hiernameToUnprot() == samep->hiernameToUnprot()
&& (!hiernameToProt().empty() || !samep->hiernameToProt().empty())
return (selfPointer() == samep->selfPointer()
&& (!selfPointer().empty() || !samep->selfPointer().empty())
&& varp()->name() == samep->varp()->name());
}
}
@ -2439,12 +2440,33 @@ public:
virtual int instrCount() const override { return widthInstrs(); }
virtual bool same(const AstNode* samep) const override {
const AstVarXRef* asamep = static_cast<const AstVarXRef*>(samep);
return (hiernameToProt() == asamep->hiernameToProt()
&& hiernameToUnprot() == asamep->hiernameToUnprot() && varp() == asamep->varp()
return (selfPointer() == asamep->selfPointer() && varp() == asamep->varp()
&& name() == asamep->name() && dotted() == asamep->dotted());
}
};
class AstAddrOfCFunc final : public AstNodeMath {
// Get address of CFunc
private:
AstCFunc* m_funcp; // Pointer to function itself
public:
AstAddrOfCFunc(FileLine* fl, AstCFunc* funcp)
: ASTGEN_SUPER_AddrOfCFunc(fl)
, m_funcp{funcp} {
dtypep(findCHandleDType());
}
public:
ASTNODE_NODE_FUNCS(AddrOfCFunc)
virtual void cloneRelink() override;
virtual const char* broken() const override;
virtual string emitVerilog() override { V3ERROR_NA_RETURN(""); }
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
virtual bool cleanOut() const override { return true; }
AstCFunc* funcp() const { return m_funcp; }
};
class AstPin final : public AstNode {
// A pin on a cell
private:
@ -5409,9 +5431,9 @@ public:
: (m_urandom ? "%f$urandom()" : "%f$random()");
}
virtual string emitC() override {
return m_reset
? "VL_RAND_RESET_%nq(%nw, %P)"
: seedp() ? "VL_RANDOM_SEEDED_%nq%lq(%nw, %P, %li)" : "VL_RANDOM_%nq(%nw, %P)";
return m_reset ? "VL_RAND_RESET_%nq(%nw, %P)"
: seedp() ? "VL_RANDOM_SEEDED_%nq%lq(%nw, %P, %li)"
: "VL_RANDOM_%nq(%nw, %P)";
}
virtual bool cleanOut() const override { return true; }
virtual bool isGateOptimizable() const override { return false; }
@ -8715,9 +8737,8 @@ private:
string m_ctorInits; // Constructor sub-class inits
string m_ifdef; // #ifdef symbol around this function
VBoolOrUnknown m_isConst; // Function is declared const (*this not changed)
VBoolOrUnknown m_isStatic; // Function is declared static (no this)
bool m_isStatic : 1; // Function is static (no need for a 'this' pointer)
bool m_dontCombine : 1; // V3Combine shouldn't compare this func tree, it's special
bool m_skipDecl : 1; // Don't declare it
bool m_declPrivate : 1; // Declare it private
bool m_formCallTree : 1; // Make a global function to call entire tree of functions
bool m_slow : 1; // Slow routine, called once or just at init time
@ -8725,26 +8746,26 @@ private:
bool m_isConstructor : 1; // Is C class constructor
bool m_isDestructor : 1; // Is C class destructor
bool m_isMethod : 1; // Is inside a class definition
bool m_isLoose : 1; // Semantically this is a method, but is implemented as a function
// with an explicitly passed 'self' pointer as the first argument
bool m_isInline : 1; // Inline function
bool m_isVirtual : 1; // Virtual function
bool m_symProlog : 1; // Setup symbol table for later instructions
bool m_entryPoint : 1; // User may call into this top level function
bool m_pure : 1; // Pure function
bool m_dpiExport : 1; // From dpi export
bool m_dpiExportWrapper : 1; // From dpi export; static function with dispatch table
bool m_dpiImport : 1; // From dpi import
bool m_dpiImportWrapper : 1; // Wrapper from dpi import
bool m_dpiExportDispatcher : 1; // This is the DPI export entry point (i.e.: called by user)
bool m_dpiExportImpl : 1; // DPI export implementation (called from DPI dispatcher via lookup)
bool m_dpiImportPrototype : 1; // This is the DPI import prototype (i.e.: provided by user)
bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code
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_isStatic = VBoolOrUnknown::BU_UNKNOWN; // Unknown until see where thisp needed
m_scopep = scopep;
m_name = name;
m_rtnType = rtnType;
m_isStatic = false;
m_dontCombine = false;
m_skipDecl = false;
m_declPrivate = false;
m_formCallTree = false;
m_slow = false;
@ -8752,14 +8773,14 @@ public:
m_isConstructor = false;
m_isDestructor = false;
m_isMethod = true;
m_isLoose = false;
m_isInline = false;
m_isVirtual = false;
m_symProlog = false;
m_entryPoint = false;
m_pure = false;
m_dpiExport = false;
m_dpiExportWrapper = false;
m_dpiImport = false;
m_dpiExportDispatcher = false;
m_dpiExportImpl = false;
m_dpiImportPrototype = false;
m_dpiImportWrapper = false;
}
ASTNODE_NODE_FUNCS(CFunc)
@ -8774,17 +8795,17 @@ public:
const AstCFunc* asamep = static_cast<const AstCFunc*>(samep);
return ((funcType() == asamep->funcType()) && (rtnTypeVoid() == asamep->rtnTypeVoid())
&& (argTypes() == asamep->argTypes()) && (ctorInits() == asamep->ctorInits())
&& (!(dpiImport() || dpiExport()) || name() == asamep->name()));
&& isLoose() == asamep->isLoose()
&& (!(dpiImportPrototype() || dpiExportImpl()) || name() == asamep->name()));
}
//
virtual void name(const string& name) override { m_name = name; }
virtual int instrCount() const override { return dpiImport() ? instrCountDpi() : 0; }
virtual int instrCount() const override { return dpiImportPrototype() ? instrCountDpi() : 0; }
VBoolOrUnknown isConst() const { return m_isConst; }
void isConst(bool flag) { m_isConst.setTrueOrFalse(flag); }
void isConst(VBoolOrUnknown flag) { m_isConst = flag; }
VBoolOrUnknown isStatic() const { return m_isStatic; }
void isStatic(bool flag) { m_isStatic.setTrueOrFalse(flag); }
void isStatic(VBoolOrUnknown flag) { m_isStatic = flag; }
bool isStatic() const { return m_isStatic; }
void isStatic(bool flag) { m_isStatic = flag; }
void cname(const string& name) { m_cname = name; }
string cname() const { return m_cname; }
AstScope* scopep() const { return m_scopep; }
@ -8792,9 +8813,7 @@ public:
string rtnTypeVoid() const { return ((m_rtnType == "") ? "void" : m_rtnType); }
bool dontCombine() const { return m_dontCombine || funcType() != AstCFuncType::FT_NORMAL; }
void dontCombine(bool flag) { m_dontCombine = flag; }
bool dontInline() const { return dontCombine() || slow() || skipDecl() || funcPublic(); }
bool skipDecl() const { return m_skipDecl; }
void skipDecl(bool flag) { m_skipDecl = flag; }
bool dontInline() const { return dontCombine() || slow() || funcPublic(); }
bool declPrivate() const { return m_declPrivate; }
void declPrivate(bool flag) { m_declPrivate = flag; }
bool formCallTree() const { return m_formCallTree; }
@ -8817,22 +8836,23 @@ public:
void isDestructor(bool flag) { m_isDestructor = flag; }
bool isMethod() const { return m_isMethod; }
void isMethod(bool flag) { m_isMethod = flag; }
bool isLoose() const { return m_isLoose; }
void isLoose(bool flag) { m_isLoose = flag; }
bool isProperMethod() const { return isMethod() && !isLoose(); }
bool isInline() const { return m_isInline; }
void isInline(bool flag) { m_isInline = flag; }
bool isVirtual() const { return m_isVirtual; }
void isVirtual(bool flag) { m_isVirtual = flag; }
bool symProlog() const { return m_symProlog; }
void symProlog(bool flag) { m_symProlog = flag; }
bool entryPoint() const { return m_entryPoint; }
void entryPoint(bool flag) { m_entryPoint = flag; }
bool pure() const { return m_pure; }
void pure(bool flag) { m_pure = flag; }
bool dpiExport() const { return m_dpiExport; }
void dpiExport(bool flag) { m_dpiExport = flag; }
bool dpiExportWrapper() const { return m_dpiExportWrapper; }
void dpiExportWrapper(bool flag) { m_dpiExportWrapper = flag; }
bool dpiImport() const { return m_dpiImport; }
void dpiImport(bool flag) { m_dpiImport = flag; }
bool dpiExportDispatcher() const { return m_dpiExportDispatcher; }
void dpiExportDispatcher(bool flag) { m_dpiExportDispatcher = flag; }
bool dpiExportImpl() const { return m_dpiExportImpl; }
void dpiExportImpl(bool flag) { m_dpiExportImpl = flag; }
bool dpiImportPrototype() const { return m_dpiImportPrototype; }
void dpiImportPrototype(bool flag) { m_dpiImportPrototype = flag; }
bool dpiImportWrapper() const { return m_dpiImportWrapper; }
void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; }
//
@ -9027,7 +9047,8 @@ class AstExecGraph final : public AstNode {
// them without traversing the graph (it's not always needed to
// traverse the graph.)
private:
V3Graph* m_depGraphp; // contains ExecMTask's
V3Graph* const m_depGraphp; // contains ExecMTask's
public:
explicit AstExecGraph(FileLine* fl);
ASTNODE_NODE_FUNCS_NO_DTOR(ExecGraph)
@ -9039,6 +9060,7 @@ public:
const V3Graph* depGraphp() const { return m_depGraphp; }
V3Graph* mutableDepGraphp() { return m_depGraphp; }
void addMTaskBody(AstMTaskBody* bodyp) { addOp1p(bodyp); }
void addStmtsp(AstNode* stmtp) { addOp2p(stmtp); }
};
class AstSplitPlaceholder final : public AstNode {
@ -9063,10 +9085,7 @@ class AstTypeTable final : public AstNode {
DetailedMap m_detailedMap;
public:
explicit AstTypeTable(FileLine* fl)
: ASTGEN_SUPER_TypeTable(fl) {
for (int i = 0; i < AstBasicDTypeKwd::_ENUM_MAX; ++i) m_basicps[i] = nullptr;
}
explicit AstTypeTable(FileLine* fl);
ASTNODE_NODE_FUNCS(TypeTable)
AstNodeDType* typesp() const { return VN_CAST(op1p(), NodeDType); } // op1 = List of dtypes
void addTypesp(AstNodeDType* nodep) { addOp1p(nodep); }
@ -9083,6 +9102,34 @@ public:
virtual void dump(std::ostream& str = std::cout) const override;
};
class AstConstPool final : public AstNode {
// Container for const static data
std::unordered_multimap<uint32_t, AstVarScope*> m_tables; // Constant tables (unpacked arrays)
std::unordered_multimap<uint32_t, AstVarScope*> m_consts; // Constant tables (scalars)
AstModule* const m_modp; // The Module holding the Scope below ...
AstScope* const m_scopep; // Scope holding the constant variables
AstVarScope* createNewEntry(const string& name, AstNode* initp);
public:
explicit AstConstPool(FileLine* fl);
ASTNODE_NODE_FUNCS(ConstPool)
AstModule* modp() const { return m_modp; }
// Find a table (unpacked array) within the constant pool which is initialized with the
// given value, or create one if one does not already exists. The returned VarScope *might*
// have a different dtype than the given initp->dtypep(), including a different element type,
// but it will always have the same size and element width. In contexts where this matters,
// the caller must handle the dtype difference as appropriate.
AstVarScope* findTable(AstInitArray* initp);
// Find a constant within the constant pool which is initialized with the given value, or
// create one if one does not already exists. If 'mergeDType' is true, then the returned
// VarScope *might* have a different type than the given initp->dtypep(). In contexts where
// this matters, the caller must handle the dtype difference as appropriate. If 'mergeDType' is
// false, the returned VarScope will have _->dtypep()->sameTree(initp->dtypep()) return true.
AstVarScope* findConst(AstConst* initp, bool mergeDType);
};
//######################################################################
// Top
@ -9091,7 +9138,8 @@ class AstNetlist final : public AstNode {
// Parents: none
// Children: MODULEs & CFILEs
private:
AstTypeTable* m_typeTablep = nullptr; // Reference to top type table, for faster lookup
AstTypeTable* const m_typeTablep; // Reference to top type table, for faster lookup
AstConstPool* const m_constPoolp; // Reference to constant pool, for faster lookup
AstPackage* m_dollarUnitPkgp = nullptr; // $unit
AstCFunc* m_evalp = nullptr; // The '_eval' function
AstExecGraph* m_execGraphp = nullptr; // Execution MTask graph for threads>1 mode
@ -9099,8 +9147,7 @@ private:
VTimescale m_timeprecision; // Global time precision
bool m_timescaleSpecified = false; // Input HDL specified timescale
public:
AstNetlist()
: ASTGEN_SUPER_Netlist(new FileLine(FileLine::builtInFilename())) {}
AstNetlist();
ASTNODE_NODE_FUNCS(Netlist)
virtual const char* broken() const override {
BROKEN_RTN(m_dollarUnitPkgp && !m_dollarUnitPkgp->brokeExists());
@ -9121,10 +9168,7 @@ public:
AstNode* miscsp() const { return op3p(); } // op3 = List of dtypes etc
void addMiscsp(AstNode* nodep) { addOp3p(nodep); }
AstTypeTable* typeTablep() { return m_typeTablep; }
void addTypeTablep(AstTypeTable* nodep) {
m_typeTablep = nodep;
addMiscsp(nodep);
}
AstConstPool* constPoolp() { return m_constPoolp; }
AstPackage* dollarUnitPkgp() const { return m_dollarUnitPkgp; }
AstPackage* dollarUnitPkgAddp() {
if (!m_dollarUnitPkgp) {

119
src/V3AstUserAllocator.h Normal file
View File

@ -0,0 +1,119 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Utility to hang advanced data structures of
// AstNode::user*p() pointers with automatic memory management.
//
// 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_V3ASTUSERALLOCATOR_H_
#define VERILATOR_V3ASTUSERALLOCATOR_H_
#include "config_build.h"
#include "verilatedos.h"
#include "V3Ast.h"
#include <type_traits>
#include <vector>
template <class T_Node, class T_Data, int T_UserN> class AstUserAllocatorBase VL_NOT_FINAL {
static_assert(1 <= T_UserN && T_UserN <= 5, "Wrong user pointer number");
static_assert(std::is_base_of<AstNode, T_Node>::value, "T_Node must be an AstNode type");
private:
std::vector<T_Data*> m_allocated;
inline T_Data* getUserp(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();
return user.to<T_Data*>();
} else if (T_UserN == 2) {
const VNUser user = nodep->user2u();
return user.to<T_Data*>();
} else if (T_UserN == 3) {
const VNUser user = nodep->user3u();
return user.to<T_Data*>();
} else if (T_UserN == 4) {
const VNUser user = nodep->user4u();
return user.to<T_Data*>();
} else {
const VNUser user = nodep->user5u();
return user.to<T_Data*>();
}
}
inline void setUserp(T_Node* nodep, T_Data* userp) const {
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
if (T_UserN == 1) {
nodep->user1u(VNUser(userp));
} else if (T_UserN == 2) {
nodep->user2u(VNUser(userp));
} else if (T_UserN == 3) {
nodep->user3u(VNUser(userp));
} else if (T_UserN == 4) {
nodep->user4u(VNUser(userp));
} else {
nodep->user5u(VNUser(userp));
}
}
protected:
AstUserAllocatorBase() {
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
if (T_UserN == 1) {
AstUser1InUse::check();
} else if (T_UserN == 2) {
AstUser2InUse::check();
} else if (T_UserN == 3) {
AstUser3InUse::check();
} else if (T_UserN == 4) {
AstUser4InUse::check();
} else {
AstUser5InUse::check();
}
}
virtual ~AstUserAllocatorBase() {
// Delete all allocated data structures
for (T_Data* const p : m_allocated) { delete p; }
}
public:
// Get a reference to the user data
T_Data& operator()(T_Node* nodep) {
T_Data* userp = getUserp(nodep);
if (!userp) {
userp = new T_Data;
m_allocated.push_back(userp);
setUserp(nodep, userp);
}
return *userp;
}
};
// User pointer allocator classes. T_Node is the type of node the allocator should be applied to
// and is simply there for a bit of extra type safety. T_Data is the type of the data structure
// managed by the allocator.
template <class T_Node, class T_Data>
class AstUser1Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 1> {};
template <class T_Node, class T_Data>
class AstUser2Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 2> {};
template <class T_Node, class T_Data>
class AstUser3Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 3> {};
template <class T_Node, class T_Data>
class AstUser4Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 4> {};
template <class T_Node, class T_Data>
class AstUser5Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 5> {};
#endif // Guard

View File

@ -117,7 +117,7 @@ private:
string dottedname = nodep->name() + "__DOT__"; // So always found
string::size_type pos;
while ((pos = dottedname.find("__DOT__")) != string::npos) {
string ident = dottedname.substr(0, pos);
const string ident = dottedname.substr(0, pos);
dottedname = dottedname.substr(pos + strlen("__DOT__"));
if (nodep->name() != "") m_namedScope = dot(m_namedScope, ident);
m_unnamedScope = dot(m_unnamedScope, ident);

View File

@ -70,15 +70,15 @@ private:
// Do if
reset();
iterateAndNextNull(nodep->ifsp());
int ifLikely = m_likely;
int ifUnlikely = m_unlikely;
const int ifLikely = m_likely;
const int ifUnlikely = m_unlikely;
// Do else
reset();
iterateAndNextNull(nodep->elsesp());
int elseLikely = m_likely;
int elseUnlikely = m_unlikely;
const int elseLikely = m_likely;
const int elseUnlikely = m_unlikely;
// Compute
int likeness = ifLikely - ifUnlikely - (elseLikely - elseUnlikely);
const int likeness = ifLikely - ifUnlikely - (elseLikely - elseUnlikely);
if (likeness > 0) {
nodep->branchPred(VBranchPred::BP_LIKELY);
} else if (likeness < 0) {

View File

@ -72,7 +72,7 @@ public:
UASSERT_OBJ(!(iter != s_nodes.end() && (iter->second & FLAG_ALLOCATED)), nodep,
"Newing AstNode object that is already allocated");
if (iter == s_nodes.end()) {
int flags = FLAG_ALLOCATED; // This int needed to appease GCC 4.1.2
const int flags = FLAG_ALLOCATED; // This int needed to appease GCC 4.1.2
s_nodes.emplace(nodep, flags);
}
}
@ -103,7 +103,7 @@ public:
UASSERT_OBJ(!(iter->second & FLAG_IN_TREE), nodep,
"AstNode is already in tree at another location");
}
int or_flags = FLAG_IN_TREE | (linkable ? FLAG_LINKABLE : 0);
const int or_flags = FLAG_IN_TREE | (linkable ? FLAG_LINKABLE : 0);
if (iter == s_nodes.end()) {
s_nodes.emplace(nodep, or_flags);
} else {

View File

@ -61,23 +61,21 @@ private:
const int funcNum = m_newFunctions.size();
const string funcName = m_basename + "_" + cvtToStr(funcNum);
AstCFunc* const funcp = new AstCFunc(m_modp->fileline(), funcName, nullptr, "void");
funcp->isStatic(!m_type.isClass()); // Class constructors are non static
funcp->isStatic(false);
funcp->isLoose(!m_type.isClass());
funcp->declPrivate(true);
funcp->slow(!m_type.isClass()); // Only classes construct on fast path
string preventUnusedStmt;
if (m_type.isClass()) {
funcp->argTypes(EmitCBaseVisitor::symClassVar());
preventUnusedStmt = "if (false && vlSymsp) {}";
preventUnusedStmt = "if (false && vlSymsp) {} // Prevent unused\n";
} else if (m_type.isCoverage()) {
funcp->argTypes(EmitCBaseVisitor::prefixNameProtect(m_modp) + "* self, "
+ EmitCBaseVisitor::symClassVar() + ", bool first");
preventUnusedStmt = "if (false && self && vlSymsp && first) {}";
} else { // Module
funcp->argTypes(EmitCBaseVisitor::prefixNameProtect(m_modp) + "* self");
preventUnusedStmt = "if (false && self) {}";
funcp->argTypes("bool first");
preventUnusedStmt = "if (false && first) {} // Prevent unused\n";
}
if (!preventUnusedStmt.empty()) {
funcp->addStmtsp(new AstCStmt(m_modp->fileline(), preventUnusedStmt));
}
preventUnusedStmt += " // Prevent unused\n";
funcp->addStmtsp(new AstCStmt(m_modp->fileline(), preventUnusedStmt));
m_modp->addStmtp(funcp);
m_numStmts = 0;
return funcp;
@ -113,10 +111,9 @@ public:
AstCCall* const callp = new AstCCall(m_modp->fileline(), funcp);
if (m_type.isClass()) {
callp->argTypes("vlSymsp");
} else if (m_type.isCoverage()) {
callp->argTypes("self, vlSymsp, first");
} else { // Module
callp->argTypes("self");
} else {
if (m_type.isCoverage()) callp->argTypes("first");
callp->selfPointer("this");
}
rootFuncp->addStmtsp(callp);
}
@ -134,6 +131,7 @@ void V3CCtors::evalAsserts() {
AstCFunc* funcp = new AstCFunc(modp->fileline(), "_eval_debug_assertions", nullptr, "void");
funcp->declPrivate(true);
funcp->isStatic(false);
funcp->isLoose(true);
funcp->slow(false);
funcp->ifdef("VL_DEBUG");
modp->addStmtp(funcp);
@ -141,11 +139,14 @@ void V3CCtors::evalAsserts() {
if (AstVar* varp = VN_CAST(np, Var)) {
if (varp->isPrimaryInish() && !varp->isSc()) {
if (AstBasicDType* basicp = VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
int storedWidth = basicp->widthAlignBytes() * 8;
int lastWordWidth = varp->width() % storedWidth;
const int storedWidth = basicp->widthAlignBytes() * 8;
const int lastWordWidth = varp->width() % storedWidth;
if (lastWordWidth != 0) {
// if (signal & CONST(upper_non_clean_mask)) { fail; }
AstNode* newp = new AstVarRef(varp->fileline(), varp, VAccess::READ);
AstVarRef* const vrefp
= new AstVarRef(varp->fileline(), varp, VAccess::READ);
vrefp->selfPointer("this");
AstNode* newp = vrefp;
if (varp->isWide()) {
newp = new AstWordSel(
varp->fileline(), newp,

View File

@ -151,7 +151,7 @@ private:
*m_ofp << " ";
}
*m_ofp << nodep->prettyTypeName() << "\n";
string lastPrefix = m_prefix;
const string lastPrefix = m_prefix;
m_prefix = lastPrefix + "1:";
iterateAndNextNull(nodep->op1p());
m_prefix = lastPrefix + "2:";
@ -449,7 +449,7 @@ private:
if (!vertexp->asyncPath() && level != 0) return false; // Not part of path
// Other logic in the path
string cont = prefix + sep;
const string cont = prefix + sep;
string nextsep = " ";
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
CdcEitherVertex* eFromVertexp = static_cast<CdcEitherVertex*>(edgep->fromp());
@ -506,7 +506,8 @@ private:
}
}
string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc_edges.txt";
const string filename
= v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc_edges.txt";
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';
@ -523,7 +524,7 @@ private:
os.setf(std::ios::left);
// Module name - doesn't work due to flattening having lost the original
// so we assume the modulename matches the filebasename
string fname = vvertexp->varScp()->fileline()->filebasename() + ":";
const string fname = vvertexp->varScp()->fileline()->filebasename() + ":";
os << " " << std::setw(20) << fname;
os << " " << std::setw(8) << what;
os << " " << std::setw(40) << vvertexp->varScp()->prettyName();
@ -727,7 +728,7 @@ public:
// CONSTRUCTORS
explicit CdcVisitor(AstNode* nodep) {
// Make report of all signal names and what clock edges they have
string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc.txt";
const string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc.txt";
m_ofp = V3File::new_ofstream(filename);
if (m_ofp->fail()) v3fatal("Can't write " << filename);
m_ofFilename = filename;

View File

@ -61,14 +61,13 @@ public:
m_chgFuncp
= new AstCFunc(m_scopetopp->fileline(), "_change_request_" + cvtToStr(++m_funcNum),
m_scopetopp, "QData");
m_chgFuncp->argTypes(EmitCBaseVisitor::symClassVar());
m_chgFuncp->symProlog(true);
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);
callp->argTypes("vlSymsp");
if (!m_tlChgFuncp->stmtsp()) {
m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp));
@ -251,8 +250,8 @@ private:
// 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->argTypes(EmitCBaseVisitor::symClassVar());
m_statep->m_tlChgFuncp->symProlog(true);
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

View File

@ -68,7 +68,7 @@ private:
void setCppWidth(AstNode* nodep) {
nodep->user2(true); // Don't resize it again
AstNodeDType* old_dtypep = nodep->dtypep();
int width = cppWidth(nodep); // widthMin is unchanged
const int width = cppWidth(nodep); // widthMin is unchanged
if (old_dtypep->width() != width) {
// Since any given dtype's cppWidth() is the same, we can just
// remember one conversion for each, and reuse it
@ -103,7 +103,7 @@ private:
void setCleanState(AstNode* nodep, CleanState clean) { nodep->user1(clean); }
CleanState getCleanState(AstNode* nodep) { return static_cast<CleanState>(nodep->user1()); }
bool isClean(AstNode* nodep) {
CleanState clstate = getCleanState(nodep);
const CleanState clstate = getCleanState(nodep);
if (clstate == CS_CLEAN) return true;
if (clstate == CS_DIRTY) return false;
nodep->v3fatalSrc("Unknown clean state on node: " + nodep->prettyTypeName());
@ -111,8 +111,9 @@ private:
}
void setClean(AstNode* nodep, bool isClean) {
computeCppWidth(nodep); // Just to be sure it's in widthMin
bool wholeUint = (nodep->widthMin() == VL_IDATASIZE || nodep->widthMin() == VL_QUADSIZE
|| (nodep->widthMin() % VL_EDATASIZE) == 0);
const bool wholeUint
= (nodep->widthMin() == VL_IDATASIZE || nodep->widthMin() == VL_QUADSIZE
|| (nodep->widthMin() % VL_EDATASIZE) == 0);
setCleanState(nodep, ((isClean || wholeUint) ? CS_CLEAN : CS_DIRTY));
}

View File

@ -37,6 +37,34 @@
#include <algorithm>
//######################################################################
// Convert every WRITE AstVarRef to a READ ref
class ConvertWriteRefsToRead final : public AstNVisitor {
private:
// MEMBERS
AstNode* m_result = nullptr;
// CONSTRUCTORS
explicit ConvertWriteRefsToRead(AstNode* nodep) {
m_result = iterateSubtreeReturnEdits(nodep);
}
// VISITORS
void visit(AstVarRef* nodep) override {
UASSERT_OBJ(!nodep->access().isRW(), nodep, "Cannot handle a READWRITE reference");
if (nodep->access().isWriteOnly()) {
nodep->replaceWith(
new AstVarRef(nodep->fileline(), nodep->varScopep(), VAccess::READ));
}
}
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
static AstNode* main(AstNode* nodep) { return ConvertWriteRefsToRead(nodep).m_result; }
};
//######################################################################
// Clock state, as a visitor of each AstNode
@ -164,6 +192,18 @@ private:
m_lastSenp = nullptr;
m_lastIfp = nullptr;
}
AstCFunc* makeTopFunction(const string& name, bool slow = false) {
AstCFunc* const funcp = new AstCFunc{m_topScopep->fileline(), name, m_topScopep->scopep()};
funcp->dontCombine(true);
funcp->isStatic(false);
funcp->isLoose(true);
funcp->entryPoint(true);
funcp->slow(slow);
funcp->isConst(false);
funcp->declPrivate(true);
m_topScopep->scopep()->addActivep(funcp);
return funcp;
}
void splitCheck(AstCFunc* ofuncp) {
if (!v3Global.opt.outputSplitCFuncs() || !ofuncp->stmtsp()) return;
if (EmitCBaseCounterVisitor(ofuncp->stmtsp()).count() < v3Global.opt.outputSplitCFuncs())
@ -179,20 +219,18 @@ private:
if (ofuncp->finalsp()) tempp->addStmtsp(ofuncp->finalsp()->unlinkFrBackWithNext());
while (tempp->stmtsp()) {
AstNode* itemp = tempp->stmtsp()->unlinkFrBack();
int stmts = EmitCBaseCounterVisitor(itemp).count();
const int stmts = EmitCBaseCounterVisitor(itemp).count();
if (!funcp || (func_stmts + stmts) > v3Global.opt.outputSplitCFuncs()) {
// Make a new function
funcp = new AstCFunc{ofuncp->fileline(), ofuncp->name() + cvtToStr(++funcnum),
m_topScopep->scopep()};
funcp->argTypes(EmitCBaseVisitor::symClassVar());
funcp->dontCombine(true);
funcp->symProlog(true);
funcp->isStatic(true);
funcp->isStatic(false);
funcp->isLoose(true);
funcp->slow(ofuncp->slow());
m_topScopep->scopep()->addActivep(funcp);
//
AstCCall* callp = new AstCCall{funcp->fileline(), funcp};
callp->argTypes("vlSymsp");
ofuncp->addStmtsp(callp);
func_stmts = 0;
}
@ -212,55 +250,10 @@ private:
// VV***** We reset all user1p()
AstNode::user1ClearTree();
// Make top functions
{
AstCFunc* funcp = new AstCFunc{nodep->fileline(), "_eval", m_topScopep->scopep()};
funcp->argTypes(EmitCBaseVisitor::symClassVar());
funcp->dontCombine(true);
funcp->symProlog(true);
funcp->isStatic(true);
funcp->entryPoint(true);
m_topScopep->scopep()->addActivep(funcp);
m_evalFuncp = funcp;
}
{
AstCFunc* funcp
= new AstCFunc{nodep->fileline(), "_eval_initial", m_topScopep->scopep()};
funcp->argTypes(EmitCBaseVisitor::symClassVar());
funcp->dontCombine(true);
funcp->slow(true);
funcp->symProlog(true);
funcp->isStatic(true);
funcp->entryPoint(true);
m_topScopep->scopep()->addActivep(funcp);
m_initFuncp = funcp;
}
{
AstCFunc* funcp = new AstCFunc{nodep->fileline(), "final", m_topScopep->scopep()};
funcp->skipDecl(true);
funcp->dontCombine(true);
funcp->slow(true);
funcp->isStatic(false);
funcp->entryPoint(true);
funcp->protect(false);
funcp->addInitsp(new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symClassVar()
+ " = this->__VlSymsp;\n"));
funcp->addInitsp(
new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign() + "\n"));
m_topScopep->scopep()->addActivep(funcp);
m_finalFuncp = funcp;
}
{
AstCFunc* funcp
= new AstCFunc{nodep->fileline(), "_eval_settle", m_topScopep->scopep()};
funcp->argTypes(EmitCBaseVisitor::symClassVar());
funcp->dontCombine(true);
funcp->slow(true);
funcp->isStatic(true);
funcp->symProlog(true);
funcp->entryPoint(true);
m_topScopep->scopep()->addActivep(funcp);
m_settleFuncp = funcp;
}
m_evalFuncp = makeTopFunction("_eval");
m_initFuncp = makeTopFunction("_eval_initial", /* slow: */ true);
m_settleFuncp = makeTopFunction("_eval_settle", /* slow: */ true);
m_finalFuncp = makeTopFunction("_final", /* slow: */ true);
// Process the activates
iterateChildren(nodep);
UINFO(4, " TOPSCOPE iter done " << nodep << endl);
@ -307,14 +300,14 @@ private:
// IF(ORIG ^ CHANGE) { INC; CHANGE = ORIG; }
AstNode* incp = nodep->incp()->unlinkFrBack();
AstNode* origp = nodep->origp()->unlinkFrBack();
AstNode* changep = nodep->changep()->unlinkFrBack();
AstIf* newp = new AstIf(nodep->fileline(), new AstXor(nodep->fileline(), origp, changep),
AstNode* changeWrp = nodep->changep()->unlinkFrBack();
AstNode* changeRdp = ConvertWriteRefsToRead::main(changeWrp->cloneTree(false));
AstIf* newp = new AstIf(nodep->fileline(), new AstXor(nodep->fileline(), origp, changeRdp),
incp, nullptr);
// We could add another IF to detect posedges, and only increment if so.
// It's another whole branch though versus a potential memory miss.
// We'll go with the miss.
newp->addIfsp(
new AstAssign(nodep->fileline(), changep->cloneTree(false), origp->cloneTree(false)));
newp->addIfsp(new AstAssign(nodep->fileline(), changeWrp, origp->cloneTree(false)));
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
@ -324,7 +317,6 @@ private:
if (nodep->formCallTree()) {
UINFO(4, " formCallTree " << nodep << endl);
AstCCall* callp = new AstCCall(nodep->fileline(), nodep);
callp->argTypes("vlSymsp");
m_finalFuncp->addStmtsp(callp);
}
}

View File

@ -72,8 +72,7 @@ public:
AstNode* const argsp
= oldp->argsp() ? oldp->argsp()->unlinkFrBackWithNext() : nullptr;
AstCCall* const newp = new AstCCall(oldp->fileline(), newfuncp, argsp);
newp->hiernameToProt(oldp->hiernameToProt());
newp->hiernameToUnprot(oldp->hiernameToUnprot());
newp->selfPointer(oldp->selfPointer());
newp->argTypes(oldp->argTypes());
addCall(newp); // Fix the table, in case the newfuncp itself gets replaced
oldp->replaceWith(newp);
@ -86,14 +85,23 @@ public:
}
}
// METHODS
void addCall(AstCCall* nodep) {
if (nodep->funcp()->dontCombine()) return;
m_callMmap.emplace(nodep->funcp(), nodep);
}
void addCall(AstCCall* nodep) { m_callMmap.emplace(nodep->funcp(), nodep); }
private:
// VISITORS
virtual void visit(AstCCall* nodep) override { addCall(nodep); }
virtual void visit(AstCCall* nodep) override {
if (nodep->funcp()->dontCombine()) return;
addCall(nodep);
}
// LCOV_EXCL_START
virtual void visit(AstAddrOfCFunc* nodep) override {
// We cannot yet handle references via AstAddrOfCFunc, but currently those are
// only used in tracing functions, which are not combined. Blow up in case this changes.
if (nodep->funcp()->dontCombine()) return;
nodep->v3fatalSrc(
"Don't know how to combine functions that are referenced via AstAddrOfCFunc");
}
// LCOV_EXCL_END
// Speed things up
virtual void visit(AstNodeAssign*) override {}
virtual void visit(AstNodeMath*) override {}

View File

@ -201,7 +201,7 @@ public:
}
void applyBlock(AstNodeBlock* nodep) {
AstPragmaType pragma = AstPragmaType::COVERAGE_BLOCK_OFF;
const AstPragmaType pragma = AstPragmaType::COVERAGE_BLOCK_OFF;
if (!nodep->unnamed()) {
for (const string& i : m_coverageOffBlocks) {
if (VString::wildmatch(nodep->name(), i)) {
@ -297,14 +297,14 @@ public:
void applyBlock(AstNodeBlock* nodep) {
// Apply to block at this line
AstPragmaType pragma = AstPragmaType::COVERAGE_BLOCK_OFF;
const AstPragmaType pragma = AstPragmaType::COVERAGE_BLOCK_OFF;
if (lineMatch(nodep->fileline()->lineno(), pragma)) {
nodep->addStmtsp(new AstPragma(nodep->fileline(), pragma));
}
}
void applyCase(AstCase* nodep) {
// Apply to this case at this line
int lineno = nodep->fileline()->lineno();
const int lineno = nodep->fileline()->lineno();
if (lineMatch(lineno, AstPragmaType::FULL_CASE)) nodep->fullPragma(true);
if (lineMatch(lineno, AstPragmaType::PARALLEL_CASE)) nodep->parallelPragma(true);
}
@ -313,7 +313,7 @@ public:
if (m_lastIgnore.lineno != filelinep->lineno()) {
// UINFO(9, " ApplyIgnores for " << filelinep->ascii() << endl);
// Process all on/offs for lines up to and including the current line
int curlineno = filelinep->lastLineno();
const int curlineno = filelinep->lastLineno();
for (; m_lastIgnore.it != m_ignLines.end(); ++m_lastIgnore.it) {
if (m_lastIgnore.it->m_lineno > curlineno) break;
// UINFO(9, " Hit " << *m_lastIt << endl);

View File

@ -82,13 +82,58 @@ public:
class ConstBitOpTreeVisitor final : public AstNVisitor {
// TYPES
struct LeafInfo { // Leaf node (either AstConst or AstVarRef)
struct LeafInfo final { // Leaf node (either AstConst or AstVarRef)
bool m_polarity = true;
int m_lsb = 0;
int m_wordIdx = -1; // -1 means AstWordSel is not used.
AstVarRef* m_refp = nullptr;
AstConst* m_constp = nullptr;
};
struct BitPolarityEntry final { // Found bit polarity during iterate()
LeafInfo m_info;
bool m_polarity;
int m_bit;
BitPolarityEntry(const LeafInfo& info, bool pol, int bit)
: m_info(info)
, m_polarity(pol)
, m_bit(bit) {}
BitPolarityEntry() = default;
};
class Restorer final { // Restore the original state unless disableRestore() is called
ConstBitOpTreeVisitor& m_visitor;
const size_t m_polaritiesSize;
const size_t m_frozenSize;
const int m_ops;
const bool m_polarity;
bool m_restore;
public:
explicit Restorer(ConstBitOpTreeVisitor& visitor)
: m_visitor(visitor)
, m_polaritiesSize(visitor.m_bitPolarities.size())
, m_frozenSize(visitor.m_frozenNodes.size())
, m_ops(visitor.m_ops)
, m_polarity(visitor.m_polarity)
, m_restore(true) {}
~Restorer() {
UASSERT(m_visitor.m_bitPolarities.size() >= m_polaritiesSize,
"m_bitPolarities must grow monotorilaclly");
UASSERT(m_visitor.m_frozenNodes.size() >= m_frozenSize,
"m_frozenNodes must grow monotorilaclly");
if (m_restore) restoreNow();
}
void disableRestore() { m_restore = false; }
void restoreNow() {
UASSERT(m_restore, "Can be called just once");
m_visitor.m_bitPolarities.resize(m_polaritiesSize);
m_visitor.m_frozenNodes.resize(m_frozenSize);
m_visitor.m_ops = m_ops;
m_visitor.m_polarity = m_polarity;
m_restore = false;
}
};
// Collect information for each Variable to transform as below
class VarInfo final {
// MEMBERS
@ -195,7 +240,8 @@ class ConstBitOpTreeVisitor final : public AstNVisitor {
AstUser4InUse m_inuser4;
std::vector<AstNode*> m_frozenNodes; // Nodes that cannot be optimized
std::vector<VarInfo*> m_varInfos; // VarInfo for each variable, [0] is nullptr
std::vector<BitPolarityEntry> m_bitPolarities; // Polarity of bits found during iterate()
std::vector<std::unique_ptr<VarInfo>> m_varInfos; // VarInfo for each variable, [0] is nullptr
// NODE STATE
// AstVarRef::user4u -> Base index of m_varInfos that points VarInfo
@ -235,14 +281,14 @@ class ConstBitOpTreeVisitor final : public AstNVisitor {
baseIdx = m_varInfos.size();
const int numWords
= ref.m_refp->dtypep()->isWide() ? ref.m_refp->dtypep()->widthWords() : 1;
m_varInfos.resize(m_varInfos.size() + numWords, nullptr);
m_varInfos.resize(m_varInfos.size() + numWords);
nodep->user4(baseIdx);
}
const size_t idx = baseIdx + std::max(0, ref.m_wordIdx);
VarInfo* varInfop = m_varInfos[idx];
VarInfo* varInfop = m_varInfos[idx].get();
if (!varInfop) {
varInfop = new VarInfo{this, ref.m_refp};
m_varInfos[idx] = varInfop;
m_varInfos[idx].reset(varInfop);
} else {
if (!varInfop->sameVarAs(ref.m_refp))
CONST_BITOP_SET_FAILED("different var (scope?)", ref.m_refp);
@ -325,28 +371,30 @@ class ConstBitOpTreeVisitor final : public AstNVisitor {
}
virtual void visit(AstRedXor* nodep) override { // Expect '^(mask & v)'
Restorer restorer{*this};
CONST_BITOP_RETURN_IF(!VN_IS(m_rootp, Xor), nodep);
AstAnd* andp = VN_CAST(nodep->lhsp(), And);
CONST_BITOP_RETURN_IF(!andp, nodep->lhsp());
auto mask = findLeaf(andp->lhsp(), true);
const auto mask = findLeaf(andp->lhsp(), true);
CONST_BITOP_RETURN_IF(!mask.m_constp || mask.m_lsb != 0, andp->lhsp());
LeafInfo leaf = findLeaf(andp->rhsp(), false);
const LeafInfo leaf = findLeaf(andp->rhsp(), false);
CONST_BITOP_RETURN_IF(!leaf.m_refp, andp->rhsp());
restorer.disableRestore(); // Now all subtree succeeded
incrOps(nodep, __LINE__);
incrOps(andp, __LINE__);
const V3Number& maskNum = mask.m_constp->num();
VarInfo& varInfo = getVarInfo(leaf);
for (int i = 0; i < maskNum.width(); ++i) {
// Set true, m_treePolarity takes care of the entire parity
if (maskNum.bitIs1(i)) varInfo.setPolarity(true, i + leaf.m_lsb);
if (maskNum.bitIs1(i)) m_bitPolarities.emplace_back(leaf, true, i + leaf.m_lsb);
}
}
virtual void visit(AstNodeBiop* nodep) override {
auto isConst = [](AstNode* nodep, vluint64_t v) -> bool {
const auto isConst = [](AstNode* nodep, vluint64_t v) -> bool {
AstConst* constp = VN_CAST(nodep, Const);
return constp && constp->toUQuad() == v;
};
@ -357,29 +405,28 @@ class ConstBitOpTreeVisitor final : public AstNVisitor {
VL_RESTORER(m_leafp);
for (int i = 0; i < 2; ++i) {
Restorer restorer{*this};
LeafInfo leafInfo;
m_leafp = &leafInfo;
m_curOpp = i == 0 ? nodep->lhsp() : nodep->rhsp();
const size_t origFrozens = m_frozenNodes.size();
const int origOps = m_ops;
const bool origFailed = m_failed;
iterate(m_curOpp);
if (leafInfo.m_constp || m_failed) {
// Rvert changes in leaf
if (m_frozenNodes.size() > origFrozens) m_frozenNodes.resize(origFrozens);
restorer.restoreNow();
m_frozenNodes.push_back(m_curOpp);
m_ops = origOps;
m_failed = origFailed;
} else if (leafInfo.m_refp) {
VarInfo& varInfo = getVarInfo(leafInfo);
if (!varInfo.hasConstantResult()) {
varInfo.setPolarity(isXorTree() || leafInfo.m_polarity, leafInfo.m_lsb);
}
continue;
}
restorer.disableRestore(); // Now all checks passed
if (leafInfo.m_refp)
m_bitPolarities.emplace_back(leafInfo, isXorTree() || leafInfo.m_polarity,
leafInfo.m_lsb);
}
return;
} else if (VN_IS(m_rootp, Xor) && VN_IS(nodep, Eq) && isConst(nodep->lhsp(), 0)
&& VN_IS(nodep->rhsp(), And)) { // 0 == (1 & RedXor)
Restorer restorer{*this};
AstAnd* andp = static_cast<AstAnd*>(nodep->rhsp()); // already checked above
CONST_BITOP_RETURN_IF(!isConst(andp->lhsp(), 1), andp->lhsp());
AstRedXor* redXorp = VN_CAST(andp->rhsp(), RedXor);
@ -388,36 +435,43 @@ class ConstBitOpTreeVisitor final : public AstNVisitor {
incrOps(andp, __LINE__);
m_polarity = !m_polarity;
iterate(redXorp);
CONST_BITOP_RETURN_IF(m_failed, redXorp);
restorer.disableRestore(); // Now all checks passed
return;
} else if (VN_IS(m_rootp, Xor) && VN_IS(nodep, And) && isConst(nodep->lhsp(), 1)
&& (VN_IS(nodep->rhsp(), Xor)
|| VN_IS(nodep->rhsp(), RedXor))) { // 1 & (v[3] ^ v[2])
Restorer restorer{*this};
incrOps(nodep, __LINE__);
iterate(nodep->rhsp());
CONST_BITOP_RETURN_IF(m_failed, nodep->rhsp());
restorer.disableRestore(); // Now all checks passed
return;
} else if ((isAndTree() && VN_IS(nodep, Eq)) || (isOrTree() && VN_IS(nodep, Neq))) {
Restorer restorer{*this};
CONST_BITOP_RETURN_IF(!m_polarity, nodep);
const bool maskFlip = isOrTree();
LeafInfo comp = findLeaf(nodep->lhsp(), true);
const LeafInfo comp = findLeaf(nodep->lhsp(), true);
CONST_BITOP_RETURN_IF(!comp.m_constp || comp.m_lsb != 0, nodep->lhsp());
AstAnd* andp = VN_CAST(nodep->rhsp(), And); // comp == (mask & v)
CONST_BITOP_RETURN_IF(!andp, nodep->rhsp());
LeafInfo mask = findLeaf(andp->lhsp(), true);
const LeafInfo mask = findLeaf(andp->lhsp(), true);
CONST_BITOP_RETURN_IF(!mask.m_constp || mask.m_lsb != 0, andp->lhsp());
LeafInfo ref = findLeaf(andp->rhsp(), false);
const LeafInfo ref = findLeaf(andp->rhsp(), false);
CONST_BITOP_RETURN_IF(!ref.m_refp, andp->rhsp());
VarInfo& varInfo = getVarInfo(ref);
restorer.disableRestore(); // Now all checks passed
const V3Number maskNum = mask.m_constp->num();
const V3Number compNum = comp.m_constp->num();
for (int i = 0; i < maskNum.width() && !varInfo.hasConstantResult(); ++i) {
for (int i = 0; i < maskNum.width(); ++i) {
const int bit = i + ref.m_lsb;
if (maskNum.bitIs0(i)) continue;
varInfo.setPolarity(compNum.bitIs1(i) ^ maskFlip, bit);
m_bitPolarities.emplace_back(ref, compNum.bitIs1(i) != maskFlip, bit);
}
incrOps(nodep, __LINE__);
incrOps(andp, __LINE__);
@ -440,13 +494,14 @@ class ConstBitOpTreeVisitor final : public AstNVisitor {
incrOps(nodep, __LINE__);
iterateChildren(nodep);
}
for (auto&& entry : m_bitPolarities) {
VarInfo& info = getVarInfo(entry.m_info);
if (info.hasConstantResult()) continue;
info.setPolarity(entry.m_polarity, entry.m_bit);
}
UASSERT_OBJ(isXorTree() || m_polarity, nodep, "must be the original polarity");
}
virtual ~ConstBitOpTreeVisitor() {
for (size_t i = 0; i < m_varInfos.size(); ++i) {
VL_DO_DANGLING(delete m_varInfos[i], m_varInfos[i]);
}
}
virtual ~ConstBitOpTreeVisitor() = default;
#undef CONST_BITOP_RETURN_IF
#undef CONST_BITOP_SET_FAILED
@ -466,7 +521,7 @@ public:
// Two ops for each varInfo. (And and Eq)
const int vars = visitor.m_varInfos.size() - 1;
int constTerms = 0;
for (const VarInfo* v : visitor.m_varInfos) {
for (auto&& v : visitor.m_varInfos) {
if (v && v->hasConstantResult()) ++constTerms;
}
// Expected number of ops after this simplification
@ -486,7 +541,7 @@ public:
AstNode* resultp = nullptr;
// VarInfo in visitor.m_varInfos appears in deterministic order,
// so the optimized AST is deterministic too.
for (const VarInfo* varinfop : visitor.m_varInfos) {
for (auto&& varinfop : visitor.m_varInfos) {
if (!varinfop) continue;
AstNode* partialresultp = varinfop->getResult();
resultp = visitor.combineTree(resultp, partialresultp);
@ -710,6 +765,86 @@ private:
VL_DO_DANGLING(nodep->deleteTree(), nodep);
return true;
}
bool matchMaskedOr(AstAnd* nodep) {
// Masking an OR with terms that have no bits set under the mask is replaced with masking
// only the remaining terms. Canonical example as generated by V3Expand is:
// 0xff & (a << 8 | b >> 24) --> 0xff & (b >> 24)
// Compute how many significant bits are in the mask
const AstConst* const constp = VN_CAST(nodep->lhsp(), Const);
const uint32_t significantBits = constp->num().widthMin();
AstOr* const orp = VN_CAST(nodep->rhsp(), Or);
// Predicate for checking whether the bottom 'significantBits' bits of the given expression
// are all zeroes.
const auto checkBottomClear = [=](const AstNode* nodep) -> bool {
if (const AstShiftL* const shiftp = VN_CAST_CONST(nodep, ShiftL)) {
if (const AstConst* const scp = VN_CAST_CONST(shiftp->rhsp(), Const)) {
return scp->num().toUInt() >= significantBits;
}
}
return false;
};
const bool orLIsRedundant = checkBottomClear(orp->lhsp());
const bool orRIsRedundant = checkBottomClear(orp->rhsp());
if (orLIsRedundant && orRIsRedundant) {
nodep->replaceWith(
new AstConst(nodep->fileline(), AstConst::DtypedValue(), nodep->dtypep(), 0));
VL_DO_DANGLING(nodep->deleteTree(), nodep);
return true;
} else if (orLIsRedundant) {
orp->replaceWith(orp->rhsp()->unlinkFrBack());
VL_DO_DANGLING(orp->deleteTree(), orp);
return false; // input node is still valid, keep going
} else if (orRIsRedundant) {
orp->replaceWith(orp->lhsp()->unlinkFrBack());
VL_DO_DANGLING(orp->deleteTree(), orp);
return false; // input node is still valid, keep going
} else {
return false;
}
}
bool matchMaskedShift(AstAnd* nodep) {
// Drop redundant masking of right shift result. E.g: 0xff & ((uint32_t)a >> 24). This
// commonly appears after V3Expand and the simplification in matchMaskedOr. Similarly,
// drop redundant masking of left shift result. E.g.: 0xff000000 & ((uint32_t)a << 24).
const auto checkMask = [=](const V3Number& mask) -> bool {
const AstConst* const constp = VN_CAST(nodep->lhsp(), Const);
if (constp->num().isCaseEq(mask)) {
AstNode* const rhsp = nodep->rhsp();
rhsp->unlinkFrBack();
nodep->replaceWith(rhsp);
rhsp->dtypeFrom(nodep);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
return true;
}
return false;
};
// Check if masking is redundant
if (AstShiftR* const shiftp = VN_CAST(nodep->rhsp(), ShiftR)) {
if (const AstConst* scp = VN_CAST_CONST(shiftp->rhsp(), Const)) {
// Check if mask is full over the non-zero bits
V3Number maskLo(nodep, nodep->width());
maskLo.setMask(nodep->width() - scp->num().toUInt());
return checkMask(maskLo);
}
} else if (AstShiftL* const shiftp = VN_CAST(nodep->rhsp(), ShiftL)) {
if (const AstConst* scp = VN_CAST_CONST(shiftp->rhsp(), Const)) {
// Check if mask is full over the non-zero bits
V3Number maskLo(nodep, nodep->width()), maskHi(nodep, nodep->width());
maskLo.setMask(nodep->width() - scp->num().toUInt());
maskHi.opShiftL(maskLo, scp->num());
return checkMask(maskHi);
}
}
return false;
}
bool matchBitOpTree(AstNode* nodep) {
if (!v3Global.opt.oConstBitOpTree()) return false;
@ -868,7 +1003,7 @@ private:
|| lp->num().isFourState() || lp->num().isNegative()) {
return false;
}
int newLsb = lp->toSInt() + bp->toSInt();
const int newLsb = lp->toSInt() + bp->toSInt();
if (newLsb + nodep->widthConst() > ap->width()) return false;
//
UINFO(9, "SEL(SHIFTR(a,b),l,w) -> SEL(a,l+b,w)\n");
@ -889,7 +1024,7 @@ private:
AstExtend* extendp = VN_CAST(nodep->rhsp(), Extend);
if (!extendp) return false;
AstNode* smallerp = extendp->lhsp();
int subsize = smallerp->width();
const int subsize = smallerp->width();
AstConst* constp = VN_CAST(nodep->lhsp(), Const);
if (!constp) return false;
if (!constp->num().isBitsZero(constp->width() - 1, subsize)) return false;
@ -914,7 +1049,7 @@ private:
const AstExtend* extendp = VN_CAST_CONST(nodep->rhsp(), Extend);
if (!extendp) return false;
AstNode* smallerp = extendp->lhsp();
int subsize = smallerp->width();
const int subsize = smallerp->width();
const AstConst* constp = VN_CAST_CONST(nodep->lhsp(), Const);
if (!constp) return false;
if (constp->num().isBitsZero(constp->width() - 1, subsize)) return false;
@ -945,10 +1080,10 @@ private:
}
// Find range of dtype we are selecting from
// Similar code in V3Unknown::AstSel
bool doit = true;
const bool doit = true;
if (m_warn && VN_IS(nodep->lsbp(), Const) && VN_IS(nodep->widthp(), Const) && doit) {
int maxDeclBit = nodep->declRange().hiMaxSelect() * nodep->declElWidth()
+ (nodep->declElWidth() - 1);
const int maxDeclBit = nodep->declRange().hiMaxSelect() * nodep->declElWidth()
+ (nodep->declElWidth() - 1);
if (VN_CAST(nodep->lsbp(), Const)->num().isFourState()
|| VN_CAST(nodep->widthp(), Const)->num().isFourState()) {
nodep->v3error("Selection index is constantly unknown or tristated: "
@ -1023,7 +1158,7 @@ private:
const AstConst* lwidth = VN_CAST_CONST(lhsp->widthp(), Const);
const AstConst* rwidth = VN_CAST_CONST(rhsp->widthp(), Const);
if (!lstart || !rstart || !lwidth || !rwidth) return false; // too complicated
int rend = (rstart->toSInt() + rwidth->toSInt());
const int rend = (rstart->toSInt() + rwidth->toSInt());
return (rend == lstart->toSInt());
}
bool ifMergeAdjacent(AstNode* lhsp, AstNode* rhsp) {
@ -1056,7 +1191,7 @@ private:
AstConst* lwidth = VN_CAST(lselp->widthp(), Const);
AstConst* rwidth = VN_CAST(rselp->widthp(), Const);
if (!lstart || !rstart || !lwidth || !rwidth) return false; // too complicated
int rend = (rstart->toSInt() + rwidth->toSInt());
const int rend = (rstart->toSInt() + rwidth->toSInt());
// a[i:j] a[j-1:k]
if (rend == lstart->toSInt()) return true;
// a[i:0] a[msb:j]
@ -1073,8 +1208,8 @@ private:
const AstNodeBiop* rp = VN_CAST_CONST(rhsp, NodeBiop);
if (!lp || !rp) return false;
// {a[]&b[], a[]&b[]}
bool lad = ifMergeAdjacent(lp->lhsp(), rp->lhsp());
bool rad = ifMergeAdjacent(lp->rhsp(), rp->rhsp());
const bool lad = ifMergeAdjacent(lp->lhsp(), rp->lhsp());
const bool rad = ifMergeAdjacent(lp->rhsp(), rp->rhsp());
if (lad && rad) return true;
// {a[] & b[]&c[], a[] & b[]&c[]}
if (lad && concatMergeable(lp->rhsp(), rp->rhsp())) return true;
@ -1315,10 +1450,10 @@ private:
// {a[1], a[0]} -> a[1:0]
AstSel* lselp = VN_CAST(nodep->lhsp()->unlinkFrBack(), Sel);
AstSel* rselp = VN_CAST(nodep->rhsp()->unlinkFrBack(), Sel);
int lstart = lselp->lsbConst();
int lwidth = lselp->widthConst();
int rstart = rselp->lsbConst();
int rwidth = rselp->widthConst();
const int lstart = lselp->lsbConst();
const int lwidth = lselp->widthConst();
const int rstart = rselp->lsbConst();
const int rwidth = rselp->widthConst();
UASSERT_OBJ((rstart + rwidth) == lstart, nodep,
"tried to merge two selects which are not adjacent");
@ -1379,7 +1514,7 @@ private:
}
void replaceMulShift(AstMul* nodep) { // Mul, but not MulS as not simple shift
UINFO(5, "MUL(2^n,b)->SHIFTL(b,n) " << nodep << endl);
int amount = VN_CAST(nodep->lhsp(), Const)->num().mostSetBitP1() - 1; // 2^n->n+1
const int amount = VN_CAST(nodep->lhsp(), Const)->num().mostSetBitP1() - 1; // 2^n->n+1
AstNode* opp = nodep->rhsp()->unlinkFrBack();
AstShiftL* newp
= new AstShiftL(nodep->fileline(), opp, new AstConst(nodep->fileline(), amount));
@ -1389,7 +1524,7 @@ private:
}
void replaceDivShift(AstDiv* nodep) { // Mul, but not MulS as not simple shift
UINFO(5, "DIV(b,2^n)->SHIFTR(b,n) " << nodep << endl);
int amount = VN_CAST(nodep->rhsp(), Const)->num().mostSetBitP1() - 1; // 2^n->n+1
const int amount = VN_CAST(nodep->rhsp(), Const)->num().mostSetBitP1() - 1; // 2^n->n+1
AstNode* opp = nodep->lhsp()->unlinkFrBack();
AstShiftR* newp
= new AstShiftR(nodep->fileline(), opp, new AstConst(nodep->fileline(), amount));
@ -1399,7 +1534,7 @@ private:
}
void replaceModAnd(AstModDiv* nodep) { // Mod, but not ModS as not simple shift
UINFO(5, "MOD(b,2^n)->AND(b,2^n-1) " << nodep << endl);
int amount = VN_CAST(nodep->rhsp(), Const)->num().mostSetBitP1() - 1; // 2^n->n+1
const int amount = VN_CAST(nodep->rhsp(), Const)->num().mostSetBitP1() - 1; // 2^n->n+1
V3Number mask(nodep, nodep->width());
mask.setMask(amount);
AstNode* opp = nodep->lhsp()->unlinkFrBack();
@ -1442,7 +1577,7 @@ private:
if (nodep->type() == lhsp->type()) {
int shift1 = VN_CAST(shift1p, Const)->toUInt();
int shift2 = VN_CAST(shift2p, Const)->toUInt();
int newshift = shift1 + shift2;
const int newshift = shift1 + shift2;
VL_DO_DANGLING(shift1p->deleteTree(), shift1p);
VL_DO_DANGLING(shift2p->deleteTree(), shift2p);
nodep->lhsp(ap);
@ -1454,7 +1589,7 @@ private:
if (VN_IS(lhsp, ShiftR)) shift1 = -shift1;
int shift2 = VN_CAST(shift2p, Const)->toUInt();
if (VN_IS(nodep, ShiftR)) shift2 = -shift2;
int newshift = shift1 + shift2;
const int newshift = shift1 + shift2;
VL_DO_DANGLING(shift1p->deleteTree(), shift1p);
VL_DO_DANGLING(shift2p->deleteTree(), shift2p);
AstNode* newp;
@ -1522,7 +1657,7 @@ private:
&& (con2p->toSInt() != con1p->toSInt() + sel1p->width())) {
return false;
}
bool lsbFirstAssign = (con1p->toUInt() < con2p->toUInt());
const bool lsbFirstAssign = (con1p->toUInt() < con2p->toUInt());
UINFO(4, "replaceAssignMultiSel " << nodep << endl);
UINFO(4, " && " << nextp << endl);
// nodep->dumpTree(cout, "comb1: ");
@ -1681,8 +1816,8 @@ private:
return true;
} else if (m_doV && VN_IS(nodep->lhsp(), StreamL)) {
// Push the stream operator to the rhs of the assignment statement
int dWidth = VN_CAST(nodep->lhsp(), StreamL)->lhsp()->width();
int sWidth = nodep->rhsp()->width();
const int dWidth = VN_CAST(nodep->lhsp(), StreamL)->lhsp()->width();
const int sWidth = nodep->rhsp()->width();
// Unlink the stuff
AstNode* dstp = VN_CAST(nodep->lhsp(), StreamL)->lhsp()->unlinkFrBack();
AstNode* streamp = VN_CAST(nodep->lhsp(), StreamL)->unlinkFrBack();
@ -1702,8 +1837,8 @@ private:
// The right stream operator on lhs of assignment statement does
// not reorder bits. However, if the rhs is wider than the lhs,
// then we select bits from the left-most, not the right-most.
int dWidth = VN_CAST(nodep->lhsp(), StreamR)->lhsp()->width();
int sWidth = nodep->rhsp()->width();
const int dWidth = VN_CAST(nodep->lhsp(), StreamR)->lhsp()->width();
const int sWidth = nodep->rhsp()->width();
// Unlink the stuff
AstNode* dstp = VN_CAST(nodep->lhsp(), StreamR)->lhsp()->unlinkFrBack();
AstNode* sizep = VN_CAST(nodep->lhsp(), StreamR)->rhsp()->unlinkFrBack();
@ -2532,7 +2667,7 @@ private:
string fmt;
bool inPct = false;
AstNode* argp = nodep->exprsp();
string text = nodep->text();
const string text = nodep->text();
for (const char ch : text) {
if (!inPct && ch == '%') {
inPct = true;
@ -2554,7 +2689,8 @@ private:
if (argp) {
AstNode* nextp = argp->nextp();
if (VN_IS(argp, Const)) { // Convert it
string out = VN_CAST(argp, Const)->num().displayed(nodep, fmt);
const string out
= VN_CAST(argp, Const)->num().displayed(nodep, fmt);
UINFO(9, " DispConst: " << fmt << " -> " << out << " for "
<< argp << endl);
// fmt = out w/ replace % with %% as it must be literal.
@ -2592,10 +2728,10 @@ private:
iterateChildren(nodep);
}
virtual void visit(AstWhile* nodep) override {
bool oldHasJumpDelay = m_hasJumpDelay;
const bool oldHasJumpDelay = m_hasJumpDelay;
m_hasJumpDelay = false;
{ iterateChildren(nodep); }
bool thisWhileHasJumpDelay = m_hasJumpDelay;
const bool thisWhileHasJumpDelay = m_hasJumpDelay;
m_hasJumpDelay = thisWhileHasJumpDelay || oldHasJumpDelay;
if (m_doNConst) {
if (nodep->condp()->isZero()) {
@ -2964,9 +3100,11 @@ private:
TREEOPV("AstConcat{$lhsp.castSel, $rhsp.castSel, ifAdjacentSel(VN_CAST($lhsp,,Sel),,VN_CAST($rhsp,,Sel))}", "replaceConcatSel(nodep)");
TREEOPV("AstConcat{ifConcatMergeableBiop($lhsp), concatMergeable($lhsp,,$rhsp)}", "replaceConcatMerge(nodep)");
// Common two-level operations that can be simplified
TREEOP ("AstAnd {$lhsp.castConst,matchAndCond(nodep)}", "DONE");
TREEOP ("AstAnd {$lhsp.castOr, $rhsp.castOr, operandAndOrSame(nodep)}", "replaceAndOr(nodep)");
TREEOP ("AstOr {$lhsp.castAnd,$rhsp.castAnd,operandAndOrSame(nodep)}", "replaceAndOr(nodep)");
TREEOP ("AstAnd {$lhsp.castConst,matchAndCond(nodep)}", "DONE");
TREEOP ("AstAnd {$lhsp.castConst, $rhsp.castOr, matchMaskedOr(nodep)}", "DONE");
TREEOPC("AstAnd {$lhsp.castConst, matchMaskedShift(nodep)}", "DONE");
TREEOP ("AstAnd {$lhsp.castOr, $rhsp.castOr, operandAndOrSame(nodep)}", "replaceAndOr(nodep)");
TREEOP ("AstOr {$lhsp.castAnd,$rhsp.castAnd,operandAndOrSame(nodep)}", "replaceAndOr(nodep)");
TREEOP ("AstOr {matchOrAndNot(nodep)}", "DONE");
TREEOP ("AstAnd {operandShiftSame(nodep)}", "replaceShiftSame(nodep)");
TREEOP ("AstOr {operandShiftSame(nodep)}", "replaceShiftSame(nodep)");

View File

@ -92,7 +92,7 @@ private:
// See also similar rule in V3TraceDecl::varIgnoreTrace
if (!nodep->isToggleCoverable()) return "Not relevant signal type";
if (!v3Global.opt.coverageUnderscore()) {
string prettyName = nodep->prettyName();
const string prettyName = nodep->prettyName();
if (prettyName[0] == '_') return "Leading underscore";
if (prettyName.find("._") != string::npos) return "Inlined leading underscore";
}
@ -114,7 +114,7 @@ private:
// Note the module name could have parameters appended, we'll consider this
// a feature as it allows for each parameterized block to be counted separately.
// Someday the user might be allowed to specify a different page suffix
string page = page_prefix + "/" + m_modp->prettyName();
const string page = page_prefix + "/" + m_modp->prettyName();
AstCoverDecl* declp = new AstCoverDecl(fl, page, comment, linescov, offset);
declp->hier(hier);
@ -144,7 +144,7 @@ private:
if (it == m_varnames.end()) {
m_varnames.emplace(name, 1);
} else {
int suffix = (it->second)++;
const int suffix = (it->second)++;
name += "_" + cvtToStr(suffix);
}
return name;
@ -278,7 +278,7 @@ private:
// We'll do this, and make the if(...) coverinc later.
// Add signal to hold the old value
string newvarname = string("__Vtogcov__") + nodep->shortName();
const string newvarname = string("__Vtogcov__") + nodep->shortName();
AstVar* chgVarp
= new AstVar(nodep->fileline(), AstVarType::MODULETEMP, newvarname, nodep);
chgVarp->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true);
@ -312,7 +312,7 @@ private:
if (bdtypep->isRanged()) {
for (int index_docs = bdtypep->lo(); index_docs < bdtypep->hi() + 1;
++index_docs) {
int index_code = index_docs - bdtypep->lo();
const int index_code = index_docs - bdtypep->lo();
ToggleEnt newent(above.m_comment + string("[") + cvtToStr(index_docs) + "]",
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
index_code, 1),
@ -326,7 +326,7 @@ private:
}
} else if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
for (int index_docs = adtypep->lo(); index_docs <= adtypep->hi(); ++index_docs) {
int index_code = index_docs - adtypep->lo();
const int index_code = index_docs - adtypep->lo();
ToggleEnt newent(above.m_comment + string("[") + cvtToStr(index_docs) + "]",
new AstArraySel(varp->fileline(),
above.m_varRefp->cloneTree(true), index_code),
@ -339,7 +339,7 @@ private:
} else if (AstPackArrayDType* adtypep = VN_CAST(dtypep, PackArrayDType)) {
for (int index_docs = adtypep->lo(); index_docs <= adtypep->hi(); ++index_docs) {
AstNodeDType* subtypep = adtypep->subDTypep()->skipRefp();
int index_code = index_docs - adtypep->lo();
const int index_code = index_docs - adtypep->lo();
ToggleEnt newent(above.m_comment + string("[") + cvtToStr(index_docs) + "]",
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
index_code * subtypep->width(), subtypep->width()),
@ -354,7 +354,7 @@ private:
for (AstMemberDType* itemp = adtypep->membersp(); itemp;
itemp = VN_CAST(itemp->nextp(), MemberDType)) {
AstNodeDType* subtypep = itemp->subDTypep()->skipRefp();
int index_code = itemp->lsb();
const int index_code = itemp->lsb();
ToggleEnt newent(above.m_comment + string(".") + itemp->name(),
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
index_code, subtypep->width()),
@ -385,17 +385,18 @@ private:
UINFO(4, " IF: " << nodep << endl);
if (m_state.m_on) {
// An else-if. When we iterate the if, use "elsif" marking
bool elsif = nodep->ifsp() && VN_IS(nodep->elsesp(), If) && !nodep->elsesp()->nextp();
const bool elsif
= nodep->ifsp() && VN_IS(nodep->elsesp(), If) && !nodep->elsesp()->nextp();
if (elsif) VN_CAST(nodep->elsesp(), If)->user1(true);
bool first_elsif = !nodep->user1() && elsif;
bool cont_elsif = nodep->user1() && elsif;
bool final_elsif = nodep->user1() && !elsif && nodep->elsesp();
const bool first_elsif = !nodep->user1() && elsif;
const bool cont_elsif = nodep->user1() && elsif;
const bool final_elsif = nodep->user1() && !elsif && nodep->elsesp();
//
// Considered: If conditional is on a different line from if/else then we
// can show it as part of line coverage of the statement
// above. Otherwise show it based on what is inside.
// But: Seemed too complicated, and fragile.
CheckState lastState = m_state;
const CheckState lastState = m_state;
CheckState ifState;
CheckState elseState;
{

View File

@ -110,7 +110,7 @@ private:
if (!lastrefp) {
vscp->user5p(nodep);
} else {
bool last_was_blocking = lastrefp->user5();
const bool last_was_blocking = lastrefp->user5();
if (last_was_blocking != blocking) {
AstNode* nonblockingp = blocking ? nodep : lastrefp;
AstNode* blockingp = blocking ? lastrefp : nodep;
@ -228,7 +228,7 @@ private:
UASSERT_OBJ(varrefp->varScopep(), varrefp, "Var didn't get varscoped in V3Scope.cpp");
varrefp->unlinkFrBack();
AstVar* oldvarp = varrefp->varp();
int modVecNum = m_scopeVecMap[varrefp->varScopep()]++;
const int modVecNum = m_scopeVecMap[varrefp->varScopep()]++;
//
std::deque<AstNode*> dimreadps; // Read value for each dimension of assignment
for (unsigned dimension = 0; dimension < dimvalp.size(); dimension++) {
@ -236,8 +236,8 @@ private:
if (VN_IS(dimp, Const)) { // bit = const, can just use it
dimreadps.push_front(dimp);
} else {
string bitvarname = (string("__Vdlyvdim") + cvtToStr(dimension) + "__"
+ oldvarp->shortName() + "__v" + cvtToStr(modVecNum));
const string bitvarname = (string("__Vdlyvdim") + cvtToStr(dimension) + "__"
+ oldvarp->shortName() + "__v" + cvtToStr(modVecNum));
AstVarScope* bitvscp
= createVarSc(varrefp->varScopep(), bitvarname, dimp->width(), nullptr);
AstAssign* bitassignp = new AstAssign(
@ -256,8 +256,8 @@ private:
// vlsb = constant, can just push constant into where we use it
bitreadp = lsbvaluep;
} else {
string bitvarname = (string("__Vdlyvlsb__") + oldvarp->shortName() + "__v"
+ cvtToStr(modVecNum));
const string bitvarname = (string("__Vdlyvlsb__") + oldvarp->shortName() + "__v"
+ cvtToStr(modVecNum));
AstVarScope* bitvscp
= createVarSc(varrefp->varScopep(), bitvarname, lsbvaluep->width(), nullptr);
AstAssign* bitassignp = new AstAssign(
@ -440,7 +440,7 @@ private:
checkActivePost(nodep, oldactivep);
}
if (!dlyvscp) { // First use of this delayed variable
string newvarname = (string("__Vdly__") + nodep->varp()->shortName());
const string newvarname = (string("__Vdly__") + nodep->varp()->shortName());
dlyvscp = createVarSc(oldvscp, newvarname, 0, nullptr);
AstNodeAssign* prep;
AstBasicDType* basicp = oldvscp->dtypep()->basicp();

View File

@ -52,7 +52,7 @@ private:
UINFO(6, " Deep " << nodep << endl);
// if (debug() >= 9) nodep->dumpTree(cout, "deep:");
string newvarname = (string("__Vdeeptemp") + cvtToStr(m_modp->varNumGetInc()));
const string newvarname = (string("__Vdeeptemp") + cvtToStr(m_modp->varNumGetInc()));
AstVar* varp
= new AstVar(nodep->fileline(), AstVarType::STMTTEMP, newvarname, nodep->dtypep());
UASSERT_OBJ(m_cfuncp, nodep, "Deep expression not under a function");
@ -128,7 +128,7 @@ private:
// (Here instead of new visitor after V3Descope just to avoid another visitor)
void needNonStaticFunc(AstNode* nodep) {
UASSERT_OBJ(m_cfuncp, nodep, "Non-static accessor not under a function");
if (m_cfuncp->isStatic().trueUnknown()) {
if (m_cfuncp->isStatic()) {
UINFO(5, "Mark non-public due to " << nodep << endl);
m_cfuncp->isStatic(false);
}

View File

@ -48,20 +48,24 @@ private:
AstCFunc* createDeepFunc(AstNode* nodep) {
AstNRelinker relinkHandle;
nodep->unlinkFrBack(&relinkHandle);
// Create function
string name = m_cfuncp->name() + "__deep" + cvtToStr(++m_deepNum);
AstCFunc* funcp = new AstCFunc(nodep->fileline(), name, nullptr);
funcp->argTypes(EmitCBaseVisitor::symClassVar());
funcp->symProlog(true);
// Create sub function
AstScope* const scopep = m_cfuncp->scopep();
const string name = m_cfuncp->name() + "__deep" + cvtToStr(++m_deepNum);
AstCFunc* const funcp = new AstCFunc(nodep->fileline(), name, scopep);
funcp->slow(m_cfuncp->slow());
funcp->isStatic(m_cfuncp->isStatic());
funcp->isLoose(m_cfuncp->isLoose());
funcp->addStmtsp(nodep);
m_modp->addStmtp(funcp);
// Call it at the point where the body was removed from
AstCCall* callp = new AstCCall(nodep->fileline(), funcp);
callp->argTypes("vlSymsp");
scopep->addActivep(funcp);
// Call sub function at the point where the body was removed from
AstCCall* const callp = new AstCCall(nodep->fileline(), funcp);
if (VN_IS(m_modp, Class)) {
funcp->argTypes(EmitCBaseVisitor::symClassVar());
callp->argTypes("vlSymsp");
}
UINFO(6, " New " << callp << endl);
//
relinkHandle.relink(callp);
// Done
return funcp;
}

View File

@ -46,17 +46,17 @@ private:
// STATE
AstNodeModule* m_modp = nullptr; // Current module
AstScope* m_scopep = nullptr; // Current scope
bool m_modSingleton = false; // m_modp is only instanced once
bool m_allowThis = false; // Allow function non-static
bool m_needThis = false; // Make function non-static
const AstScope* m_scopep = nullptr; // Current scope
const AstCFunc* m_funcp = nullptr; // Current function
bool m_modSingleton = false; // m_modp is only instantiated once
FuncMmap m_modFuncs; // Name of public functions added
// METHODS
VL_DEBUG_FUNC; // Declare debug()
static bool modIsSingleton(AstNodeModule* modp) {
// True iff there's exactly one instance of this module in the design.
// True iff there's exactly one instance of this module in the design (including top).
if (modp->isTop()) return true;
int instances = 0;
for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (VN_IS(stmtp, Scope)) {
@ -66,82 +66,39 @@ private:
return (instances == 1);
}
// Construct the best prefix to reference an object in 'scopep'
// from a CFunc in 'm_scopep'. Result may be relative
// ("this->[...]") or absolute ("vlTOPp->[...]").
// Construct the best self pointer to reference an object in 'scopep' from a CFunc in
// 'm_scopep'. Result may be relative ("this->[...]") or absolute ("vlSyms->[...]").
//
// Using relative references allows V3Combine'ing
// code across multiple instances of the same module.
//
// Sets 'hierThisr' true if the object is local to this scope
// (and could be made into a function-local later in V3Localize),
// false if the object is in another scope.
string descopedName(bool& hierThisr, string& hierUnprot, const AstScope* scopep,
const AstVar* varp) {
// Using relative references allows V3Combine'ing code across multiple instances of the same
// module.
string descopedSelfPointer(const AstScope* scopep) {
UASSERT(scopep, "Var/Func not scoped");
hierThisr = (scopep == m_scopep);
// It's possible to disable relative references. This is a concession
// to older compilers (gcc < 4.5.x) that don't understand __restrict__
// well and emit extra ld/st to guard against pointer aliasing
// when this-> and vlTOPp-> are mixed in the same function.
//
// "vlTOPp" is declared "restrict" so better compilers understand
// that it won't alias with "this".
bool relativeRefOk = v3Global.opt.relativeCFuncs();
//
// Static functions can't use this
if (!m_allowThis) relativeRefOk = false;
//
// Use absolute refs in top-scoped routines, keep them static.
// The DPI callback registration depends on representing top-level
// static routines as plain function pointers. That breaks if those
// become true OO routines.
//
// V3Combine wouldn't likely be able to combine top-level
// routines anyway, so there's no harm in keeping these static.
UASSERT_OBJ(m_modp, scopep, "Scope not under module");
if (m_modp->isTop()) relativeRefOk = false;
//
// Use absolute refs if this scope is the only instance of the module.
// Saves a bit of overhead on passing the 'this' pointer, and there's no
// need to be nice to V3Combine when we have only a single instance.
// The risk that this prevents combining identical logic from differently-
// named but identical modules seems low.
if (m_modSingleton) relativeRefOk = false;
//
// Class methods need relative
if (m_modp && VN_IS(m_modp, Class)) relativeRefOk = true;
// Static functions can't use relative references via 'this->'
const bool relativeRefOk = !m_funcp->isStatic();
if (varp && varp->isFuncLocal()) {
hierThisr = true;
return ""; // Relative to function, not in this
} else if (relativeRefOk && scopep == m_scopep) {
m_needThis = true;
return "this->";
UINFO(8, " Descope ref under " << m_scopep << endl);
UINFO(8, " ref to " << scopep << endl);
UINFO(8, " aboveScope " << scopep->aboveScopep() << endl);
if (relativeRefOk && scopep == m_scopep) {
return "this";
} else if (VN_IS(scopep->modp(), Class)) {
hierUnprot = v3Global.opt.modPrefix() + "_"; // Prefix before protected part
return scopep->modp()->name() + "::";
} else if (relativeRefOk && scopep->aboveScopep() && scopep->aboveScopep() == m_scopep) {
// Reference to scope of instance directly under this module, can just "cell->"
return "this";
} else if (!m_modSingleton && relativeRefOk && scopep->aboveScopep() == m_scopep
&& VN_IS(scopep->modp(), Module)) {
// Reference to scope of instance directly under this module, can just "this->cell",
// which can potentially be V3Combined, but note this requires one extra pointer
// dereference which is slower, so we only use it if the source scope is not a
// singleton.
string name = scopep->name();
string::size_type pos;
if ((pos = name.rfind('.')) != string::npos) name.erase(0, pos + 1);
m_needThis = true;
return name + "->";
return "this->" + name;
} else {
// Reference to something elsewhere, or relative references
// are disabled. Use global variable
UINFO(8, " Descope " << scopep << endl);
UINFO(8, " to " << scopep->name() << endl);
UINFO(8, " under " << m_scopep->name() << endl);
if (!scopep->aboveScopep()) { // Top
// We could also return "vlSymsp->TOPp->" here, but GCC would
// suspect aliases.
return "vlTOPp->";
} else {
return scopep->nameVlSym() + ".";
}
// Reference to something elsewhere, or relative references are disabled. Use global
// variable
return "(&" + scopep->nameVlSym() + ")";
}
}
@ -150,7 +107,7 @@ private:
// If for any given name only one function exists, we can use that function directly.
// If multiple functions exist, we need to select the appropriate scope.
for (FuncMmap::iterator it = m_modFuncs.begin(); it != m_modFuncs.end(); ++it) {
string name = it->first;
const string name = it->first;
AstCFunc* topFuncp = it->second;
auto nextIt1 = it;
++nextIt1;
@ -164,11 +121,6 @@ private:
if (newfuncp->finalsp()) newfuncp->finalsp()->unlinkFrBackWithNext()->deleteTree();
newfuncp->name(name);
newfuncp->isStatic(false);
newfuncp->addInitsp(
new AstCStmt(newfuncp->fileline(),
EmitCBaseVisitor::symClassVar() + " = this->__VlSymsp;\n"));
newfuncp->addInitsp(
new AstCStmt(newfuncp->fileline(), EmitCBaseVisitor::symTopAssign() + "\n"));
topFuncp->addNextHere(newfuncp);
// In the body, call each function if it matches the given scope
for (FuncMmap::iterator eachIt = it;
@ -177,7 +129,8 @@ private:
AstCFunc* funcp = eachIt->second;
auto nextIt2 = eachIt;
++nextIt2;
bool moreOfSame = (nextIt2 != m_modFuncs.end() && nextIt2->first == name);
const bool moreOfSame
= (nextIt2 != m_modFuncs.end() && nextIt2->first == name);
UASSERT_OBJ(funcp->scopep(), funcp, "Not scoped");
UINFO(6, " Wrapping " << name << " " << funcp << endl);
@ -252,16 +205,17 @@ private:
}
virtual void visit(AstNodeVarRef* nodep) override {
iterateChildren(nodep);
if (!nodep->varScopep()) {
UASSERT_OBJ(nodep->varp()->isFuncLocal(), nodep,
"unscoped reference can only appear to function locals at this point");
return;
}
// Convert the hierch name
UINFO(9, " ref-in " << nodep << endl);
UASSERT_OBJ(m_scopep, nodep, "Node not under scope");
bool hierThis;
string hierUnprot;
nodep->hiernameToProt(descopedName(hierThis /*ref*/, hierUnprot /*ref*/,
nodep->varScopep()->scopep(),
nodep->varScopep()->varp()));
nodep->hiernameToUnprot(hierUnprot);
nodep->hierThis(hierThis);
const AstVar* const varp = nodep->varScopep()->varp();
const AstScope* const scopep = nodep->varScopep()->scopep();
if (!varp->isFuncLocal()) { nodep->selfPointer(descopedSelfPointer(scopep)); }
nodep->varScopep(nullptr);
UINFO(9, " refout " << nodep << endl);
}
@ -270,24 +224,20 @@ private:
iterateChildren(nodep);
// Convert the hierch name
UASSERT_OBJ(m_scopep, nodep, "Node not under scope");
UASSERT_OBJ(nodep->funcp()->scopep(), nodep, "CFunc not under scope");
bool hierThis;
string hierUnprot;
nodep->hiernameToProt(
descopedName(hierThis /*ref*/, hierUnprot /*ref*/, nodep->funcp()->scopep(), nullptr));
nodep->hiernameToUnprot(hierUnprot);
const AstScope* const scopep = nodep->funcp()->scopep();
nodep->selfPointer(descopedSelfPointer(scopep));
// Can't do this, as we may have more calls later
// nodep->funcp()->scopep(nullptr);
}
virtual void visit(AstCFunc* nodep) override {
VL_RESTORER(m_needThis);
VL_RESTORER(m_allowThis);
VL_RESTORER(m_funcp);
if (!nodep->user1()) {
m_needThis = false;
m_allowThis = nodep->isStatic().falseUnknown(); // Non-static or unknown if static
// Static functions should have been moved under the corresponding AstClassPackage
UASSERT(!(nodep->isStatic() && VN_IS(m_modp, Class)),
"Static function under AstClass");
m_funcp = nodep;
iterateChildren(nodep);
nodep->user1(true);
if (m_needThis) nodep->isStatic(false);
// If it's under a scope, move it up to the top
if (m_scopep) {
nodep->unlinkFrBack();
@ -316,7 +266,6 @@ public:
void V3Descope::descopeAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
v3Global.assertScoped(false);
{ DescopeVisitor visitor(nodep); } // Destruct before checking
V3Global::dumpCheckGlobalTree("descope", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,9 @@
class V3EmitC final {
public:
static void emitc();
static void emitcConstPool();
static void emitcInlines();
static void emitcModel();
static void emitcSyms(bool dpiHdrOnly = false);
static void emitcTrace();
static void emitcFiles();

223
src/V3EmitCBase.cpp Normal file
View File

@ -0,0 +1,223 @@
// -*- 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 "V3EmitCBase.h"
#include "V3Task.h"
//######################################################################
// EmitCBaseVisitor implementation
string EmitCBaseVisitor::funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp) {
modp = modp ? modp : VN_CAST(nodep->user4p(), NodeModule);
string name;
if (nodep->isConstructor()) {
name += prefixNameProtect(modp);
} else if (nodep->isDestructor()) {
name += "~";
name += prefixNameProtect(modp);
} else {
if (nodep->isLoose()) {
name += prefixNameProtect(modp);
name += "__";
}
name += nodep->nameProtect();
}
return name;
}
AstCFile* EmitCBaseVisitor::newCFile(const string& filename, bool slow, bool source) {
AstCFile* cfilep = new AstCFile(v3Global.rootp()->fileline(), filename);
cfilep->slow(slow);
cfilep->source(source);
v3Global.rootp()->addFilesp(cfilep);
return cfilep;
}
string EmitCBaseVisitor::cFuncArgs(const AstCFunc* nodep) {
// Return argument list for given C function
string args;
if (nodep->isLoose() && !nodep->isStatic()) {
if (nodep->isConst().trueKnown()) args += "const ";
AstNodeModule* modp = VN_CAST(nodep->user4p(), NodeModule);
args += prefixNameProtect(modp);
args += "* vlSelf";
}
if (!nodep->argTypes().empty()) {
if (!args.empty()) args += ", ";
args += nodep->argTypes();
}
// Might be a user function with argument list.
for (const AstNode* stmtp = nodep->argsp(); stmtp; stmtp = stmtp->nextp()) {
if (const AstVar* portp = VN_CAST_CONST(stmtp, Var)) {
if (portp->isIO() && !portp->isFuncReturn()) {
if (args != "") args += ", ";
if (nodep->dpiImportPrototype() || nodep->dpiExportDispatcher()) {
args += portp->dpiArgType(true, false);
} else if (nodep->funcPublic()) {
args += portp->cPubArgType(true, false);
} else {
args += portp->vlArgType(true, false, true);
}
}
}
}
return args;
}
void EmitCBaseVisitor::emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp,
bool withScope) {
if (!funcp->isConstructor() && !funcp->isDestructor()) {
puts(funcp->rtnTypeVoid());
puts(" ");
}
if (withScope) {
if (funcp->dpiExportDispatcher()) {
puts(topClassName() + "::");
} else if (funcp->isProperMethod()) {
puts(prefixNameProtect(modp) + "::");
}
}
puts(funcNameProtect(funcp, modp));
puts("(" + cFuncArgs(funcp) + ")");
if (funcp->isConst().trueKnown() && funcp->isProperMethod()) puts(" const");
}
void EmitCBaseVisitor::emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp,
bool cLinkage) {
ensureNewLine();
if (!funcp->ifdef().empty()) puts("#ifdef " + funcp->ifdef() + "\n");
if (cLinkage) puts("extern \"C\" ");
if (funcp->isStatic() && funcp->isProperMethod()) puts("static ");
if (funcp->isVirtual()) {
UASSERT_OBJ(funcp->isProperMethod(), funcp, "Virtual function is not a proper method");
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) {
const AstBasicDType* const basicp = nodep->basicp();
bool refNeedParens = VN_IS(nodep->dtypeSkipRefp(), UnpackArrayDType);
const auto emitDeclArrayBrackets = [this](const AstVar* nodep) -> void {
// This isn't very robust and may need cleanup for other data types
for (const AstUnpackArrayDType* arrayp
= VN_CAST_CONST(nodep->dtypeSkipRefp(), UnpackArrayDType);
arrayp; arrayp = VN_CAST_CONST(arrayp->subDTypep()->skipRefp(), UnpackArrayDType)) {
puts("[" + cvtToStr(arrayp->elementsConst()) + "]");
}
};
if (nodep->isIO() && nodep->isSc()) {
UASSERT_OBJ(basicp, nodep, "Unimplemented: Outputting this data type");
if (nodep->attrScClocked() && nodep->isReadOnly()) {
puts("sc_in_clk ");
} else {
if (nodep->isInoutish()) {
puts("sc_inout<");
} else if (nodep->isWritable()) {
puts("sc_out<");
} else if (nodep->isNonOutput()) {
puts("sc_in<");
} else {
nodep->v3fatalSrc("Unknown type");
}
puts(nodep->scType());
puts("> ");
}
if (asRef) {
if (refNeedParens) puts("(");
puts("&");
}
puts(nodep->nameProtect());
if (asRef && refNeedParens) { puts(")"); }
emitDeclArrayBrackets(nodep);
puts(";\n");
} else if (nodep->isIO() && basicp && !basicp->isOpaque()) {
if (nodep->isInoutish()) {
puts("VL_INOUT");
} else if (nodep->isWritable()) {
puts("VL_OUT");
} else if (nodep->isNonOutput()) {
puts("VL_IN");
} else {
nodep->v3fatalSrc("Unknown type");
}
if (nodep->isQuad()) {
puts("64");
} else if (nodep->widthMin() <= 8) {
puts("8");
} else if (nodep->widthMin() <= 16) {
puts("16");
} else if (nodep->isWide()) {
puts("W");
refNeedParens = true;
}
puts("(");
if (asRef) {
if (refNeedParens) puts("(");
puts("&");
}
puts(nodep->nameProtect());
if (asRef && refNeedParens) { puts(")"); }
emitDeclArrayBrackets(nodep);
// If it's a packed struct/array then nodep->width is the whole
// thing, msb/lsb is just lowest dimension
puts("," + cvtToStr(basicp->lo() + nodep->width() - 1) + "," + cvtToStr(basicp->lo()));
if (nodep->isWide()) puts("," + cvtToStr(nodep->widthWords()));
puts(");\n");
} else {
// strings and other fundamental c types
if (nodep->isFuncLocal() && nodep->isString()) {
const string name = nodep->name();
const string suffix = V3Task::dpiTemporaryVarSuffix();
// string temporary variable for DPI-C needs to be static because c_str() will be
// passed to C code and the lifetime of the variable must be long enough. See also
// Issue 2622.
const bool beStatic = name.size() >= suffix.size()
&& name.substr(name.size() - suffix.size()) == suffix;
if (beStatic) puts("static VL_THREAD_LOCAL ");
}
puts(nodep->vlArgType(true, false, false, prefixIfImp, asRef));
puts(";\n");
}
}
void EmitCBaseVisitor::emitModCUse(AstNodeModule* modp, VUseType useType) {
string nl;
for (AstNode* itemp = modp->stmtsp(); itemp; itemp = itemp->nextp()) {
if (AstCUse* usep = VN_CAST(itemp, CUse)) {
if (usep->useType() == useType) {
if (usep->useType().isInclude()) {
puts("#include \"" + prefixNameProtect(usep) + ".h\"\n");
}
if (usep->useType().isFwdClass()) {
puts("class " + prefixNameProtect(usep) + ";\n");
}
nl = "\n";
}
}
}
puts(nl);
}

View File

@ -43,6 +43,7 @@ public:
if (v3Global.opt.decoration()) puts(str);
}
void putsQuoted(const string& str) { ofp()->putsQuoted(str); }
void ensureNewLine() { ofp()->ensureNewLine(); }
bool optSystemC() { return v3Global.opt.systemC(); }
static string protect(const string& name) { return VIdProtect::protectIf(name, true); }
static string protectIf(const string& name, bool doIt) {
@ -52,59 +53,35 @@ public:
return VIdProtect::protectWordsIf(name, doIt);
}
static string ifNoProtect(const string& in) { return v3Global.opt.protectIds() ? "" : in; }
static string voidSelfAssign(const AstNodeModule* modp) {
const string className = prefixNameProtect(modp);
return className + "* const __restrict vlSelf VL_ATTR_UNUSED = static_cast<" + className
+ "*>(voidSelf);\n";
}
static string symClassName() { return v3Global.opt.prefix() + "_" + protect("_Syms"); }
static string symClassVar() { return symClassName() + "* __restrict vlSymsp"; }
static string symTopAssign() {
return v3Global.opt.prefix() + "* const __restrict vlTOPp VL_ATTR_UNUSED = vlSymsp->TOPp;";
}
static string funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp) {
if (nodep->isConstructor()) {
return prefixNameProtect(modp);
} else if (nodep->isDestructor()) {
return string("~") + prefixNameProtect(modp);
} else {
return nodep->nameProtect();
}
static string symClassAssign() {
return symClassName() + "* const __restrict vlSymsp VL_ATTR_UNUSED = vlSelf->vlSymsp;\n";
}
static string funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp = nullptr);
static string prefixNameProtect(const AstNode* nodep) { // C++ name with prefix
const AstNodeModule* modp = VN_CAST_CONST(nodep, NodeModule);
if (modp && modp->isTop()) {
return v3Global.opt.prefix();
} else {
return v3Global.opt.modPrefix() + "_" + protect(nodep->name());
}
return v3Global.opt.modPrefix() + "_" + protect(nodep->name());
}
static string topClassName() { // Return name of top wrapper module
return v3Global.opt.prefix();
}
static AstCFile* newCFile(const string& filename, bool slow, bool source) {
AstCFile* cfilep = new AstCFile(v3Global.rootp()->fileline(), filename);
cfilep->slow(slow);
cfilep->source(source);
v3Global.rootp()->addFilesp(cfilep);
return cfilep;
}
string cFuncArgs(const AstCFunc* nodep) {
// Return argument list for given C function
string args = nodep->argTypes();
// Might be a user function with argument list.
for (const AstNode* stmtp = nodep->argsp(); stmtp; stmtp = stmtp->nextp()) {
if (const AstVar* portp = VN_CAST_CONST(stmtp, Var)) {
if (portp->isIO() && !portp->isFuncReturn()) {
if (args != "") args += ", ";
if (nodep->dpiImport() || nodep->dpiExportWrapper()) {
args += portp->dpiArgType(true, false);
} else if (nodep->funcPublic()) {
args += portp->cPubArgType(true, false);
} else {
args += portp->vlArgType(true, false, true);
}
}
}
}
return args;
static bool isConstPoolMod(AstNode* modp) {
return modp == v3Global.rootp()->constPoolp()->modp();
}
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);
// CONSTRUCTORS
EmitCBaseVisitor() = default;
virtual ~EmitCBaseVisitor() override = default;

192
src/V3EmitCConstPool.cpp Normal file
View File

@ -0,0 +1,192 @@
// -*- 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 "V3EmitC.h"
#include "V3EmitCBase.h"
#include "V3File.h"
#include "V3Global.h"
#include "V3String.h"
#include "V3Stats.h"
#include <algorithm>
#include <cinttypes>
//######################################################################
// Const pool emitter
class EmitCConstPool final : EmitCBaseVisitor {
// MEMBERS
bool m_inUnpacked = false;
uint32_t m_unpackedWord = 0;
uint32_t m_outFileCount = 0;
int m_outFileSize = 0;
VDouble0 m_tablesEmitted;
VDouble0 m_constsEmitted;
// METHODS
VL_DEBUG_FUNC; // Declare debug()
V3OutCFile* newOutCFile() const {
const string fileName = v3Global.opt.makeDir() + "/" + topClassName() + "__ConstPool_"
+ cvtToStr(m_outFileCount) + ".cpp";
newCFile(fileName, /* slow: */ true, /* source: */ true);
V3OutCFile* const ofp = new V3OutCFile(fileName);
ofp->putsHeader();
ofp->puts("// DESCRIPTION: Verilator output: Constant pool\n");
ofp->puts("//\n");
ofp->puts("\n");
ofp->puts("#include \"verilated_heavy.h\"\n");
return ofp;
}
void maybeSplitCFile() {
if (v3Global.opt.outputSplit() && m_outFileSize < v3Global.opt.outputSplit()) return;
// Splitting file, so using parallel build.
v3Global.useParallelBuild(true);
// Close current file
VL_DO_DANGLING(delete m_ofp, m_ofp);
// Open next file
m_outFileSize = 0;
++m_outFileCount;
m_ofp = newOutCFile();
}
void emitVars(const AstConstPool* poolp) {
std::vector<const AstVar*> varps;
for (AstNode* nodep = poolp->modp()->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { varps.push_back(varp); }
}
if (varps.empty()) return; // Constant pool is empty, so we are done
stable_sort(varps.begin(), varps.end(), [](const AstVar* ap, const AstVar* bp) { //
return ap->name() < bp->name();
});
m_ofp = newOutCFile();
for (const AstVar* varp : varps) {
maybeSplitCFile();
const string nameProtect = topClassName() + "__ConstPool__" + varp->nameProtect();
puts("\n");
puts("extern const ");
puts(varp->dtypep()->cType(nameProtect, false, false));
puts(" = ");
iterate(varp->valuep());
puts(";\n");
// Keep track of stats
if (VN_IS(varp->dtypep(), UnpackArrayDType)) {
++m_tablesEmitted;
} else {
++m_constsEmitted;
}
}
VL_DO_DANGLING(delete m_ofp, m_ofp);
}
// 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());
}
}
public:
explicit EmitCConstPool(AstConstPool* poolp) {
emitVars(poolp);
V3Stats::addStatSum("ConstPool, Tables emitted", m_tablesEmitted);
V3Stats::addStatSum("ConstPool, Constants emitted", m_constsEmitted);
}
};
//######################################################################
// EmitC static functions
void V3EmitC::emitcConstPool() {
UINFO(2, __FUNCTION__ << ": " << endl);
EmitCConstPool(v3Global.rootp()->constPoolp());
}

1066
src/V3EmitCFunc.cpp Normal file

File diff suppressed because it is too large Load Diff

1257
src/V3EmitCFunc.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -30,32 +30,20 @@ class EmitCInlines final : EmitCBaseVisitor {
// STATE
// METHODS
void checkHeavy(AstNode* nodep) {
if (nodep->isHeavy()) v3Global.needHeavy(true);
}
// VISITORS
virtual void visit(AstClass* nodep) override {
checkHeavy(nodep);
iterateChildren(nodep);
}
virtual void visit(AstCNew* nodep) override {
checkHeavy(nodep);
if (v3Global.opt.savable())
v3warn(E_UNSUPPORTED, "Unsupported: --savable with dynamic new");
iterateChildren(nodep);
}
virtual void visit(AstDumpCtl* nodep) override {
checkHeavy(nodep);
if (v3Global.opt.trace()) v3Global.needTraceDumper(true);
iterateChildren(nodep);
}
//---------------------------------------
virtual void visit(AstNode* nodep) override {
checkHeavy(nodep);
iterateChildren(nodep);
}
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
explicit EmitCInlines(AstNetlist* nodep) { iterate(nodep); }

View File

@ -40,7 +40,7 @@ public:
private:
// MAIN METHOD
void emitInt() {
string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__main.cpp";
const string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__main.cpp";
newCFile(filename, false /*slow*/, true /*source*/);
V3OutCFile cf(filename);
m_ofp = &cf;

View File

@ -64,7 +64,7 @@ class CMakeEmitter final {
static void cmake_set(std::ofstream& of, const string& name, const string& value,
const string& cache_type = "", const string& docstring = "") {
string raw_value = "\"" + value + "\"";
const string raw_value = "\"" + value + "\"";
cmake_set_raw(of, name, raw_value, cache_type, docstring);
}
@ -80,7 +80,7 @@ class CMakeEmitter final {
static void emitOverallCMake() {
const std::unique_ptr<std::ofstream> of(
V3File::new_ofstream(v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + ".cmake"));
string name = v3Global.opt.prefix();
const string name = v3Global.opt.prefix();
*of << "# Verilated -*- CMake -*-\n";
*of << "# DESCR"

629
src/V3EmitCModel.cpp Normal file
View File

@ -0,0 +1,629 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Emit C++ for model entry point class
//
// 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 "V3EmitCFunc.h"
#include <algorithm>
#include <vector>
class EmitCModel final : public EmitCFunc {
// METHODS
VL_DEBUG_FUNC;
void putSectionDelimiter(const string& name) {
puts("\n");
puts("//============================================================\n");
puts("// " + name + "\n");
}
void emitHeader(AstNodeModule* modp) {
UASSERT(!m_ofp, "Output file should not be open");
const string filename = v3Global.opt.makeDir() + "/" + topClassName() + ".h";
newCFile(filename, /* slow: */ false, /* source: */ false);
m_ofp = v3Global.opt.systemC() ? new V3OutScFile(filename) : new V3OutCFile(filename);
ofp()->putsHeader();
puts("// DESCRIPTION: Verilator output: Primary model header\n");
puts("//\n");
puts("// This header should be included by all source files instantiating the design.\n");
puts("// The class here is then constructed to instantiate the design.\n");
puts("// See the Verilator manual for examples.\n");
ofp()->putsGuard();
// Include files
puts("\n");
ofp()->putsIntTopInclude();
puts("#include \"verilated_heavy.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");
if (v3Global.dpi()) puts("#include \"svdpi.h\"\n");
// Declare foreign instances up front to make C++ happy
puts("\n");
puts("class " + symClassName() + ";\n");
puts("class " + prefixNameProtect(modp) + ";\n"); // For rootp pointer only
if (v3Global.opt.trace()) puts("class " + v3Global.opt.traceClassLang() + ";\n");
emitModCUse(modp, VUseType::INT_FWD_CLASS); // Note: This is needed for cell forwarding
puts("\n");
puts("// This class is the main interface to the Verilated model\n");
if (optSystemC()) {
puts("SC_MODULE(" + topClassName() + ") {\n");
} else {
puts("class " + topClassName() + " VL_NOT_FINAL {\n");
}
ofp()->resetPrivate();
ofp()->putsPrivate(true); // private:
puts("// Symbol table holding complete model state (owned by this class)\n");
puts(symClassName() + "* const vlSymsp;\n");
puts("\n");
ofp()->putsPrivate(false); // public:
// User accessible IO
puts("\n// PORTS\n"
"// The application code writes and reads these signals to\n"
"// propagate new values into/out from the Verilated model.\n");
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);
}
}
}
// Cells instantiated by the top level (for access to /* verilator public */)
puts("\n// CELLS\n"
"// Public to allow access to /* verilator public */ items.\n"
"// Otherwise the application code can consider these internals.\n");
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstCell* const cellp = VN_CAST_CONST(nodep, Cell)) {
puts(prefixNameProtect(cellp->modp()) + "* const " + cellp->nameProtect() + ";\n");
}
}
// root instance pointer (for access to internals, including public_flat items).
puts("\n// Root instance pointer to allow access to model internals,\n"
"// including inlined /* verilator public_flat_* */ items.\n");
puts(prefixNameProtect(modp) + "* const rootp;\n");
puts("\n");
ofp()->putsPrivate(false); // public:
puts("// CONSTRUCTORS\n");
if (optSystemC()) {
puts("SC_CTOR(" + topClassName() + ");\n");
puts("virtual ~" + topClassName() + "();\n");
} else {
puts("/// Construct the model; called by application code\n");
puts("/// If contextp is null, then the model will use the default global "
"context\n");
puts("/// If name is \"\", then makes a wrapper with a\n");
puts("/// single model invisible with respect to DPI scope names.\n");
puts("explicit " + topClassName() + "(VerilatedContext* contextp,"
+ " const char* name = \"TOP\");\n");
puts("explicit " + topClassName() + "(const char* name = \"TOP\");\n");
puts("/// Destroy the model; called (often implicitly) by application code\n");
puts("virtual ~" + topClassName() + "();\n");
}
ofp()->putsPrivate(true);
puts("VL_UNCOPYABLE(" + topClassName() + "); ///< Copying not allowed\n");
puts("\n");
ofp()->putsPrivate(false); // public:
puts("// API METHODS\n");
string callEvalEndStep
= (v3Global.needTraceDumper() && !optSystemC()) ? "eval_end_step(); " : "";
if (optSystemC()) {
ofp()->putsPrivate(true); ///< eval() is invoked by our sensitive() calls.
}
if (!optSystemC()) {
puts("/// Evaluate the model. Application must call when inputs change.\n");
}
puts("void eval() { eval_step(); " + callEvalEndStep + "}\n");
if (!optSystemC()) {
puts("/// Evaluate when calling multiple units/models per time step.\n");
}
puts("void eval_step();\n");
if (!optSystemC()) {
puts("/// Evaluate at end of a timestep for tracing, when using eval_step().\n");
puts("/// Application must call after all eval() and before time changes.\n");
puts("void eval_end_step()");
if (callEvalEndStep == "") {
puts(" {}\n");
} else {
puts(";\n");
}
}
if (!optSystemC()) {
puts("/// Simulation complete, run final blocks. Application "
"must call on completion.\n");
}
ofp()->putsPrivate(false); // public:
puts("void final();\n");
if (v3Global.opt.trace()) {
puts("/// Trace signals in the model; called by application code\n");
puts("void trace(" + v3Global.opt.traceClassBase()
+ "C* tfp, int levels, int options = 0);\n");
if (optSystemC()) {
puts("/// SC tracing; avoid overloaded virtual function lint warning\n");
puts("virtual void trace(sc_trace_file* tfp) const override { "
"::sc_core::sc_module::trace(tfp); }\n");
}
}
puts("/// Return current simulation context for this model.\n");
puts("/// Used to get to e.g. simulation time via contextp()->time()\n");
puts("VerilatedContext* contextp() const;\n");
if (!optSystemC()) {
puts("/// Retrieve name of this model instance (as passed to constructor).\n");
puts("const char* name() const;\n");
}
// Emit DPI export dispatcher declarations
{
std::vector<const AstCFunc*> funcps;
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstCFunc* funcp = VN_CAST(nodep, CFunc)) {
if (!funcp->dpiExportDispatcher()) continue;
funcps.push_back(funcp);
}
}
stable_sort(funcps.begin(), funcps.end(), [](const AstNode* ap, const AstNode* bp) {
return ap->name() < bp->name();
});
if (!funcps.empty()) {
puts("\n/// DPI Export functions\n");
for (const AstCFunc* funcp : funcps) { emitCFuncDecl(funcp, modp); }
}
}
if (v3Global.opt.savable()) {
puts("\n");
puts("// Serialization functions\n");
puts("friend VerilatedSerialize& operator<<(VerilatedSerialize& os, " //
+ topClassName() + "& rhs);\n");
puts("friend VerilatedDeserialize& operator>>(VerilatedDeserialize& os, "
+ topClassName() + "& rhs);\n");
}
puts("} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);\n");
ofp()->putsEndGuard();
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
}
void emitConstructorImplementation(AstNodeModule* modp) {
putSectionDelimiter("Constructors");
puts("\n");
puts(topClassName() + "::" + topClassName());
if (optSystemC()) {
puts("(sc_module_name /* unused */)\n");
puts(" : vlSymsp{new " + symClassName() + "(nullptr, name(), this)}\n");
} else {
puts(+"(VerilatedContext* _vcontextp__, const char* _vcname__)\n");
puts(" : vlSymsp{new " + symClassName() + "(_vcontextp__, _vcname__, this)}\n");
}
// Set up IO references
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) {
if (varp->isPrimaryIO()) {
const string protName = varp->nameProtect();
puts(" , " + protName + "{vlSymsp->TOP." + protName + "}\n");
}
}
}
// Setup cell pointers
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstCell* const cellp = VN_CAST_CONST(nodep, Cell)) {
const string protName = cellp->nameProtect();
puts(" , " + protName + "{vlSymsp->TOP." + protName + "}\n");
}
}
// Setup rootp root instance pointer,
puts(" , rootp{&(vlSymsp->TOP)}\n");
puts("{\n");
if (optSystemC()) {
// Create sensitivity list for when to evaluate the model.
putsDecoration("// Sensitivities on all clocks and combinational inputs\n");
puts("SC_METHOD(eval);\n");
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstVar* const varp = VN_CAST(nodep, Var)) {
if (varp->isNonOutput() && (varp->isScSensitive() || varp->isUsedClock())) {
int vects = 0;
// This isn't very robust and may need cleanup for other data types
for (AstUnpackArrayDType* arrayp
= VN_CAST(varp->dtypeSkipRefp(), UnpackArrayDType);
arrayp;
arrayp = VN_CAST(arrayp->subDTypep()->skipRefp(), 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(arrayp->lo()));
puts("; " + ivar + "<=" + cvtToStr(arrayp->hi()));
puts("; ++" + ivar + ") {\n");
}
puts("sensitive << " + 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");
}
puts("}\n");
if (!optSystemC()) {
puts("\n");
puts(topClassName() + "::" + topClassName() + "(const char* _vcname__)\n");
puts(" : " + topClassName() + "(nullptr, _vcname__)\n{\n}\n");
}
}
void emitDestructorImplementation() {
putSectionDelimiter("Destructor");
puts("\n");
puts(topClassName() + "::~" + topClassName() + "() {\n");
puts("delete vlSymsp;\n");
puts("}\n");
}
void emitSettleLoop(AstNodeModule* modp, bool initial) {
const string topModNameProtected = prefixNameProtect(modp);
putsDecoration("// Evaluate till stable\n");
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(\"+ ");
puts(initial ? "Initial" : "Clock");
puts(" loop\\n\"););\n");
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");
}
void emitStandardMethods(AstNodeModule* modp) {
UASSERT_OBJ(modp->isTop(), modp, "Attempting to emitWrapEval for non-top class");
const string topModNameProtected = prefixNameProtect(modp);
const string selfDecl = "(" + topModNameProtected + "* vlSelf)";
putSectionDelimiter("Evaluation loop");
// Forward declarations
puts("\n");
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");
puts("#ifdef VL_DEBUG\n");
puts("void " + topModNameProtected + "__" + protect("_eval_debug_assertions") + selfDecl
+ ";\n");
puts("#endif // VL_DEBUG\n");
puts("void " + topModNameProtected + "__" + protect("_final") + selfDecl + ";\n");
// _eval_initial_loop
puts("\nstatic void " + protect("_eval_initial_loop") + "(" + symClassVar() + ")"
+ " {\n");
puts("vlSymsp->__Vm_didInit = true;\n");
puts(topModNameProtected + "__" + protect("_eval_initial") + "(&(vlSymsp->TOP));\n");
emitSettleLoop(modp, /* initial: */ true);
ensureNewLine();
puts("}\n");
// ::eval_step
puts("\nvoid " + topClassName() + "::eval_step() {\n");
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+++++TOP Evaluate " + topClassName()
+ "::eval_step\\n\"); );\n");
puts("#ifdef VL_DEBUG\n");
putsDecoration("// Debug assertions\n");
puts(topModNameProtected + "__" + protect("_eval_debug_assertions")
+ "(&(vlSymsp->TOP));\n");
puts("#endif // VL_DEBUG\n");
putsDecoration("// Initialize\n");
puts("if (VL_UNLIKELY(!vlSymsp->__Vm_didInit)) " + protect("_eval_initial_loop")
+ "(vlSymsp);\n");
if (v3Global.opt.threads() == 1) {
uint32_t mtaskId = 0;
putsDecoration("// MTask " + cvtToStr(mtaskId) + " start\n");
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"MTask" + cvtToStr(mtaskId) + " starting\\n\"););\n");
puts("Verilated::mtaskId(" + cvtToStr(mtaskId) + ");\n");
}
if (v3Global.opt.mtasks() && v3Global.opt.profThreads()) {
puts("if (VL_UNLIKELY((vlSymsp->_vm_contextp__->profThreadsStart() != "
"vlSymsp->__Vm_profile_time_finished)\n");
puts(" && (VL_TIME_Q() > vlSymsp->_vm_contextp__->profThreadsStart())\n");
puts(" && (vlSymsp->_vm_contextp__->profThreadsWindow() >= 1))) {\n");
// Within a profile (either starting, middle, or end)
puts("if (vlSymsp->__Vm_profile_window_ct == 0) {\n"); // Opening file?
// Start profile on this cycle. We'll capture a window worth, then
// only analyze the next window worth. The idea is that the first window
// capture will hit some cache-cold stuff (eg printf) but it'll be warm
// by the time we hit the second window, we hope.
puts("vlSymsp->__Vm_profile_cycle_start = VL_RDTSC_Q();\n");
// "* 2" as first half is warmup, second half is collection
puts("vlSymsp->__Vm_profile_window_ct = vlSymsp->_vm_contextp__->profThreadsWindow() "
"* 2 "
"+ "
"1;\n");
puts("}\n");
puts("--(vlSymsp->__Vm_profile_window_ct);\n");
puts("if (vlSymsp->__Vm_profile_window_ct == "
"vlSymsp->_vm_contextp__->profThreadsWindow()) {\n");
// This barrier record in every threads' profile demarcates the
// cache-warm-up cycles before the barrier from the actual profile
// cycles afterward.
puts("vlSymsp->__Vm_threadPoolp->profileAppendAll(");
puts("VlProfileRec(VlProfileRec::Barrier()));\n");
puts("vlSymsp->__Vm_profile_cycle_start = VL_RDTSC_Q();\n");
puts("}\n");
puts("else if (vlSymsp->__Vm_profile_window_ct == 0) {\n");
// Ending file.
puts("vluint64_t elapsed = VL_RDTSC_Q() - vlSymsp->__Vm_profile_cycle_start;\n");
puts("vlSymsp->__Vm_threadPoolp->profileDump(vlSymsp->_vm_contextp__->"
"profThreadsFilename().c_str(), elapsed);\n");
// This turns off the test to enter the profiling code, but still
// allows the user to collect another profile by changing
// profThreadsStart
puts("vlSymsp->__Vm_profile_time_finished = "
"vlSymsp->_vm_contextp__->profThreadsStart();\n");
puts("vlSymsp->__Vm_profile_cycle_start = 0;\n");
puts("}\n");
puts("}\n");
}
emitSettleLoop(modp, /* initial: */ false);
if (v3Global.opt.threads() == 1) {
puts("Verilated::endOfThreadMTask(vlSymsp->__Vm_evalMsgQp);\n");
}
if (v3Global.opt.threads()) puts("Verilated::endOfEval(vlSymsp->__Vm_evalMsgQp);\n");
puts("}\n");
// ::eval_end_step
if (v3Global.needTraceDumper() && !optSystemC()) {
puts("\nvoid " + topClassName() + "::eval_end_step() {\n");
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+eval_end_step " + topClassName()
+ "::eval_end_step\\n\"); );\n");
puts("#ifdef VM_TRACE\n");
putsDecoration("// Tracing\n");
// SystemC's eval loop deals with calling trace, not us
puts("if (VL_UNLIKELY(vlSymsp->__Vm_dumping)) vlSymsp->_traceDump();\n");
puts("#endif // VM_TRACE\n");
puts("}\n");
}
putSectionDelimiter("Invoke final blocks");
// ::final
puts("\nvoid " + topClassName() + "::final() {\n");
puts(topModNameProtected + "__" + protect("_final") + "(&(vlSymsp->TOP));\n");
puts("}\n");
putSectionDelimiter("Utilities");
// ::contextp
puts("\nVerilatedContext* " + topClassName() + "::contextp() const {\n");
puts(/**/ "return vlSymsp->_vm_contextp__;\n");
puts("}\n");
if (!optSystemC()) {
// ::name
puts("\nconst char* " + topClassName() + "::name() const {\n");
puts(/**/ "return vlSymsp->name();\n");
puts("}\n");
}
}
void emitTraceMethods(AstNodeModule* modp) {
const string topModNameProtected = prefixNameProtect(modp);
putSectionDelimiter("Trace configuration");
// Forward declaration
puts("\nvoid " + topModNameProtected + "__" + protect("traceInitTop") + "("
+ topModNameProtected + "* vlSelf, " + v3Global.opt.traceClassBase()
+ "* tracep);\n");
// Static helper function
puts("\nstatic void " + protect("traceInit") + "(void* voidSelf, "
+ v3Global.opt.traceClassBase() + "* tracep, uint32_t code) {\n");
putsDecoration("// Callback from tracep->open()\n");
puts(voidSelfAssign(modp));
puts(symClassAssign());
puts("if (!vlSymsp->_vm_contextp__->calcUnusedSigs()) {\n");
puts("VL_FATAL_MT(__FILE__, __LINE__, __FILE__,\n");
puts("\"Turning on wave traces requires Verilated::traceEverOn(true) call before time "
"0.\");\n");
puts("}\n");
puts("vlSymsp->__Vm_baseCode = code;\n");
puts("tracep->module(vlSymsp->name());\n");
puts("tracep->scopeEscape(' ');\n");
puts(topModNameProtected + "__" + protect("traceInitTop") + "(vlSelf, tracep);\n");
puts("tracep->scopeEscape('.');\n"); // Restore so later traced files won't break
puts("}\n");
// Forward declaration
puts("\nvoid " + topModNameProtected + "__" + protect("traceRegister") + "("
+ 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")
+ "(&(vlSymsp->TOP), tfp->spTrace());\n");
puts("}\n");
}
void emitSerializationFunctions() {
putSectionDelimiter("Model serialization");
puts("\nVerilatedSerialize& operator<<(VerilatedSerialize& os, " + topClassName()
+ "& rhs) {\n");
puts("Verilated::quiesce();\n");
puts("rhs.vlSymsp->" + protect("__Vserialize") + "(os);\n");
puts("return os;\n");
puts("}\n");
puts("\nVerilatedDeserialize& operator>>(VerilatedDeserialize& os, " + topClassName()
+ "& rhs) {\n");
puts("Verilated::quiesce();\n");
puts("rhs.vlSymsp->" + protect("__Vdeserialize") + "(os);\n");
puts("return os;\n");
puts("}\n");
}
void emitImplementation(AstNodeModule* modp) {
UASSERT(!m_ofp, "Output file should not be open");
const string filename = v3Global.opt.makeDir() + "/" + topClassName() + ".cpp";
newCFile(filename, /* slow: */ false, /* source: */ true);
m_ofp = v3Global.opt.systemC() ? new V3OutScFile(filename) : new V3OutCFile(filename);
ofp()->putsHeader();
puts("// DESCRIPTION: Verilator output: "
"Model implementation (design independent parts)\n");
puts("\n");
puts("#include \"" + topClassName() + ".h\"\n");
puts("#include \"" + symClassName() + ".h\"\n");
if (v3Global.opt.trace()) {
puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n");
}
if (v3Global.dpi()) { //
puts("#include \"verilated_dpi.h\"\n");
}
emitConstructorImplementation(modp);
emitDestructorImplementation();
emitStandardMethods(modp);
if (v3Global.opt.trace()) { emitTraceMethods(modp); }
if (v3Global.opt.savable()) { emitSerializationFunctions(); }
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
}
void emitDpiExportDispatchers(AstNodeModule* modp) {
UASSERT(!m_ofp, "Output file should not be open");
// Emit DPI Export dispatchers
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
AstCFunc* const funcp = VN_CAST(nodep, CFunc);
if (!funcp || !funcp->dpiExportDispatcher()) continue;
if (splitNeeded()) {
// Splitting file, so using parallel build.
v3Global.useParallelBuild(true);
// Close old file
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
}
if (!m_ofp) {
const string filename = v3Global.opt.makeDir() + "/" + topClassName()
+ "__Dpi_Export_" + cvtToStr(splitFilenumInc() - 1)
+ ".cpp";
newCFile(filename, /* slow: */ false, /* source: */ true);
m_ofp = v3Global.opt.systemC() ? new V3OutScFile(filename)
: new V3OutCFile(filename);
m_lazyDecls.reset();
m_ofp->putsHeader();
puts(
"// DESCRIPTION: Verilator output: Implementation of DPI export functions.\n");
puts("//\n");
puts("#include \"" + topClassName() + ".h\"\n");
puts("#include \"" + symClassName() + ".h\"\n");
puts("#include \"verilated_dpi.h\"\n");
puts("\n");
}
iterate(funcp);
}
if (m_ofp) { VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); }
}
void main(AstNodeModule* modp) {
m_modp = modp;
emitHeader(modp);
emitImplementation(modp);
if (v3Global.dpi()) { emitDpiExportDispatchers(modp); }
}
// VISITORS
public:
explicit EmitCModel(AstNetlist* netlistp) { main(netlistp->topModulep()); }
};
//######################################################################
// EmitC class functions
void V3EmitC::emitcModel() {
UINFO(2, __FUNCTION__ << ": " << endl);
{ EmitCModel emit(v3Global.rootp()); }
}

View File

@ -83,9 +83,9 @@ class EmitCSyms final : EmitCBaseVisitor {
};
struct CmpDpi {
bool operator()(const AstCFunc* lhsp, const AstCFunc* rhsp) const {
if (lhsp->dpiImport() != rhsp->dpiImport()) {
if (lhsp->dpiImportPrototype() != rhsp->dpiImportPrototype()) {
// cppcheck-suppress comparisonOfFuncReturningBoolError
return lhsp->dpiImport() < rhsp->dpiImport();
return lhsp->dpiImportPrototype() < rhsp->dpiImportPrototype();
}
return lhsp->name() < rhsp->name();
}
@ -125,7 +125,7 @@ class EmitCSyms final : EmitCBaseVisitor {
&& !(VN_IS(nodep, CFunc)
&& (VN_CAST(nodep, CFunc)->isConstructor()
|| VN_CAST(nodep, CFunc)->isDestructor()))) {
string rsvd = V3LanguageWords::isKeyword(nodep->name());
const string rsvd = V3LanguageWords::isKeyword(nodep->name());
if (rsvd != "") {
// Generally V3Name should find all of these and throw SYMRSVDWORD.
// We'll still check here because the compiler errors
@ -170,7 +170,7 @@ class EmitCSyms final : EmitCBaseVisitor {
const auto scpit = m_vpiScopeCandidates.find(scp);
if ((scpit != m_vpiScopeCandidates.end())
&& (m_scopeNames.find(scp) == m_scopeNames.end())) {
auto scopeNameit = m_scopeNames.find(scpit->second.m_symName);
const auto scopeNameit = m_scopeNames.find(scpit->second.m_symName);
if (scopeNameit == m_scopeNames.end()) {
m_scopeNames.emplace(scpit->second.m_symName, scpit->second);
} else {
@ -216,9 +216,9 @@ class EmitCSyms final : EmitCBaseVisitor {
}
// UINFO(9,"For "<<scopep->name()<<" - "<<varp->name()<<" Scp "<<scpName<<"
// Var "<<varBase<<endl);
string varBasePretty = AstNode::prettyName(varBase);
string scpPretty = AstNode::prettyName(scpName);
string scpSym = scopeSymString(scpName);
const string varBasePretty = AstNode::prettyName(varBase);
const string scpPretty = AstNode::prettyName(scpName);
const string scpSym = scopeSymString(scpName);
// UINFO(9," scnameins sp "<<scpName<<" sp "<<scpPretty<<" ss "<<scpSym<<endl);
if (v3Global.opt.vpi()) varHierarchyScopes(scpName);
if (m_scopeNames.find(scpSym) == m_scopeNames.end()) {
@ -238,7 +238,7 @@ class EmitCSyms final : EmitCBaseVisitor {
++it) {
if (it->second.m_type != "SCOPE_MODULE") continue;
string symName = it->second.m_symName;
const string symName = it->second.m_symName;
string above = symName;
if (above.substr(0, 4) == "TOP.") above.replace(0, 4, "");
@ -278,6 +278,7 @@ class EmitCSyms final : EmitCBaseVisitor {
if (!m_dpiHdrOnly) emitDpiImp();
}
}
virtual void visit(AstConstPool* nodep) override {} // Ignore
virtual void visit(AstNodeModule* nodep) override {
nameCheck(nodep);
VL_RESTORER(m_modp);
@ -288,10 +289,11 @@ class EmitCSyms final : EmitCBaseVisitor {
}
virtual void visit(AstCellInline* nodep) override {
if (v3Global.opt.vpi()) {
string type = (nodep->origModName() == "__BEGIN__") ? "SCOPE_OTHER" : "SCOPE_MODULE";
string name = nodep->scopep()->name() + "__DOT__" + nodep->name();
string name_dedot = AstNode::dedotName(name);
int timeunit = m_modp->timeunit().powerOfTen();
const string type
= (nodep->origModName() == "__BEGIN__") ? "SCOPE_OTHER" : "SCOPE_MODULE";
const string name = nodep->scopep()->name() + "__DOT__" + nodep->name();
const string name_dedot = AstNode::dedotName(name);
const int timeunit = m_modp->timeunit().powerOfTen();
m_vpiScopeCandidates.insert(
std::make_pair(name, ScopeData(scopeSymString(name), name_dedot, timeunit, type)));
}
@ -303,19 +305,19 @@ class EmitCSyms final : EmitCBaseVisitor {
m_scopes.emplace_back(std::make_pair(nodep, m_modp));
if (v3Global.opt.vpi() && !nodep->isTop()) {
string type = VN_IS(nodep->modp(), Package) ? "SCOPE_OTHER" : "SCOPE_MODULE";
string name_dedot = AstNode::dedotName(nodep->shortName());
int timeunit = m_modp->timeunit().powerOfTen();
const string type = VN_IS(nodep->modp(), Package) ? "SCOPE_OTHER" : "SCOPE_MODULE";
const string name_dedot = AstNode::dedotName(nodep->shortName());
const int timeunit = m_modp->timeunit().powerOfTen();
m_vpiScopeCandidates.insert(
std::make_pair(nodep->name(), ScopeData(scopeSymString(nodep->name()), name_dedot,
timeunit, type)));
}
}
virtual void visit(AstScopeName* nodep) override {
string name = nodep->scopeSymName();
const string name = nodep->scopeSymName();
// UINFO(9,"scnameins sp "<<nodep->name()<<" sp "<<nodep->scopePrettySymName()
// <<" ss"<<name<<endl);
int timeunit = m_modp ? m_modp->timeunit().powerOfTen() : 0;
const int timeunit = m_modp ? m_modp->timeunit().powerOfTen() : 0;
if (m_scopeNames.find(name) == m_scopeNames.end()) {
m_scopeNames.emplace(
name, ScopeData(name, nodep->scopePrettySymName(), timeunit, "SCOPE_OTHER"));
@ -347,7 +349,7 @@ class EmitCSyms final : EmitCBaseVisitor {
}
virtual void visit(AstCFunc* nodep) override {
nameCheck(nodep);
if (nodep->dpiImport() || nodep->dpiExportWrapper()) m_dpis.push_back(nodep);
if (nodep->dpiImportPrototype() || nodep->dpiExportDispatcher()) m_dpis.push_back(nodep);
VL_RESTORER(m_cfuncp);
{
m_cfuncp = nodep;
@ -368,7 +370,7 @@ public:
void EmitCSyms::emitSymHdr() {
UINFO(6, __FUNCTION__ << ": " << endl);
string filename = v3Global.opt.makeDir() + "/" + symClassName() + ".h";
const string filename = v3Global.opt.makeDir() + "/" + symClassName() + ".h";
newCFile(filename, true /*slow*/, false /*source*/);
if (v3Global.opt.systemC()) {
@ -378,8 +380,7 @@ void EmitCSyms::emitSymHdr() {
}
ofp()->putsHeader();
puts("// DESCR"
"IPTION: Verilator output: Symbol table internal header\n");
puts("// DESCRIPTION: Verilator output: Symbol table internal header\n");
puts("//\n");
puts("// Internal details; most calling programs do not need this header,\n");
puts("// unless using verilator public meta comments.\n");
@ -388,12 +389,14 @@ void EmitCSyms::emitSymHdr() {
puts("\n");
ofp()->putsIntTopInclude();
if (v3Global.needHeavy()) {
puts("#include \"verilated_heavy.h\"\n");
} else {
puts("#include \"verilated.h\"\n");
puts("#include \"verilated_heavy.h\"\n");
if (v3Global.needTraceDumper()) {
puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n");
}
puts("\n// INCLUDE MODEL CLASS\n");
puts("\n#include \"" + topClassName() + ".h\"\n");
puts("\n// INCLUDE MODULE CLASSES\n");
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep;
nodep = VN_CAST(nodep->nextp(), NodeModule)) {
@ -406,49 +409,61 @@ void EmitCSyms::emitSymHdr() {
std::map<const string, int> types; // Remove duplicates and sort
for (const auto& itr : m_scopeFuncs) {
AstCFunc* funcp = itr.second.m_cfuncp;
if (funcp->dpiExport()) {
string cbtype = protect(v3Global.opt.prefix() + "__Vcb_" + funcp->cname() + "_t");
if (funcp->dpiExportImpl()) {
const string cbtype
= protect(v3Global.opt.prefix() + "__Vcb_" + funcp->cname() + "_t");
types["using " + cbtype + " = void (*) (" + cFuncArgs(funcp) + ");\n"] = 1;
}
}
for (const auto& i : types) puts(i.first);
}
puts("\n// SYMS CLASS\n");
puts(string("class ") + symClassName() + " : public VerilatedSyms {\n");
puts("\n// SYMS CLASS (contains all model state)\n");
puts("class " + symClassName() + " final : public VerilatedSyms {\n");
ofp()->putsPrivate(false); // public:
puts("\n// LOCAL STATE\n");
// Must be before subcells, as constructor order needed before _vlCoverInsert.
puts("const char* __Vm_namep;\n");
puts("// INTERNAL STATE\n");
puts(topClassName() + "* const __Vm_modelp;\n");
if (v3Global.needTraceDumper()) {
// __Vm_dumperp is local, otherwise we wouldn't know what design's eval()
// should call a global dumpperp
puts("bool __Vm_dumping; // Dumping is active\n");
puts("bool __Vm_dumping = false; // Dumping is active\n");
puts("VerilatedMutex __Vm_dumperMutex; // Protect __Vm_dumperp\n");
puts(v3Global.opt.traceClassLang()
+ "* __Vm_dumperp VL_GUARDED_BY(__Vm_dumperMutex); /// Trace class for $dump*\n");
+ "* __Vm_dumperp VL_GUARDED_BY(__Vm_dumperMutex) = nullptr;"
" /// Trace class for $dump*\n");
}
if (v3Global.opt.trace()) {
puts("bool __Vm_activity; ///< Used by trace routines to determine change occurred\n");
puts("uint32_t __Vm_baseCode; "
"///< Used by trace routines when tracing multiple models\n");
puts("bool __Vm_activity = false;"
" ///< Used by trace routines to determine change occurred\n");
puts("uint32_t __Vm_baseCode = 0;"
" ///< Used by trace routines when tracing multiple models\n");
}
puts("bool __Vm_didInit;\n");
puts("bool __Vm_didInit = false;\n");
puts("\n// SUBCELL STATE\n");
if (v3Global.opt.mtasks()) {
puts("VlThreadPool* const __Vm_threadPoolp;\n");
puts("bool __Vm_even_cycle = false;\n");
if (v3Global.opt.profThreads()) {
// rdtsc() at current cycle start
puts("vluint64_t __Vm_profile_cycle_start = 0;\n");
// Time we finished analysis
puts("vluint64_t __Vm_profile_time_finished = 0;\n");
// Track our position in the cache warmup and actual profile window
puts("vluint32_t __Vm_profile_window_ct = 0;\n");
}
}
puts("\n// MODULE INSTANCE STATE\n");
for (const auto& i : m_scopes) {
AstScope* scopep = i.first;
AstNodeModule* modp = i.second;
if (VN_IS(modp, Class)) continue;
if (modp->isTop()) {
ofp()->printf("%-30s ", (prefixNameProtect(modp) + "*").c_str());
puts(protectIf(scopep->nameDotless() + "p", scopep->protect()) + ";\n");
} else {
ofp()->printf("%-30s ", (prefixNameProtect(modp) + "").c_str());
puts(protectIf(scopep->nameDotless(), scopep->protect()) + ";\n");
}
const string name = prefixNameProtect(modp);
ofp()->printf("%-30s ", name.c_str());
puts(protectIf(scopep->nameDotless(), scopep->protect()) + ";\n");
}
if (m_coverBins) {
@ -471,28 +486,30 @@ void EmitCSyms::emitSymHdr() {
puts("VerilatedHierarchy __Vhier;\n");
}
puts("\n// CREATORS\n");
puts(symClassName() + "(VerilatedContext* contextp, " + topClassName()
+ "* topp, const char* namep);\n");
puts("\n// CONSTRUCTORS\n");
puts(symClassName() + "(VerilatedContext* contextp, const char* namep, " + topClassName()
+ "* modelp);\n");
puts(string("~") + symClassName() + "();\n");
for (const auto& i : m_usesVfinal) {
puts("void " + symClassName() + "_" + cvtToStr(i.first) + "(");
if (i.second) {
puts("int __Vfinal");
} else {
puts(topClassName() + "* topp");
}
if (i.second) { puts("int __Vfinal"); }
puts(");\n");
}
puts("\n// METHODS\n");
puts("inline const char* name() { return __Vm_namep; }\n");
puts("const char* name() { return TOP.name(); }\n");
if (v3Global.needTraceDumper()) {
if (!optSystemC()) puts("void _traceDump();\n");
puts("void _traceDumpOpen();\n");
puts("void _traceDumpClose();\n");
}
if (v3Global.opt.savable()) {
puts("void " + protect("__Vserialize") + "(VerilatedSerialize& os);\n");
puts("void " + protect("__Vdeserialize") + "(VerilatedDeserialize& os);\n");
}
puts("\n");
puts("} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);\n");
ofp()->putsEndGuard();
@ -530,20 +547,12 @@ void EmitCSyms::checkSplit(bool usesVfinal) {
}
m_ofpBase->puts(symClassName() + "_" + cvtToStr(m_funcNum) + "(");
if (usesVfinal) {
m_ofpBase->puts("__Vfinal");
} else {
m_ofpBase->puts("topp");
}
if (usesVfinal) { m_ofpBase->puts("__Vfinal"); }
m_ofpBase->puts(");\n");
emitSymImpPreamble();
puts("void " + symClassName() + "::" + symClassName() + "_" + cvtToStr(m_funcNum) + "(");
if (usesVfinal) {
puts("int __Vfinal");
} else {
puts(topClassName() + "* topp");
}
if (usesVfinal) { puts("int __Vfinal"); }
puts(") {\n");
}
@ -555,21 +564,32 @@ void EmitCSyms::emitSymImpPreamble() {
// Includes
puts("#include \"" + symClassName() + ".h\"\n");
puts("#include \"" + topClassName() + ".h\"\n");
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep;
nodep = VN_CAST(nodep->nextp(), NodeModule)) {
if (VN_IS(nodep, Class)) continue; // Class included earlier
puts("#include \"" + prefixNameProtect(nodep) + ".h\"\n");
}
puts("\n");
// Declarations for DPI Export implementation functions
bool needsNewLine = false;
for (const auto& pair : m_scopeFuncs) {
const AstCFunc* const funcp = pair.second.m_cfuncp;
if (!funcp->dpiExportImpl()) continue;
emitCFuncDecl(funcp, pair.second.m_modp);
needsNewLine = true;
}
if (needsNewLine) puts("\n");
}
void EmitCSyms::emitScopeHier(bool destroy) {
if (v3Global.opt.vpi()) {
string verb = destroy ? "Tear down" : "Set up";
string method = destroy ? "remove" : "add";
const string verb = destroy ? "Tear down" : "Set up";
const string method = destroy ? "remove" : "add";
puts("\n// " + verb + " scope hierarchy\n");
for (ScopeNames::const_iterator it = m_scopeNames.begin(); it != m_scopeNames.end();
++it) {
string name = it->second.m_prettyName;
const string name = it->second.m_prettyName;
if (it->first == "TOP") continue;
if ((name.find('.') == string::npos) && (it->second.m_type == "SCOPE_MODULE")) {
puts("__Vhier." + method + "(0, &" + protect("__Vscope_" + it->second.m_symName)
@ -580,8 +600,8 @@ void EmitCSyms::emitScopeHier(bool destroy) {
for (auto it = m_vpiScopeHierarchy.cbegin(); it != m_vpiScopeHierarchy.cend(); ++it) {
for (ScopeNameList::const_iterator lit = it->second.begin(); lit != it->second.end();
++lit) {
string fromname = scopeSymString(it->first);
string toname = scopeSymString(*lit);
const string fromname = scopeSymString(it->first);
const string toname = scopeSymString(*lit);
const auto from = vlstd::as_const(m_scopeNames).find(fromname);
const auto to = vlstd::as_const(m_scopeNames).find(toname);
UASSERT(from != m_scopeNames.end(), fromname + " not in m_scopeNames");
@ -597,7 +617,7 @@ void EmitCSyms::emitScopeHier(bool destroy) {
void EmitCSyms::emitSymImp() {
UINFO(6, __FUNCTION__ << ": " << endl);
string filename = v3Global.opt.makeDir() + "/" + symClassName() + ".cpp";
const string filename = v3Global.opt.makeDir() + "/" + symClassName() + ".cpp";
AstCFile* cfilep = newCFile(filename, true /*slow*/, true /*source*/);
cfilep->support(true);
@ -610,89 +630,120 @@ void EmitCSyms::emitSymImp() {
m_ofpBase = m_ofp;
emitSymImpPreamble();
// puts("\n// GLOBALS\n");
puts("\n");
if (v3Global.opt.savable()) {
puts("\n");
for (int de = 0; de < 2; ++de) {
string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize";
string funcname = de ? "__Vdeserialize" : "__Vserialize";
string op = de ? ">>" : "<<";
const string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize";
const string funcname = de ? "__Vdeserialize" : "__Vserialize";
const string op = de ? ">>" : "<<";
// NOLINTNEXTLINE(performance-inefficient-string-concatenation)
puts("void " + symClassName() + "::" + protect(funcname) + "(" + classname
+ "& os) {\n");
puts("// LOCAL STATE\n");
// __Vm_namep presumably already correct
puts("// Internal state\n");
if (v3Global.opt.trace()) puts("os" + op + "__Vm_activity;\n");
puts("os" + op + "__Vm_didInit;\n");
puts("// SUBCELL STATE\n");
for (std::vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end();
++it) {
AstScope* scopep = it->first;
AstNodeModule* modp = it->second;
if (!modp->isTop()) {
puts(protectIf(scopep->nameDotless(), scopep->protect()) + "."
+ protect(funcname) + "(os);\n");
}
puts("os " + op + " __Vm_didInit;\n");
puts("// Module instance state\n");
for (const auto& pair : m_scopes) {
const AstScope* const scopep = pair.first;
puts(protectIf(scopep->nameDotless(), scopep->protect()) + "." + protect(funcname)
+ "(os);\n");
}
puts("}\n");
}
puts("\n");
}
puts("\n");
puts("\n// FUNCTIONS\n");
puts("// FUNCTIONS\n");
// Destructor
puts(symClassName() + "::~" + symClassName() + "()\n");
puts("{\n");
emitScopeHier(true);
puts("}\n\n");
puts(symClassName() + "::" + symClassName() + "(VerilatedContext* contextp, " + topClassName()
+ "* topp, const char* namep)\n");
puts(" // Setup locals\n");
puts(" : VerilatedSyms{contextp}\n");
puts(" , __Vm_namep(namep)\n"); // No leak, as gets destroyed when the top is destroyed
if (v3Global.needTraceDumper()) {
puts(" , __Vm_dumping(false)\n");
puts(" , __Vm_dumperp(nullptr)\n");
puts("#ifdef VM_TRACE\n");
puts("if (__Vm_dumping) _traceDumpClose();\n");
puts("#endif // VM_TRACE\n");
}
if (v3Global.opt.trace()) {
puts(" , __Vm_activity(false)\n");
puts(" , __Vm_baseCode(0)\n");
if (v3Global.opt.mtasks()) { puts("delete __Vm_threadPoolp;\n"); }
puts("}\n\n");
// Constructor
puts(symClassName() + "::" + symClassName() + "(VerilatedContext* contextp, const char* namep,"
+ topClassName() + "* modelp)\n");
puts(" : VerilatedSyms{contextp}\n");
puts(" // Setup internal state of the Syms class\n");
puts(" , __Vm_modelp(modelp)\n");
if (v3Global.opt.mtasks()) {
// TODO -- For now each model creates its own ThreadPool here,
// and deletes it in the destructor. If A and B are each top level
// modules, each creates a separate thread pool. This allows
// A.eval() and B.eval() to run concurrently without any
// interference -- so long as the physical machine has enough cores
// to support both pools and all testbench threads.
//
// In the future, we might want to let the client provide a
// threadpool to the constructor. This would allow two or more
// models to share a single threadpool.
//
// For example: suppose models A and B are each compiled to run on
// 4 threads. The client might create a single thread pool with 3
// threads and pass it to both models. If the client can ensure that
// A.eval() and B.eval() do NOT run concurrently, there will be no
// contention for the threads. This mode is missing for now. (Is
// there demand for such a setup?)
//
// 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__, "
+ cvtToStr(v3Global.opt.threads() - 1) + ", " + cvtToStr(v3Global.opt.profThreads())
+ "))\n");
}
puts(" , __Vm_didInit(false)\n");
puts(" // Setup submodule names\n");
char comma = ',';
puts(" // Setup module instances\n");
for (const auto& i : m_scopes) {
AstScope* scopep = i.first;
AstNodeModule* modp = i.second;
const AstScope* const scopep = i.first;
const AstNodeModule* const modp = i.second;
puts(" , ");
puts(protect(scopep->nameDotless()));
if (modp->isTop()) {
puts("(namep)\n");
} else {
puts(string(" ") + comma + " " + protect(scopep->nameDotless()));
puts("(Verilated::catName(topp->name(), ");
// The "." is added by catName
puts("(Verilated::catName(namep, ");
putsQuoted(protectWordsIf(scopep->prettyName(), scopep->protect()));
puts("))\n");
comma = ',';
++m_numStmts;
}
++m_numStmts;
}
puts("{\n");
puts("// Pointer to top level\n");
puts("TOPp = topp;\n");
puts("// Configure time unit / time precision\n");
if (!v3Global.rootp()->timeunit().isNone()) {
puts("_vm_contextp__->timeunit(");
puts(cvtToStr(v3Global.rootp()->timeunit().powerOfTen()));
puts(");\n");
}
if (!v3Global.rootp()->timeprecision().isNone()) {
puts("_vm_contextp__->timeprecision(");
puts(cvtToStr(v3Global.rootp()->timeprecision().powerOfTen()));
puts(");\n");
}
puts("// Setup each module's pointers to their submodules\n");
for (const auto& i : m_scopes) {
AstScope* scopep = i.first;
AstNodeModule* modp = i.second;
if (!modp->isTop()) {
const AstScope* const scopep = i.first;
const AstNodeModule* const modp = i.second;
if (const AstScope* const aboveScopep = scopep->aboveScopep()) {
checkSplit(false);
string arrow = scopep->name();
string::size_type pos;
while ((pos = arrow.find('.')) != string::npos) arrow.replace(pos, 1, "->");
if (arrow.substr(0, 5) == "TOP->") arrow.replace(0, 5, "TOPp->");
ofp()->puts(protectWordsIf(arrow, scopep->protect()));
const string protName = protectWordsIf(scopep->name(), scopep->protect());
if (VN_IS(modp, ClassPackage)) {
// ClassPackage modules seem to be a bit out of place, so hard code...
puts("TOP");
} else {
puts(protectIf(aboveScopep->nameDotless(), aboveScopep->protect()));
}
puts(".");
puts(protName.substr(protName.rfind(".") + 1));
puts(" = &");
puts(protectIf(scopep->nameDotless(), scopep->protect()) + ";\n");
++m_numStmts;
@ -700,19 +751,16 @@ void EmitCSyms::emitSymImp() {
}
puts("// Setup each module's pointer back to symbol table (for public functions)\n");
puts("TOPp->" + protect("__Vconfigure") + "(this, true);\n");
for (const auto& i : m_scopes) {
AstScope* scopep = i.first;
AstNodeModule* modp = i.second;
if (!modp->isTop()) {
checkSplit(false);
// first is used by AstCoverDecl's call to __vlCoverInsert
bool first = !modp->user1();
modp->user1(true);
puts(protectIf(scopep->nameDotless(), scopep->protect()) + "."
+ protect("__Vconfigure") + "(this, " + (first ? "true" : "false") + ");\n");
++m_numStmts;
}
checkSplit(false);
// first is used by AstCoverDecl's call to __vlCoverInsert
const bool first = !modp->user1();
modp->user1(true);
puts(protectIf(scopep->nameDotless(), scopep->protect()) + "." + protect("__Vconfigure")
+ "(this, " + (first ? "true" : "false") + ");\n");
++m_numStmts;
}
if (!m_scopeNames.empty()) { // Setup scope names
@ -742,13 +790,13 @@ void EmitCSyms::emitSymImp() {
AstScopeName* scopep = it->second.m_scopep;
AstCFunc* funcp = it->second.m_cfuncp;
AstNodeModule* modp = it->second.m_modp;
if (funcp->dpiExport()) {
if (funcp->dpiExportImpl()) {
checkSplit(true);
puts(protect("__Vscope_" + scopep->scopeSymName()) + ".exportInsert(__Vfinal, ");
putsQuoted(funcp->cname()); // Not protected - user asked for import/export
puts(", (void*)(&");
puts(prefixNameProtect(modp));
puts("::");
puts("__");
puts(funcp->nameProtect());
puts("));\n");
++m_numStmts;
@ -758,7 +806,6 @@ void EmitCSyms::emitSymImp() {
// Someday. For now public isn't common.
for (auto it = m_scopeVars.begin(); it != m_scopeVars.end(); ++it) {
checkSplit(true);
AstNodeModule* modp = it->second.m_modp;
AstScope* scopep = it->second.m_scopep;
AstVar* varp = it->second.m_varp;
//
@ -810,11 +857,7 @@ void EmitCSyms::emitSymImp() {
putsQuoted(protect(it->second.m_varBasePretty));
std::string varName;
if (modp->isTop()) {
varName += (protectIf(scopep->nameDotless() + "p", scopep->protect()) + "->");
} else {
varName += (protectIf(scopep->nameDotless(), scopep->protect()) + ".");
}
varName += (protectIf(scopep->nameDotless(), scopep->protect()) + ".");
if (varp->isParam()) {
varName += protect("var_" + varp->name());
@ -853,6 +896,34 @@ void EmitCSyms::emitSymImp() {
}
m_ofpBase->puts("}\n");
if (v3Global.needTraceDumper()) {
if (!optSystemC()) {
puts("\nvoid " + symClassName() + "::_traceDump() {\n");
// Caller checked for __Vm_dumperp non-nullptr
puts("const VerilatedLockGuard lock(__Vm_dumperMutex);\n");
puts("__Vm_dumperp->dump(VL_TIME_Q());\n");
puts("}\n");
}
puts("\nvoid " + symClassName() + "::_traceDumpOpen() {\n");
puts("const VerilatedLockGuard lock(__Vm_dumperMutex);\n");
puts("if (VL_UNLIKELY(!__Vm_dumperp)) {\n");
puts("__Vm_dumperp = new " + v3Global.opt.traceClassLang() + "();\n");
puts("__Vm_modelp->trace(__Vm_dumperp, 0, 0);\n");
puts("std::string dumpfile = _vm_contextp__->dumpfileCheck();\n");
puts("__Vm_dumperp->open(dumpfile.c_str());\n");
puts("__Vm_dumping = true;\n");
puts("}\n");
puts("}\n");
puts("\nvoid " + symClassName() + "::_traceDumpClose() {\n");
puts("const VerilatedLockGuard lock(__Vm_dumperMutex);\n");
puts("__Vm_dumping = false;\n");
puts("VL_DO_CLEAR(delete __Vm_dumperp, __Vm_dumperp = nullptr);\n");
puts("}\n");
}
closeSplit();
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
}
@ -861,7 +932,7 @@ void EmitCSyms::emitSymImp() {
void EmitCSyms::emitDpiHdr() {
UINFO(6, __FUNCTION__ << ": " << endl);
string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__Dpi.h";
const string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__Dpi.h";
AstCFile* cfilep = newCFile(filename, false /*slow*/, false /*source*/);
cfilep->support(true);
V3OutCFile hf(filename);
@ -885,13 +956,13 @@ void EmitCSyms::emitDpiHdr() {
int firstExp = 0;
int firstImp = 0;
for (AstCFunc* nodep : m_dpis) {
if (nodep->dpiExportWrapper()) {
if (nodep->dpiExportDispatcher()) {
if (!firstExp++) puts("\n// DPI EXPORTS\n");
putsDecoration("// DPI export" + ifNoProtect(" at " + nodep->fileline()->ascii())
+ "\n");
puts("extern " + nodep->rtnTypeVoid() + " " + nodep->nameProtect() + "("
+ cFuncArgs(nodep) + ");\n");
} else if (nodep->dpiImport()) {
} else if (nodep->dpiImportPrototype()) {
if (!firstImp++) puts("\n// DPI IMPORTS\n");
putsDecoration("// DPI import" + ifNoProtect(" at " + nodep->fileline()->ascii())
+ "\n");
@ -910,7 +981,7 @@ void EmitCSyms::emitDpiHdr() {
void EmitCSyms::emitDpiImp() {
UINFO(6, __FUNCTION__ << ": " << endl);
string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__Dpi.cpp";
const string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__Dpi.cpp";
AstCFile* cfilep = newCFile(filename, false /*slow*/, true /*source*/);
cfilep->support(true);
V3OutCFile hf(filename);
@ -936,7 +1007,7 @@ void EmitCSyms::emitDpiImp() {
puts("\n");
for (AstCFunc* nodep : m_dpis) {
if (nodep->dpiExportWrapper()) {
if (nodep->dpiExportDispatcher()) {
// Prevent multi-definition if used by multiple models
puts("#ifndef VL_DPIDECL_" + nodep->name() + "_\n");
puts("#define VL_DPIDECL_" + nodep->name() + "_\n");

View File

@ -168,6 +168,8 @@ public:
}
of.puts("\n### Switches...\n");
of.puts("# C++ code coverage 0/1 (from --prof-c)\n");
of.puts(string("VM_PROFC = ") + ((v3Global.opt.profC()) ? "1" : "0") + "\n");
of.puts("# SystemC output mode? 0/1 (from --sc)\n");
of.puts(string("VM_SC = ") + ((v3Global.opt.systemC()) ? "1" : "0") + "\n");
of.puts("# Legacy or SystemC output mode? 0/1 (from --sc)\n");
@ -203,7 +205,7 @@ public:
const V3StringSet& cppFiles = v3Global.opt.cppFiles();
for (const auto& cppfile : cppFiles) {
of.puts("\t" + V3Os::filenameNonExt(cppfile) + " \\\n");
string dir = V3Os::filenameDir(cppfile);
const string dir = V3Os::filenameDir(cppfile);
dirs.insert(dir);
}
of.puts("\n");
@ -228,7 +230,7 @@ public:
of.puts("VPATH += $(VM_USER_DIR)\n");
of.puts("\n");
for (const string& cppfile : cppFiles) {
string basename = V3Os::filenameNonExt(cppfile);
const string basename = V3Os::filenameNonExt(cppfile);
// NOLINTNEXTLINE(performance-inefficient-string-concatenation)
of.puts(basename + ".o: " + cppfile + "\n");
of.puts("\t$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -c -o $@ $<\n");

View File

@ -624,9 +624,12 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
if (nodep->varScopep()) {
putfs(nodep, nodep->varScopep()->prettyName());
} else {
putfs(nodep, nodep->hiernameToUnprot());
puts(nodep->hiernameToProt());
puts(nodep->varp()->prettyName());
if (nodep->selfPointer().empty()) {
putfs(nodep, nodep->varp()->prettyName());
} else {
putfs(nodep, nodep->selfPointer() + "->");
puts(nodep->varp()->prettyName());
}
}
}
virtual void visit(AstVarXRef* nodep) override {
@ -847,7 +850,8 @@ void V3EmitV::emitvFiles() {
void V3EmitV::debugEmitV(const string& stage) {
UINFO(2, __FUNCTION__ << ": " << endl);
string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__" + stage + ".v";
const string filename
= v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__" + stage + ".v";
V3OutVFile of(filename);
EmitVFileVisitor visitor(v3Global.rootp(), &of, true, true);
}

View File

@ -117,6 +117,7 @@ class EmitXmlFileVisitor final : public AstNVisitor {
iterateChildren(nodep);
puts("</netlist>\n");
}
virtual void visit(AstConstPool*) override {}
virtual void visit(AstNodeModule* nodep) override {
outputTag(nodep, "");
puts(" origName=");
@ -128,13 +129,14 @@ class EmitXmlFileVisitor final : public AstNVisitor {
outputChildrenEnd(nodep, "");
}
virtual void visit(AstVar* nodep) override {
AstVarType typ = nodep->varType();
string kw = nodep->verilogKwd();
string vt = nodep->dtypep()->name();
const AstVarType typ = nodep->varType();
const string kw = nodep->verilogKwd();
const string vt = nodep->dtypep()->name();
outputTag(nodep, "");
if (nodep->isIO()) {
puts(" dir=");
putsQuoted(kw);
if (nodep->pinNum() != 0) puts(" pinIndex=\"" + cvtToStr(nodep->pinNum()) + "\"");
puts(" vartype=");
putsQuoted(!vt.empty() ? vt : typ == AstVarType::PORT ? "port" : "unknown");
} else {
@ -181,7 +183,7 @@ class EmitXmlFileVisitor final : public AstNVisitor {
}
virtual void visit(AstModportVarRef* nodep) override {
// Dump direction for Modport references
string kw = nodep->direction().xmlKwd();
const string kw = nodep->direction().xmlKwd();
outputTag(nodep, "");
puts(" direction=");
putsQuoted(kw);
@ -316,15 +318,17 @@ private:
VL_DEBUG_FUNC; // Declare debug()
// 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
m_os << "<cells>\n";
m_os << "<cell " << nodep->fileline()->xml() << " "
<< nodep->fileline()->xmlDetailedLocation() << " name=\"" << nodep->name() << "\""
<< " submodname=\"" << nodep->name() << "\""
<< " hier=\"" << nodep->name() << "\"";
m_hier = nodep->name() + ".";
<< nodep->fileline()->xmlDetailedLocation() //
<< " name=\"" << nodep->prettyName() << "\""
<< " submodname=\"" << nodep->prettyName() << "\""
<< " hier=\"" << nodep->prettyName() << "\"";
m_hier = nodep->prettyName() + ".";
m_hasChildren = false;
iterateChildren(nodep);
if (m_hasChildren) {
@ -373,9 +377,9 @@ public:
void V3EmitXml::emitxml() {
UINFO(2, __FUNCTION__ << ": " << endl);
// All-in-one file
string filename = (v3Global.opt.xmlOutput().empty()
? v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + ".xml"
: v3Global.opt.xmlOutput());
const string filename = (v3Global.opt.xmlOutput().empty()
? v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + ".xml"
: v3Global.opt.xmlOutput());
V3OutXmlFile of(filename);
of.putsHeader();
of.puts("<!-- DESCR"

View File

@ -97,7 +97,7 @@ void V3Error::incErrors() {
}
void V3Error::abortIfWarnings() {
bool exwarn = warnFatal() && warnCount();
const bool exwarn = warnFatal() && warnCount();
if (errorCount() && exwarn) {
v3fatalExit("Exiting due to " << std::dec << errorCount() << " error(s), " //
<< warnCount() << " warning(s)\n");
@ -132,7 +132,7 @@ bool V3Error::isError(V3ErrorCode code, bool supp) {
string V3Error::msgPrefix() {
V3ErrorCode code = s_errorCode;
bool supp = s_errorSuppressed;
const bool supp = s_errorSuppressed;
if (supp) {
return "-arning-suppressed: ";
} else if (code == V3ErrorCode::USERINFO) {
@ -210,8 +210,8 @@ void V3Error::v3errorEnd(std::ostringstream& sstr, const string& locationStr) {
if (s_messages.find(msg) != s_messages.end()) return;
s_messages.insert(msg);
if (!locationStr.empty()) {
string locationMsg = warnMore() + locationStr + "\n";
size_t pos = msg.find('\n');
const string locationMsg = warnMore() + locationStr + "\n";
const size_t pos = msg.find('\n');
msg.insert(pos + 1, locationMsg);
}
// Output

View File

@ -149,11 +149,11 @@ private:
AstNode* newp;
// Negative word numbers requested for lhs when it's "before" what we want.
// We get a 0 then.
int othword = word - shift / VL_EDATASIZE;
const int othword = word - shift / VL_EDATASIZE;
AstNode* llowp = newAstWordSelClone(lhsp, othword);
if (int loffset = VL_BITBIT_E(shift)) {
if (const int loffset = VL_BITBIT_E(shift)) {
AstNode* lhip = newAstWordSelClone(lhsp, othword - 1);
int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword
const int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword
newp = new AstOr(
fl,
new AstAnd(fl, new AstConst(fl, AstConst::SizedEData(), VL_MASK_E(loffset)),
@ -344,6 +344,7 @@ private:
// See under ASSIGN(WIDE)
} else if (nodep->fromp()->isWide()) {
UINFO(8, " SEL(wide) " << nodep << endl);
UASSERT_OBJ(nodep->widthConst() <= 64, nodep, "Inconsistent width");
// Selection amounts
// Check for constant shifts & save some constification work later.
// Grab lowest bit(s)
@ -358,60 +359,65 @@ private:
AstNode* midp = nullptr;
V3Number zero(nodep, longOrQuadWidth(nodep));
if (nodep->widthConst() > 1) {
AstNode* midwordp = // SEL(from,[1+wordnum])
const uint32_t midMsbOffset
= std::min<uint32_t>(nodep->widthConst(), VL_EDATASIZE) - 1;
AstNode* const midMsbp
= new AstAdd(nodep->lsbp()->fileline(),
new AstConst(nodep->lsbp()->fileline(), midMsbOffset),
nodep->lsbp()->cloneTree(false));
AstNode* midwordp = // SEL(from,[midwordnum])
newWordSel(nodep->fromp()->fileline(), nodep->fromp()->cloneTree(true),
nodep->lsbp(), 1);
midMsbp, 0);
// newWordSel clones the index, so delete it
VL_DO_DANGLING(midMsbp->deleteTree(), midMsbp);
if (nodep->isQuad() && !midwordp->isQuad()) {
midwordp = new AstCCast(nodep->fileline(), midwordp, nodep);
}
// If we're selecting bit zero, then all 32 bits in word 1
// get shifted << by 32 bits
// else we need to form the lower word, so we << by 31 or less
// nbitsfromlow <= (lsb==0) ? 64-bitbit(lsb) : 32-bitbit(lsb)
AstNode* midshiftp
AstNode* const midshiftp
= new AstSub(nodep->lsbp()->fileline(),
new AstConst(nodep->lsbp()->fileline(), VL_EDATASIZE),
newSelBitBit(nodep->lsbp()));
if (nodep->isQuad()) {
midshiftp = new AstCond(
nodep->fileline(),
new AstEq(nodep->fileline(), new AstConst(nodep->fileline(), 0),
newSelBitBit(nodep->lsbp())),
new AstConst(nodep->lsbp()->fileline(), VL_EDATASIZE), midshiftp);
}
AstNode* midmayp
= new AstShiftL(nodep->fileline(), midwordp, midshiftp, nodep->width());
if (nodep->isQuad()) {
midp = midmayp; // Always grab from two words
} else {
midp = new AstCond(nodep->fileline(),
new AstEq(nodep->fileline(),
new AstConst(nodep->fileline(), 0),
newSelBitBit(nodep->lsbp())),
new AstConst(nodep->fileline(), zero), midmayp);
}
// If we're selecting bit zero, then all 32 bits in the mid word
// get shifted << by 32 bits, so ignore them.
midp = new AstCond(
nodep->fileline(),
// lsb % VL_EDATASIZE == 0 ?
new AstEq(nodep->fileline(), new AstConst(nodep->fileline(), 0),
newSelBitBit(nodep->lsbp())),
// 0 :
new AstConst(nodep->fileline(), zero),
// midword >> (VL_EDATASIZE - (lbs % VL_EDATASIZE))
new AstShiftL(nodep->fileline(), midwordp, midshiftp, nodep->width()));
}
// If > 32 bits, we might be crossing the second word boundary
AstNode* hip = nullptr;
if (nodep->widthConst() > VL_EDATASIZE) {
AstNode* hiwordp = // SEL(from,[2+wordnum])
newWordSel(nodep->fromp()->fileline(), nodep->fromp()->cloneTree(true),
nodep->lsbp(), 2);
const uint32_t hiMsbOffset = nodep->widthConst() - 1;
AstNode* const hiMsbp
= new AstAdd(nodep->lsbp()->fileline(),
new AstConst(nodep->lsbp()->fileline(), hiMsbOffset),
nodep->lsbp()->cloneTree(false));
AstNode* hiwordp = // SEL(from,[hiwordnum])
newWordSel(nodep->fromp()->fileline(), nodep->fromp()->cloneTree(true), hiMsbp,
0);
// newWordSel clones the index, so delete it
VL_DO_DANGLING(hiMsbp->deleteTree(), hiMsbp);
if (nodep->isQuad() && !hiwordp->isQuad()) {
hiwordp = new AstCCast(nodep->fileline(), hiwordp, nodep);
}
AstNode* himayp
= new AstShiftL(nodep->fileline(), hiwordp,
// nbitsfromlow_and_mid <= 64-bitbit(lsb)
new AstSub(nodep->lsbp()->fileline(),
new AstConst(nodep->lsbp()->fileline(), 64),
newSelBitBit(nodep->lsbp())),
nodep->width());
// if (frombit==0) then ignore, else use it
hip = new AstCond(nodep->fileline(),
AstNode* const hishiftp
= new AstCond(nodep->fileline(),
// lsb % VL_EDATASIZE == 0 ?
new AstEq(nodep->fileline(), new AstConst(nodep->fileline(), 0),
newSelBitBit(nodep->lsbp())),
new AstConst(nodep->fileline(), zero), himayp);
// VL_EDATASIZE :
new AstConst(nodep->lsbp()->fileline(), VL_EDATASIZE),
// 64 - (lbs % VL_EDATASIZE)
new AstSub(nodep->lsbp()->fileline(),
new AstConst(nodep->lsbp()->fileline(), 64),
newSelBitBit(nodep->lsbp())));
hip = new AstShiftL(nodep->fileline(), hiwordp, hishiftp, nodep->width());
}
AstNode* newp = lowp;
@ -442,7 +448,7 @@ private:
UASSERT_OBJ(nodep->widthMin() == rhsp->widthConst(), nodep, "Width mismatch");
if (!doExpand(nodep)) return false;
if (VN_IS(rhsp->lsbp(), Const) && VL_BITBIT_E(rhsp->lsbConst()) == 0) {
int lsb = rhsp->lsbConst();
const int lsb = rhsp->lsbConst();
UINFO(8, " Wordize ASSIGN(SEL,align) " << nodep << endl);
for (int w = 0; w < nodep->widthWords(); w++) {
addWordAssign(nodep, w, newAstWordSelClone(rhsp->fromp(), w + VL_BITWORD_E(lsb)));
@ -485,15 +491,15 @@ private:
// rhsp: may be allones and can remove AND NOT gate
// lsbp: constant or variable
// Yuk.
bool destwide = lhsp->fromp()->isWide();
bool ones = nodep->rhsp()->isAllOnesV();
const bool destwide = lhsp->fromp()->isWide();
const bool ones = nodep->rhsp()->isAllOnesV();
if (VN_IS(lhsp->lsbp(), Const)) {
// The code should work without this constant test, but it won't
// constify as nicely as we'd like.
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
AstNode* destp = lhsp->fromp()->unlinkFrBack();
int lsb = lhsp->lsbConst();
int msb = lhsp->msbConst();
const int lsb = lhsp->lsbConst();
const int msb = lhsp->msbConst();
V3Number maskset(nodep, destp->widthMin());
for (int bit = lsb; bit < (msb + 1); bit++) maskset.setBit(bit, 1);
V3Number maskold(nodep, destp->widthMin());
@ -653,7 +659,7 @@ private:
UINFO(8, " CONCAT " << nodep << endl);
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
int rhsshift = rhsp->widthMin();
const int rhsshift = rhsp->widthMin();
if (nodep->isQuad() && !lhsp->isQuad()) {
lhsp = new AstCCast(nodep->fileline(), lhsp, nodep);
}
@ -674,7 +680,7 @@ private:
if (!doExpand(rhsp)) return false;
// Lhs or Rhs may be word, long, or quad.
// newAstWordSelClone nicely abstracts the difference.
int rhsshift = rhsp->rhsp()->widthMin();
const int rhsshift = rhsp->rhsp()->widthMin();
// Sometimes doing the words backwards is preferable.
// When we have x={x,foo} backwards is better, when x={foo,x} forward is better
// However V3Subst tends to rip this up, so not worth optimizing now.
@ -695,7 +701,7 @@ private:
} else {
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
AstNode* newp;
int lhswidth = lhsp->widthMin();
const int lhswidth = lhsp->widthMin();
if (lhswidth == 1) {
UINFO(8, " REPLICATE(w1) " << nodep << endl);
newp = new AstNegate(nodep->fileline(), lhsp);
@ -710,7 +716,7 @@ private:
}
newp = lhsp->cloneTree(true);
for (unsigned repnum = 1; repnum < times; repnum++) {
int rhsshift = repnum * lhswidth;
const int rhsshift = repnum * lhswidth;
newp = new AstOr(nodep->fileline(),
new AstShiftL(nodep->fileline(), lhsp->cloneTree(true),
new AstConst(nodep->fileline(), rhsshift),
@ -728,7 +734,7 @@ private:
UINFO(8, " Wordize ASSIGN(REPLICATE) " << nodep << endl);
if (!doExpand(rhsp)) return false;
AstNode* lhsp = rhsp->lhsp();
int lhswidth = lhsp->widthMin();
const int lhswidth = lhsp->widthMin();
const AstConst* constp = VN_CAST(rhsp->rhsp(), Const);
UASSERT_OBJ(constp, rhsp, "Replication value isn't a constant. Checked earlier!");
uint32_t times = constp->toUInt();

View File

@ -89,8 +89,8 @@ class V3FileDependImp final {
time_t mnstime() const { return VL_STAT_MTIME_NSEC(m_stat); } // Nanoseconds
void loadStats() {
if (!m_stat.st_mtime) {
string fn = filename();
int err = stat(fn.c_str(), &m_stat);
const string fn = filename();
const int err = stat(fn.c_str(), &m_stat);
if (err != 0) {
memset(&m_stat, 0, sizeof(m_stat));
m_stat.st_mtime = 1;
@ -181,7 +181,7 @@ inline void V3FileDependImp::writeTimes(const string& filename, const string& cm
const std::unique_ptr<std::ofstream> ofp(V3File::new_ofstream(filename));
if (ofp->fail()) v3fatal("Can't write " << filename);
string cmdline = stripQuotes(cmdlineIn);
const string cmdline = stripQuotes(cmdlineIn);
*ofp << "# DESCR"
<< "IPTION: Verilator output: Timestamp data for --skip-identical. Delete at will.\n";
*ofp << "C \"" << cmdline << "\"\n";
@ -219,7 +219,7 @@ inline bool V3FileDependImp::checkTimes(const string& filename, const string& cm
return false;
}
{
string ignore = V3Os::getline(*ifp);
const string ignore = V3Os::getline(*ifp);
if (ignore.empty()) { /*used*/
}
}
@ -228,8 +228,8 @@ inline bool V3FileDependImp::checkTimes(const string& filename, const string& cm
*ifp >> chkDir;
char quote;
*ifp >> quote;
string chkCmdline = V3Os::getline(*ifp, '"');
string cmdline = stripQuotes(cmdlineIn);
const string chkCmdline = V3Os::getline(*ifp, '"');
const string cmdline = stripQuotes(cmdlineIn);
if (cmdline != chkCmdline) {
UINFO(2, " --check-times failed: different command line\n");
return false;
@ -254,12 +254,12 @@ inline bool V3FileDependImp::checkTimes(const string& filename, const string& cm
*ifp >> chkMnstime;
char quote;
*ifp >> quote;
string chkFilename = V3Os::getline(*ifp, '"');
const string chkFilename = V3Os::getline(*ifp, '"');
V3Options::fileNfsFlush(chkFilename);
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
struct stat chkStat;
int err = stat(chkFilename.c_str(), &chkStat);
const int err = stat(chkFilename.c_str(), &chkStat);
if (err != 0) {
UINFO(2, " --check-times failed: missing " << chkFilename << endl);
return false;
@ -354,7 +354,7 @@ private:
}
}
bool readContentsFile(const string& filename, StrList& outl) {
int fd = open(filename.c_str(), O_RDONLY);
const int fd = open(filename.c_str(), O_RDONLY);
if (fd < 0) return false;
m_readEof = false;
readBlocks(fd, -1, outl);
@ -365,7 +365,7 @@ private:
if (filename != "" || outl.empty()) {} // Prevent unused
#ifdef INFILTER_PIPE
writeFilter("read \"" + filename + "\"\n");
string line = readFilterLine();
const string line = readFilterLine();
if (line.find("Content-Length") != string::npos) {
int len = 0;
sscanf(line.c_str(), "Content-Length: %d\n", &len);
@ -402,7 +402,7 @@ private:
ssize_t todo = INFILTER_IPC_BUFSIZ;
if (size > 0 && size < todo) todo = size;
errno = 0;
ssize_t got = read(fd, buf, todo);
const ssize_t got = read(fd, buf, todo);
// UINFO(9,"RD GOT g "<< got<<" e "<<errno<<" "<<strerror(errno)<<endl);
// usleep(50*1000);
if (got > 0) {
@ -431,7 +431,7 @@ private:
while (!m_readEof) {
StrList outl;
readBlocks(m_readFd, 1, outl);
string onechar = listString(outl);
const string onechar = listString(outl);
line += onechar;
if (onechar == "\n") {
if (line == "\n") {
@ -459,7 +459,7 @@ private:
unsigned offset = 0;
while (!m_readEof && out.length() > offset) {
errno = 0;
int got = write(m_writeFd, (out.c_str()) + offset, out.length() - offset);
const int got = write(m_writeFd, (out.c_str()) + offset, out.length() - offset);
// UINFO(9,"WR GOT g "<< got<<" e "<<errno<<" "<<strerror(errno)<<endl);
// usleep(50*1000);
if (got > 0) {
@ -506,7 +506,7 @@ private:
UINFO(1, "--pipe-filter: /bin/sh -c " << command << endl);
pid_t pid = fork();
const pid_t pid = fork();
if (pid < 0) v3fatal("--pipe-filter: fork failed: " << strerror(errno));
if (pid == 0) { // Child
UINFO(6, "In child\n");
@ -827,7 +827,7 @@ void V3OutFormatter::putsQuoted(const string& strg) {
// Quote \ and " for use inside C programs
// Don't use to quote a filename for #include - #include doesn't \ escape.
putcNoTracking('"');
string quoted = quoteNameControls(strg);
const string quoted = quoteNameControls(strg);
for (const char c : quoted) putcNoTracking(c);
putcNoTracking('"');
}
@ -894,8 +894,8 @@ string V3OutFormatter::quoteNameControls(const string& namein, V3OutFormatter::L
out += c;
} else {
// This will also cover \a etc
string octal = string("\\") + cvtToStr((c >> 6) & 3) + cvtToStr((c >> 3) & 7)
+ cvtToStr(c & 7);
const string octal = string("\\") + cvtToStr((c >> 6) & 3) + cvtToStr((c >> 3) & 7)
+ cvtToStr(c & 7);
out += octal;
}
}
@ -965,8 +965,8 @@ protected:
public:
VIdProtectImp() {
passthru("this");
passthru("TOPp");
passthru("vlTOPp");
passthru("TOP");
passthru("vlSelf");
passthru("vlSymsp");
}
~VIdProtectImp() = default;
@ -1003,7 +1003,7 @@ public:
out = "PS" + digest.digestSymbol();
// See if we can shrink the digest symbol to something smaller
for (size_t len = 6; len < out.size() - 3; len += 3) {
string tryout = out.substr(0, len);
const string tryout = out.substr(0, len);
if (m_newIdSet.find(tryout) == m_newIdSet.end()) {
out = tryout;
break;
@ -1016,8 +1016,7 @@ public:
}
}
string protectWordsIf(const string& old, bool doIt) {
// Split at " " (for traces), "." (for scopes), "->" (for scopes), "::" (for superclass
// reference)
// Split at " " (for traces), "." (for scopes), "->", "(", "&", ")" (for self pointers)
if (!(doIt && v3Global.opt.protectIds())) return old;
string out;
string::size_type start = 0;
@ -1029,7 +1028,9 @@ public:
trySep(old, start, " ", pos /*ref*/, separator /*ref*/);
trySep(old, start, ".", pos /*ref*/, separator /*ref*/);
trySep(old, start, "->", pos /*ref*/, separator /*ref*/);
trySep(old, start, "::", pos /*ref*/, separator /*ref*/);
trySep(old, start, "(", pos /*ref*/, separator /*ref*/);
trySep(old, start, "&", pos /*ref*/, separator /*ref*/);
trySep(old, start, ")", pos /*ref*/, separator /*ref*/);
if (pos == string::npos) break;
out += protectIf(old.substr(start, pos - start), true) + separator;
start = pos + separator.length();

View File

@ -165,6 +165,9 @@ public:
void blockDec() {
if (!m_parenVec.empty()) m_parenVec.pop();
}
void ensureNewLine() {
if (!m_nobreak) puts("\n");
}
// STATIC METHODS
static string indentSpaces(int num);
// Add escaped characters to strings

View File

@ -60,7 +60,7 @@ string FileLineSingleton::filenameLetters(int fileno) {
int FileLineSingleton::nameToNumber(const string& filename) {
const auto it = vlstd::as_const(m_namemap).find(filename);
if (VL_LIKELY(it != m_namemap.end())) return it->second;
int num = m_names.size();
const int num = m_names.size();
m_names.push_back(filename);
m_languages.push_back(V3LangCode::mostRecent());
m_namemap.emplace(filename, num);
@ -95,7 +95,7 @@ void VFileContent::pushText(const string& text) {
}
// Any leftover text is stored on largest line (might be "")
string leftover = m_lines.back() + text;
const string leftover = m_lines.back() + text;
m_lines.pop_back();
// Insert line-by-line
@ -125,7 +125,7 @@ string VFileContent::getLine(int lineno) const {
return "";
}
}
string text = m_lines[lineno];
const string text = m_lines[lineno];
UINFO(9, "Get Stream[ct" << m_id << "+" << lineno << "]: " << text);
return text;
}
@ -399,7 +399,7 @@ string FileLine::warnContext(bool secondary) const {
if (!v3Global.opt.context()) return "";
string out;
if (firstLineno() == lastLineno() && firstColumn()) {
string sourceLine = prettySource();
const string sourceLine = prettySource();
// Don't show super-long lines as can fill screen and unlikely to help user
if (!sourceLine.empty() && sourceLine.length() < SHOW_SOURCE_MAX_LENGTH
&& sourceLine.length() >= static_cast<size_t>(lastColumn() - 1)) {

View File

@ -571,7 +571,7 @@ void GateVisitor::optimizeSignals(bool allowMultiIn) {
if (logicVertexp->reducible()) {
// Can we eliminate?
GateOkVisitor okVisitor(logicp, vvertexp->isClock(), false);
bool multiInputs = okVisitor.rhsVarRefs().size() > 1;
const bool multiInputs = okVisitor.rhsVarRefs().size() > 1;
// Was it ok?
bool doit = okVisitor.isSimple();
if (doit && multiInputs) {

View File

@ -29,11 +29,7 @@
//######################################################################
// V3 Class -- top level
AstNetlist* V3Global::makeNetlist() {
AstNetlist* newp = new AstNetlist();
newp->addTypeTablep(new AstTypeTable(newp->fileline()));
return newp;
}
AstNetlist* V3Global::makeNetlist() { return new AstNetlist(); }
void V3Global::clear() {
#ifdef VL_LEAK_CHECK

View File

@ -98,7 +98,6 @@ class V3Global final {
bool m_assertScoped = false; // Tree is scoped
bool m_constRemoveXs = false; // Const needs to strip any Xs
// Experimenting with always requiring heavy, see (#2701)
bool m_needHeavy = true; // Need verilated_heavy.h include
bool m_needTraceDumper = false; // Need __Vm_dumperp in symbols
bool m_dpi = false; // Need __Dpi include files
bool m_useParallelBuild = false; // Use parallel build for model
@ -142,8 +141,6 @@ public:
void constRemoveXs(bool flag) { m_constRemoveXs = flag; }
string debugFilename(const string& nameComment, int newNumber = 0);
static string digitsFilename(int number);
bool needHeavy() const { return m_needHeavy; }
void needHeavy(bool flag) { m_needHeavy = flag; }
bool needTraceDumper() const { return m_needTraceDumper; }
void needTraceDumper(bool flag) { m_needTraceDumper = flag; }
bool dpi() const { return m_dpi; }

View File

@ -114,7 +114,7 @@ V3GraphEdge* V3GraphVertex::findConnectingEdgep(GraphWay way, const V3GraphVerte
// O(edges) linear search. Searches search both nodes' edge lists in
// parallel. The lists probably aren't _both_ huge, so this is
// unlikely to blow up even on fairly nasty graphs.
GraphWay inv = way.invert();
const GraphWay inv = way.invert();
V3GraphEdge* aedgep = this->beginp(way);
V3GraphEdge* bedgep = waywardp->beginp(inv);
while (aedgep && bedgep) {
@ -133,10 +133,10 @@ void V3GraphVertex::v3errorEnd(std::ostringstream& str) const {
nsstr << endl;
nsstr << "-vertex: " << this << endl;
}
if (!fileline()) {
V3Error::v3errorEnd(nsstr);
if (FileLine* const flp = fileline()) {
flp->v3errorEnd(nsstr);
} else {
fileline()->v3errorEnd(nsstr);
V3Error::v3errorEnd(nsstr);
}
}
void V3GraphVertex::v3errorEndFatal(std::ostringstream& str) const {
@ -340,7 +340,7 @@ void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const {
int n = 0;
string subgr;
for (auto it = subgraphs.cbegin(); it != subgraphs.cend(); ++it) {
string vertexSubgraph = it->first;
const string vertexSubgraph = it->first;
V3GraphVertex* vertexp = it->second;
numMap[vertexp] = n;
if (subgr != vertexSubgraph) {
@ -366,8 +366,8 @@ void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const {
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) {
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
if (edgep->weight()) {
int fromVnum = numMap[edgep->fromp()];
int toVnum = numMap[edgep->top()];
const int fromVnum = numMap[edgep->fromp()];
const int toVnum = numMap[edgep->top()];
*logp << "\tn" << edgep->fromp()->dotName() << fromVnum << " -> n"
<< edgep->top()->dotName() << toVnum
<< " ["

View File

@ -318,7 +318,7 @@ private:
// Foreach input state (NFA inputs of this DFA state)
for (int inIt : inputs) {
DfaInput input = inIt;
const DfaInput input = inIt;
UINFO(9, " ===" << ++i << "=======================\n");
UINFO(9, " On input " << cvtToHex(input.toNodep()) << endl);

View File

@ -134,10 +134,10 @@ public:
return (na() ? "yellow" : epsilon() ? "green" : "black");
}
virtual string dotLabel() const override {
return (na() ? ""
: epsilon() ? "e"
: complement() ? ("not " + cvtToStr(input().toInt()))
: cvtToStr(input().toInt()));
return (na() ? ""
: epsilon() ? "e"
: complement() ? ("not " + cvtToStr(input().toInt()))
: cvtToStr(input().toInt()));
}
virtual string dotStyle() const override { return (na() || cutable()) ? "dashed" : ""; }
bool epsilon() const { return input().toInt() == EPSILON().toInt(); }

View File

@ -73,7 +73,7 @@ GraphPathChecker::~GraphPathChecker() {
void GraphPathChecker::initHalfCriticalPaths(GraphWay way, bool checkOnly) {
GraphStreamUnordered order(m_graphp, way);
GraphWay rev = way.invert();
const GraphWay rev = way.invert();
while (const V3GraphVertex* vertexp = order.nextp()) {
unsigned critPathCost = 0;
for (V3GraphEdge* edgep = vertexp->beginp(rev); edgep; edgep = edgep->nextp(rev)) {

View File

@ -18,6 +18,7 @@
#include <functional>
#include <iomanip>
#include <iostream>
V3Hash::V3Hash(const std::string& val)
: m_value{static_cast<uint32_t>(std::hash<std::string>{}(val))} {}

View File

@ -26,16 +26,22 @@
class V3Hash final {
uint32_t m_value; // The 32-bit hash value.
inline static uint32_t combine(uint32_t a, uint32_t b) {
return a ^ (b + 0x9e3779b9 + (a << 6) + (a >> 2));
}
public:
// CONSTRUCTORS
V3Hash()
: m_value{0} {}
explicit V3Hash(uint32_t val)
: m_value{val + 0x9e3779b9} {} // This is the same as 'V3Hash() + val'
: m_value{val} {}
explicit V3Hash(int32_t val)
: m_value{static_cast<uint32_t>(val)} {}
explicit V3Hash(size_t val)
: m_value{static_cast<uint32_t>(val)} {}
explicit V3Hash(uint64_t val)
: m_value{combine(static_cast<uint32_t>(val), static_cast<uint32_t>(val >> 32))} {}
explicit V3Hash(int64_t val)
: m_value{combine(static_cast<uint32_t>(val), static_cast<uint32_t>(val >> 32))} {}
explicit V3Hash(const std::string& val);
// METHODS
@ -48,20 +54,12 @@ public:
bool operator<(const V3Hash& rh) const { return m_value < rh.m_value; }
// '+' combines hashes
V3Hash operator+(const V3Hash& that) const {
return V3Hash(m_value ^ (that.m_value + 0x9e3779b9 + (m_value << 6) + (m_value >> 2)));
template <class T> V3Hash operator+(T that) const {
return V3Hash(combine(m_value, V3Hash(that).m_value));
}
V3Hash operator+(uint32_t value) const { return *this + V3Hash(value); }
V3Hash operator+(int32_t value) const { return *this + V3Hash(value); }
V3Hash operator+(size_t value) const { return *this + V3Hash(value); }
V3Hash operator+(const std::string& value) const { return *this + V3Hash(value); }
// '+=' combines in place
V3Hash& operator+=(const V3Hash& that) { return *this = *this + that; }
V3Hash& operator+=(uint32_t value) { return *this += V3Hash(value); }
V3Hash& operator+=(int32_t value) { return *this += V3Hash(value); }
V3Hash& operator+=(size_t value) { return *this += V3Hash(value); }
V3Hash& operator+=(const std::string& that) { return *this += V3Hash(that); }
template <class T> V3Hash& operator+=(T that) { return *this = *this + that; }
};
std::ostream& operator<<(std::ostream& os, const V3Hash& rhs);

View File

@ -199,7 +199,7 @@ private:
iterateNull(nodep->varScopep());
} else {
iterateNull(nodep->varp());
m_hash += nodep->hiernameToProt();
m_hash += nodep->selfPointer();
}
});
}
@ -229,6 +229,11 @@ private:
m_hash += nodep->text();
});
}
virtual void visit(AstAddrOfCFunc* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
iterateNull(nodep->funcp());
});
}
//------------------------------------------------------------
// AstNodeStmt
@ -346,7 +351,17 @@ private:
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
virtual void visit(AstInitArray* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
// Hash unpacked array initializers by value, as the order of initializer nodes does not
// matter, and we want semantically equivalent initializers to map to the same hash.
AstUnpackArrayDType* const dtypep = VN_CAST(nodep->dtypep(), UnpackArrayDType);
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, /* hashChildren: */ !dtypep, [=]() {
if (dtypep) {
const uint32_t size = dtypep->elementsConst();
for (uint32_t n = 0; n < size; ++n) { //
iterateNull(nodep->getIndexDefaultedValuep(n));
}
}
});
}
virtual void visit(AstPragma* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
@ -364,7 +379,9 @@ private:
});
}
virtual void visit(AstCFunc* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->isLoose();
});
}
virtual void visit(AstVar* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {

View File

@ -309,7 +309,7 @@ bool V3HierBlockPlan::isHierBlock(const AstNodeModule* modp) const {
}
void V3HierBlockPlan::add(const AstNodeModule* modp, const std::vector<AstVar*>& gparams) {
iterator it = m_blocks.find(modp);
const iterator it = m_blocks.find(modp);
if (it == m_blocks.end()) {
V3HierBlock* hblockp = new V3HierBlock(modp, gparams);
UINFO(3, "Add " << modp->prettyNameQ() << " with " << gparams.size() << " parameters"
@ -319,9 +319,9 @@ void V3HierBlockPlan::add(const AstNodeModule* modp, const std::vector<AstVar*>&
}
void V3HierBlockPlan::registerUsage(const AstNodeModule* parentp, const AstNodeModule* childp) {
iterator parent = m_blocks.find(parentp);
const iterator parent = m_blocks.find(parentp);
UASSERT_OBJ(parent != m_blocks.end(), parentp, "must be added");
iterator child = m_blocks.find(childp);
const iterator child = m_blocks.find(childp);
if (child != m_blocks.end()) {
UINFO(3, "Found usage relation " << parentp->prettyNameQ() << " uses "
<< childp->prettyNameQ() << std::endl);

View File

@ -165,7 +165,7 @@ private:
virtual void visit(AstNodeAssign* nodep) override {
// Don't count assignments, as they'll likely flatten out
// Still need to iterate though to nullify VarXRefs
int oldcnt = m_modp->user4();
const int oldcnt = m_modp->user4();
iterateChildren(nodep);
m_modp->user4(oldcnt);
}
@ -191,8 +191,8 @@ private:
}
modp->user4(statements);
int allowed = modp->user2();
int refs = modp->user3();
const int allowed = modp->user2();
const int refs = modp->user3();
// Should we automatically inline this module?
// If --flatten is specified, then force everything to be inlined that can be.
@ -282,7 +282,7 @@ private:
nodep->unlinkFrBack();
m_modp->addInlinesp(nodep);
// Rename
string name = m_cellp->name() + "__DOT__" + nodep->name();
const string name = m_cellp->name() + "__DOT__" + nodep->name();
nodep->name(name);
UINFO(6, " Inline " << nodep << endl);
// Do CellInlines under this, but don't move them
@ -290,12 +290,12 @@ private:
}
virtual void visit(AstCell* nodep) override {
// Cell under the inline cell, need to rename to avoid conflicts
string name = m_cellp->name() + "__DOT__" + nodep->name();
const string name = m_cellp->name() + "__DOT__" + nodep->name();
nodep->name(name);
iterateChildren(nodep);
}
virtual void visit(AstClass* nodep) override {
string name = m_cellp->name() + "__DOT__" + nodep->name();
const string name = m_cellp->name() + "__DOT__" + nodep->name();
nodep->name(name);
iterateChildren(nodep);
}
@ -376,7 +376,7 @@ private:
}
// Variable under the inline cell, need to rename to avoid conflicts
// Also clear I/O bits, as it is now local.
string name = m_cellp->name() + "__DOT__" + nodep->name();
const string name = m_cellp->name() + "__DOT__" + nodep->name();
if (!nodep->isFuncLocal() && !nodep->isClassMember()) nodep->inlineAttrReset(name);
if (!m_cellp->isTrace()) nodep->trace(false);
if (debug() >= 9) nodep->dumpTree(cout, "varchanged:");
@ -415,7 +415,7 @@ private:
}
virtual void visit(AstVarXRef* nodep) override {
// Track what scope it was originally under so V3LinkDot can resolve it
string newdots = VString::dot(m_cellp->name(), ".", nodep->inlinedDots());
const string newdots = VString::dot(m_cellp->name(), ".", nodep->inlinedDots());
nodep->inlinedDots(newdots);
for (string tryname = nodep->dotted(); true;) {
if (m_renamedInterfaces.count(tryname)) {
@ -434,7 +434,7 @@ private:
}
virtual void visit(AstNodeFTaskRef* nodep) override {
// Track what scope it was originally under so V3LinkDot can resolve it
string newdots = VString::dot(m_cellp->name(), ".", nodep->inlinedDots());
const string newdots = VString::dot(m_cellp->name(), ".", nodep->inlinedDots());
nodep->inlinedDots(newdots);
if (m_renamedInterfaces.count(nodep->dotted())) {
nodep->dotted(m_cellp->name() + "__DOT__" + nodep->dotted());
@ -659,7 +659,7 @@ private:
AstCell* cellp;
if ((cellp = VN_CAST(fromVarp->user1p(), Cell)) || (cellp = irdtp->cellp())) {
varp->user1p(cellp);
string alias = m_scope + "__DOT__" + pinp->name();
const string alias = m_scope + "__DOT__" + pinp->name();
cellp->addIntfRefp(new AstIntfRef(pinp->fileline(), alias));
}
}

View File

@ -207,7 +207,7 @@ private:
AstUnpackArrayDType* arrdtype = VN_CAST(nodep->dtypep(), UnpackArrayDType);
AstNode* prevp = nullptr;
for (int i = arrdtype->lo(); i <= arrdtype->hi(); ++i) {
string varNewName = nodep->name() + "__BRA__" + cvtToStr(i) + "__KET__";
const string varNewName = nodep->name() + "__BRA__" + cvtToStr(i) + "__KET__";
UINFO(8, "VAR name insert " << varNewName << " " << nodep << endl);
if (!m_deModVars.find(varNewName)) {
AstIfaceRefDType* ifaceRefp
@ -246,15 +246,16 @@ private:
m_cellRangep = nodep->rangep();
AstVar* ifaceVarp = VN_CAST(nodep->nextp(), Var);
bool isIface = ifaceVarp && VN_IS(ifaceVarp->dtypep(), UnpackArrayDType)
&& VN_IS(VN_CAST(ifaceVarp->dtypep(), UnpackArrayDType)->subDTypep(),
IfaceRefDType);
const bool isIface
= ifaceVarp && VN_IS(ifaceVarp->dtypep(), UnpackArrayDType)
&& VN_IS(VN_CAST(ifaceVarp->dtypep(), UnpackArrayDType)->subDTypep(),
IfaceRefDType);
// Make all of the required clones
for (int i = 0; i < m_cellRangep->elementsConst(); i++) {
m_instSelNum
= m_cellRangep->littleEndian() ? (m_cellRangep->elementsConst() - 1 - i) : i;
int instNum = m_cellRangep->loConst() + i;
const int instNum = m_cellRangep->loConst() + i;
AstCell* newp = nodep->cloneTree(false);
nodep->addNextHere(newp);
@ -316,8 +317,8 @@ private:
if (!nodep->exprp()) return; // No-connect
if (m_cellRangep) {
UINFO(4, " PIN " << nodep << endl);
int pinwidth = nodep->modVarp()->width();
int expwidth = nodep->exprp()->width();
const int pinwidth = nodep->modVarp()->width();
const int expwidth = nodep->exprp()->width();
std::pair<uint32_t, uint32_t> pinDim = nodep->modVarp()->dtypep()->dimensions(false);
std::pair<uint32_t, uint32_t> expDim = nodep->exprp()->dtypep()->dimensions(false);
UINFO(4, " PINVAR " << nodep->modVarp() << endl);
@ -328,9 +329,9 @@ private:
if (expDim.first == pinDim.first && expDim.second == pinDim.second + 1) {
// Connection to array, where array dimensions match the instant dimension
AstRange* rangep = VN_CAST(nodep->exprp()->dtypep(), UnpackArrayDType)->rangep();
int arraySelNum = rangep->littleEndian()
? (rangep->elementsConst() - 1 - m_instSelNum)
: m_instSelNum;
const int arraySelNum = rangep->littleEndian()
? (rangep->elementsConst() - 1 - m_instSelNum)
: m_instSelNum;
AstNode* exprp = nodep->exprp()->unlinkFrBack();
exprp = new AstArraySel(exprp->fileline(), exprp, arraySelNum);
nodep->exprp(exprp);
@ -346,7 +347,7 @@ private:
<< m_cellRangep->rightConst() << "]");
}
AstNode* exprp = nodep->exprp()->unlinkFrBack();
bool inputPin = nodep->modVarp()->isNonOutput();
const bool inputPin = nodep->modVarp()->isNonOutput();
if (!inputPin
&& !VN_IS(exprp, VarRef)
// V3Const will collapse the SEL with the one we're about to make
@ -373,7 +374,7 @@ private:
"Unsupported: Non-constant index when passing interface to module");
return;
}
string index = AstNode::encodeNumber(constp->toSInt());
const string index = AstNode::encodeNumber(constp->toSInt());
if (VN_IS(arrselp->lhsp(), SliceSel))
arrselp->lhsp()->v3error("Unsupported: interface slices");
AstVarRef* varrefp = VN_CAST(arrselp->lhsp(), VarRef);
@ -397,7 +398,7 @@ private:
// Clone pin varp:
for (int in = 0; in < pinArrp->elementsConst(); ++in) { // 0 = leftmost
int i = pinArrp->left() + in * pinArrp->declRange().leftToRightInc();
string varNewName = pinVarp->name() + "__BRA__" + cvtToStr(i) + "__KET__";
const string varNewName = pinVarp->name() + "__BRA__" + cvtToStr(i) + "__KET__";
AstVar* varNewp = nullptr;
// Only clone the var once for all usages of a given child module
@ -445,7 +446,7 @@ private:
expr_i = exprArrp->left() + in * exprArrp->declRange().leftToRightInc();
}
string newname = varrefp->name() + "__BRA__" + cvtToStr(expr_i) + "__KET__";
const string newname = varrefp->name() + "__BRA__" + cvtToStr(expr_i) + "__KET__";
AstVarXRef* newVarXRefp
= new AstVarXRef(nodep->fileline(), newname, "", VAccess::WRITE);
newVarXRefp->varp(newp->modVarp());

View File

@ -387,7 +387,7 @@ private:
// Just don't optimize blocks with labels; they're rare - so far.
LifeBlock* prevLifep = m_lifep;
LifeBlock* bodyLifep = new LifeBlock(prevLifep, m_statep);
bool prev_noopt = m_noopt;
const bool prev_noopt = m_noopt;
{
m_lifep = bodyLifep;
m_noopt = true;
@ -414,7 +414,7 @@ private:
// UINFO(4, " CFUNC " << nodep << endl);
if (!m_tracingCall && !nodep->entryPoint()) return;
m_tracingCall = false;
if (nodep->dpiImport() && !nodep->pure()) {
if (nodep->dpiImportPrototype() && !nodep->pure()) {
m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment
}
iterateChildren(nodep);

View File

@ -108,8 +108,8 @@ public:
: mtaskp{mtaskp_}
, sequence{sequence_} {}
bool operator<(const LifeLocation& b) const {
unsigned a_id = mtaskp ? mtaskp->id() : 0;
unsigned b_id = b.mtaskp ? b.mtaskp->id() : 0;
const unsigned a_id = mtaskp ? mtaskp->id() : 0;
const unsigned b_id = b.mtaskp ? b.mtaskp->id() : 0;
if (a_id < b_id) return true;
if (b_id < a_id) return false;
return sequence < b.sequence;

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