Merge from master for release.
This commit is contained in:
commit
fa3c072dd1
|
|
@ -16,7 +16,6 @@ env:
|
|||
CI_COMMIT: ${{ github.sha }}
|
||||
CCACHE_COMPRESS: 1
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_MAXSIZE: 2Gi # 2GiB for clang and gcc, 4GiB in total
|
||||
|
||||
jobs:
|
||||
|
||||
|
|
@ -31,7 +30,7 @@ jobs:
|
|||
- id: generate
|
||||
name: Run 'generate_matrix.sh'
|
||||
run: |
|
||||
if [ '${{ github.event_name}}' = 'pull_request' ]; then
|
||||
if [ '${{ github.event_name }}' = 'pull_request' ]; then
|
||||
matrix='[ "ubuntu-20.04" ]'
|
||||
else
|
||||
matrix='[ "ubuntu-16.04", "ubuntu-18.04", "ubuntu-20.04" ]'
|
||||
|
|
@ -57,7 +56,8 @@ jobs:
|
|||
CI_MAKE_SRC_TARGET: ${{ matrix.debug }}
|
||||
CC: ${{ matrix.compiler.cc }}
|
||||
CXX: ${{ matrix.compiler.cxx }}
|
||||
CACHE_KEY: ${{ matrix.os }}-${{ matrix.compiler.cc }}
|
||||
CACHE_KEY: build-${{ matrix.os }}-${{ matrix.compiler.cc }}-${{ matrix.debug }}
|
||||
CCACHE_MAXSIZE: 256Mi # Per build matrix entry
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
|
|
@ -77,7 +77,9 @@ jobs:
|
|||
|
||||
- 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
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
|
|
@ -89,7 +91,6 @@ jobs:
|
|||
needs: [ Matrix, Build ]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
max-parallel: 8
|
||||
matrix:
|
||||
os: ${{ fromJson(needs.Matrix.outputs.matrix) }}
|
||||
compiler:
|
||||
|
|
@ -98,6 +99,7 @@ jobs:
|
|||
suite:
|
||||
- dist-vlt-0
|
||||
- dist-vlt-1
|
||||
- dist-vlt-2
|
||||
- vltmt-0
|
||||
- vltmt-1
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
|
@ -107,7 +109,8 @@ jobs:
|
|||
CI_RUNS_ON: ${{ matrix.os }}
|
||||
CC: ${{ matrix.compiler.cc }}
|
||||
CXX: ${{ matrix.compiler.cxx }}
|
||||
CACHE_KEY: ${{ matrix.os }}-${{ matrix.compiler.cc }}
|
||||
CACHE_KEY: test-${{ matrix.os }}-${{ matrix.compiler.cc }}-${{ matrix.suite }}
|
||||
CCACHE_MAXSIZE: 256Mi # Per build matrix entry
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
|
|
@ -134,4 +137,7 @@ jobs:
|
|||
- name: Test
|
||||
env:
|
||||
TESTS: ${{ matrix.suite }}
|
||||
run: ./ci/ci-script.bash
|
||||
run: |
|
||||
ccache -z
|
||||
./ci/ci-script.bash
|
||||
ccache -s
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ env:
|
|||
CI_COMMIT: ${{ github.sha }}
|
||||
CCACHE_COMPRESS: 1
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_MAXSIZE: 2Gi # 2GiB for clang and gcc, 4GiB in total
|
||||
COVERAGE: 1
|
||||
|
||||
jobs:
|
||||
|
|
@ -24,7 +23,8 @@ jobs:
|
|||
env:
|
||||
CI_BUILD_STAGE_NAME: build
|
||||
CI_RUNS_ON: ubuntu-20.04
|
||||
CACHE_KEY: ubuntu-20.04-${{ matrix.compiler.cc }}-coverage
|
||||
CACHE_KEY: coverage-build
|
||||
CCACHE_MAXSIZE: 512Mi
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
|
|
@ -37,7 +37,7 @@ jobs:
|
|||
with:
|
||||
path: ${{ github.workspace }}/.ccache
|
||||
key: ${{ env.CACHE_KEY }}-${{ env.cache-name }}-${{ github.sha }}
|
||||
restore-keys: coverage-${{ env.cache-name }}
|
||||
restore-keys: ${{ env.CACHE_KEY }}-${{ env.cache-name }}
|
||||
|
||||
- name: Install packages for build
|
||||
env:
|
||||
|
|
@ -46,7 +46,9 @@ jobs:
|
|||
|
||||
- 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
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
|
|
@ -80,7 +82,8 @@ jobs:
|
|||
env:
|
||||
CI_BUILD_STAGE_NAME: test
|
||||
CI_RUNS_ON: ubuntu-20.04
|
||||
CACHE_KEY: ubuntu-20.04-${{ matrix.compiler.cc }}-coverage
|
||||
CACHE_KEY: coverage-test-${{ matrix.test }}${{ matrix.num }}
|
||||
CCACHE_MAXSIZE: 512Mi # Per build matrix entry
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
|
|
@ -93,7 +96,7 @@ jobs:
|
|||
with:
|
||||
path: ${{ github.workspace }}/.ccache
|
||||
key: ${{ env.CACHE_KEY }}-${{ env.cache-name }}-${{ github.sha }}
|
||||
restore-keys: coverage-${{ env.cache-name }}
|
||||
restore-keys: ${{ env.CACHE_KEY }}-${{ env.cache-name }}
|
||||
|
||||
- uses: actions/download-artifact@v2
|
||||
|
||||
|
|
@ -106,4 +109,7 @@ jobs:
|
|||
- name: Test
|
||||
env:
|
||||
TESTS: coverage-${{ matrix.test }}${{ matrix.num }}
|
||||
run: ./ci/ci-script.bash
|
||||
run: |
|
||||
ccache -z
|
||||
./ci/ci-script.bash
|
||||
ccache -s
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# DESCRIPTION: Github actions config
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
name: clang-format
|
||||
name: format
|
||||
|
||||
on:
|
||||
push:
|
||||
|
|
@ -38,6 +38,6 @@ jobs:
|
|||
- name: Push
|
||||
run: |
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
git commit . -m "Apply clang-format" &&
|
||||
git commit . -m "Apply 'make format'" &&
|
||||
git push origin
|
||||
fi
|
||||
37
Changes
37
Changes
|
|
@ -8,6 +8,41 @@ The changes in each Verilator version are described below. The
|
|||
contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
|
||||
Verilator 4.204 2021-06-12
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Add 'make ccache-report' (#3011). [Geza Lore]
|
||||
* Add --reloop-limit argument (#2943) (#2960). [Geza Lore]
|
||||
* Add --expand-limit argument (#3005). [Julien Margetts]
|
||||
* Add TRACE_THREADS to CMake (#2934). [Jonathan Drolet]
|
||||
* Optimize large lookup tables to static data (#2925). [Geza Lore]
|
||||
* Optimize reloop to accept constant index offsets (#2939). [Geza Lore]
|
||||
* Split always blocks to better respect --output-split-cfuncs. [Geza Lore]
|
||||
* Support ignoring "`pragma protect ..." (#2886). [Udi Finkelstein]
|
||||
* Support --trace-fst for SystemC with CMake (#2927). [Jonathan Drolet]
|
||||
* Update cmake latest C++ Standard Compilation flag (#2951). [Ameya Vikram Singh]
|
||||
* Prep work towards better ccache hashing/performance. [Geza Lore]
|
||||
* Fix assertion failure in bitOpTree optimization (#2891) (#2899). [Raynard Qiao]
|
||||
* Fix DPI functions not seen as vpiModule (#2893). [Todd Strader]
|
||||
* Fix bounds check in VL_SEL_IWII (#2910). [Krzysztof Bieganski]
|
||||
* Fix slowdown in elaboration (#2911). [Nathan Graybeal]
|
||||
* Fix initialization of assoc in assoc array (#2914). [myftptoyman]
|
||||
* Fix make support for gmake 3.x (#2920) (#2921). [Philipp Wagner]
|
||||
* Fix VPI memory access for packed arrays (#2922). [Todd Strader]
|
||||
* Fix MCD close also closing stdout (#2931). [Alexander Grobman]
|
||||
* Fix split procedures to better respect --output-split-cfuncs (#2942). [Geza Lore]
|
||||
* Fix to emit 'else if' without nesting (#2944). [Geza Lore]
|
||||
* Fix part select issues in LATCH warning (#2948) (#2938). [Julien Margetts]
|
||||
* Fix to not emit empty files with low split limits (#2961). [Geza Lore]
|
||||
* Fix merging of assignments in C++ code (#2970). [Ruper Swarbrick]
|
||||
* Fix unused variable warnings (#2991). [Pieter Kapsenberg]
|
||||
* Fix --protect-ids when using SV classes (#2994). [Geza Lore]
|
||||
* Fix constant function calls with uninit value (#2995). [yanx21]
|
||||
* Fix Makefiles to support Windows EXEEXT usage (#3008). [Miodrag Milanovic]
|
||||
|
||||
|
||||
Verilator 4.202 2021-04-24
|
||||
==========================
|
||||
|
||||
|
|
@ -331,7 +366,7 @@ Verilator 4.034 2020-05-03
|
|||
* Support event data type (with some restrictions).
|
||||
* Support $root. (#2150) [Keyi Zhang]
|
||||
* Add error if use SystemC 2.2 and earlier (pre-2011) as is deprecated.
|
||||
* Fix build of fast path tracing code to use OPT_FAST. (#2245) [Geza Lore]
|
||||
* Add support of --trace-structs for CMake (#2986). [Martin Schmidt]
|
||||
* Fix arrayed instances connecting to slices. (#2263) [Don/engr248]
|
||||
* Fix error on unpacked connecting to packed. (#2288) [Joseph Shaker]
|
||||
* Fix logical not optimization with empty begin. (#2291) [Baltazar Ortiz]
|
||||
|
|
|
|||
36
Makefile.in
36
Makefile.in
|
|
@ -44,6 +44,7 @@
|
|||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
HOST = @HOST@
|
||||
EXEEXT = @EXEEXT@
|
||||
|
||||
DOXYGEN = doxygen
|
||||
INSTALL = @INSTALL@
|
||||
|
|
@ -130,6 +131,7 @@ DISTFILES1 = $(INFOS) .gitignore \
|
|||
verilator-config.cmake.in \
|
||||
verilator-config-version.cmake.in \
|
||||
bin/verilator \
|
||||
bin/verilator_ccache_report \
|
||||
bin/verilator_coverage \
|
||||
bin/verilator_difftree \
|
||||
bin/verilator_gantt \
|
||||
|
|
@ -193,6 +195,7 @@ DISTFILES2 = \
|
|||
|
||||
INST_PROJ_FILES = \
|
||||
bin/verilator \
|
||||
bin/verilator_ccache_report \
|
||||
bin/verilator_coverage \
|
||||
bin/verilator_gantt \
|
||||
bin/verilator_includer \
|
||||
|
|
@ -203,9 +206,9 @@ INST_PROJ_FILES = \
|
|||
include/vltstd/*.[chv]* \
|
||||
|
||||
INST_PROJ_BIN_FILES = \
|
||||
bin/verilator_bin \
|
||||
bin/verilator_bin_dbg \
|
||||
bin/verilator_coverage_bin_dbg \
|
||||
bin/verilator_bin$(EXEEXT) \
|
||||
bin/verilator_bin_dbg$(EXEEXT) \
|
||||
bin/verilator_coverage_bin_dbg$(EXEEXT) \
|
||||
|
||||
EXAMPLES_FIRST = \
|
||||
examples/make_hello_c \
|
||||
|
|
@ -221,10 +224,10 @@ all: all_nomsg msg_test
|
|||
all_nomsg: verilator_exe $(VL_INST_MAN_FILES)
|
||||
|
||||
.PHONY:verilator_exe
|
||||
.PHONY:verilator_bin
|
||||
.PHONY:verilator_bin_dbg
|
||||
.PHONY:verilator_coverage_bin_dbg
|
||||
verilator_exe verilator_bin verilator_bin_dbg verilator_coverage_bin_dbg:
|
||||
.PHONY:verilator_bin$(EXEEXT)
|
||||
.PHONY:verilator_bin_dbg$(EXEEXT)
|
||||
.PHONY:verilator_coverage_bin_dbg$(EXEEXT)
|
||||
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)
|
||||
|
|
@ -280,8 +283,8 @@ verilator.pdf: Makefile
|
|||
$(MAKE) -C docs verilator.pdf
|
||||
|
||||
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
|
||||
VL_INST_BIN_FILES = verilator verilator_bin verilator_bin_dbg verilator_coverage_bin_dbg \
|
||||
verilator_coverage verilator_gantt verilator_includer verilator_profcfunc
|
||||
VL_INST_BIN_FILES = verilator verilator_bin$(EXEEXT) verilator_bin_dbg$(EXEEXT) verilator_coverage_bin_dbg$(EXEEXT) \
|
||||
verilator_ccache_report verilator_coverage verilator_gantt verilator_includer verilator_profcfunc
|
||||
# Some scripts go into both the search path and pkgdatadir,
|
||||
# so they can be found by the user, and under $VERILATOR_ROOT.
|
||||
|
||||
|
|
@ -305,11 +308,12 @@ installbin:
|
|||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_coverage $(DESTDIR)$(bindir)/verilator_coverage )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_gantt $(DESTDIR)$(bindir)/verilator_gantt )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_profcfunc $(DESTDIR)$(bindir)/verilator_profcfunc )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_bin $(DESTDIR)$(bindir)/verilator_bin )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_bin_dbg $(DESTDIR)$(bindir)/verilator_bin_dbg )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_coverage_bin_dbg $(DESTDIR)$(bindir)/verilator_coverage_bin_dbg )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_bin$(EXEEXT) $(DESTDIR)$(bindir)/verilator_bin$(EXEEXT) )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_bin_dbg$(EXEEXT) $(DESTDIR)$(bindir)/verilator_bin_dbg$(EXEEXT) )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_coverage_bin_dbg$(EXEEXT) $(DESTDIR)$(bindir)/verilator_coverage_bin_dbg$(EXEEXT) )
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/bin
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_includer $(DESTDIR)$(pkgdatadir)/bin/verilator_includer )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_ccache_report $(DESTDIR)$(pkgdatadir)/bin/verilator_ccache_report )
|
||||
|
||||
# Man files can either be part of the original kit, or built in current directory
|
||||
# So important we use $^ so VPATH is searched
|
||||
|
|
@ -458,7 +462,7 @@ clang-tidy: $(CLANGTIDY_DEP)
|
|||
|
||||
analyzer-src:
|
||||
-rm -rf src/obj_dbg
|
||||
scan-build $(MAKE) -k verilator_coverage_bin_dbg verilator_bin_dbg
|
||||
scan-build $(MAKE) -k verilator_coverage_bin_dbg$(EXEEXT) verilator_bin_dbg$(EXEEXT)
|
||||
|
||||
analyzer-include:
|
||||
-rm -rf examples/*/obj*
|
||||
|
|
@ -476,12 +480,12 @@ clang-format:
|
|||
$(CLANGFORMAT) $(CLANGFORMAT_FLAGS) $(CLANGFORMAT_FILES)
|
||||
|
||||
PY_PROGRAMS = \
|
||||
bin/verilator_ccache_report \
|
||||
examples/xml_py/vl_file_copy \
|
||||
examples/xml_py/vl_hier_graph \
|
||||
docs/guide/conf.py \
|
||||
docs/guide/vl_sphinx_extract \
|
||||
docs/guide/vl_sphinx_extract \
|
||||
docs/guide/vl_doxygen_filter \
|
||||
docs/bin/vl_sphinx_extract \
|
||||
docs/bin/vl_sphinx_fix \
|
||||
src/astgen \
|
||||
src/bisonpre \
|
||||
src/config_rev \
|
||||
|
|
|
|||
|
|
@ -315,6 +315,7 @@ detailed descriptions of these arguments.
|
|||
-E Preprocess, but do not compile
|
||||
--error-limit <value> Abort after this number of errors
|
||||
--exe Link to create executable
|
||||
--expand-limit <value> Set expand optimization limit
|
||||
-F <file> Parse arguments from a file, relatively
|
||||
-f <file> Parse arguments from a file
|
||||
-FI <file> Force include of a file
|
||||
|
|
@ -378,6 +379,7 @@ detailed descriptions of these arguments.
|
|||
--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
|
||||
--savable Enable model save-restore
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
#!/usr/bin/env python3
|
||||
# pylint: disable=C0103,C0114,C0115,C0116,C0123,C0301,R0902,R0913,R0914,R0912,R0915,W0621
|
||||
######################################################################
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
import pathlib
|
||||
import re
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
allow_abbrev=False,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description="""Report ccache behavior of a Verilated model build.""",
|
||||
epilog=
|
||||
"""Copyright 2002-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""")
|
||||
|
||||
parser.add_argument('-o',
|
||||
type=argparse.FileType('w'),
|
||||
metavar="OUTFILE",
|
||||
required=True,
|
||||
help='output file')
|
||||
parser.add_argument('logdir', type=pathlib.Path, help='log directory')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
results = {}
|
||||
elapsed = {}
|
||||
|
||||
|
||||
def toDateTime(s):
|
||||
return datetime.strptime(s, "%Y-%m-%dT%H:%M:%S.%f")
|
||||
|
||||
|
||||
for logfile in args.logdir.iterdir():
|
||||
with logfile.open() as fh:
|
||||
start = None
|
||||
obj = None
|
||||
for line in fh:
|
||||
line = line.strip()
|
||||
match = re.match(r'\[(\S+)\s.*Object file: (.*)$', line)
|
||||
if match:
|
||||
start = toDateTime(match.group(1))
|
||||
obj = match.group(2)
|
||||
match = re.match(r'\[(\S+)\s.*Result: (.*)$', line)
|
||||
if match:
|
||||
assert obj is not None
|
||||
elapsed[obj] = toDateTime(match.group(1)) - start
|
||||
results[obj] = match.group(2)
|
||||
|
||||
args.o.write("#" * 80 + "\n")
|
||||
args.o.write("ccache report (from verilator_ccache_report) :\n")
|
||||
|
||||
if not results:
|
||||
args.o.write("\nAll object files up to date\n")
|
||||
else:
|
||||
args.o.write("\nCompiled object files:\n")
|
||||
wnames = max(len(_) for _ in results) + 1
|
||||
wresults = max(len(_) for _ in results.values()) + 1
|
||||
for k in sorted(results.keys()):
|
||||
args.o.write("{:{wnames}} : {:{wresults}} : {}s\n".format(
|
||||
k,
|
||||
results[k],
|
||||
elapsed[k].total_seconds(),
|
||||
wnames=wnames,
|
||||
wresults=wresults))
|
||||
|
||||
args.o.write("\nSummary:\n")
|
||||
counts = collections.Counter(_ for _ in results.values())
|
||||
total = sum(counts.values())
|
||||
for k in sorted(counts.keys()):
|
||||
c = counts[k]
|
||||
args.o.write("{:{width}}| {} ({:.2%})\n".format(k,
|
||||
c,
|
||||
c / total,
|
||||
width=wresults))
|
||||
|
||||
args.o.write("\nLongest:\n")
|
||||
longest = sorted(list(elapsed.items()),
|
||||
key=lambda kv: -kv[1].total_seconds())
|
||||
for i, (k, v) in enumerate(longest):
|
||||
args.o.write("{:{width}}| {}s\n".format(k,
|
||||
v.total_seconds(),
|
||||
width=wnames))
|
||||
if i > 4: break
|
||||
|
||||
args.o.write("#" * 80 + "\n")
|
||||
|
|
@ -85,10 +85,13 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
|||
# Run the specified test
|
||||
case $TESTS in
|
||||
dist-vlt-0)
|
||||
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=0/2
|
||||
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=0/3
|
||||
;;
|
||||
dist-vlt-1)
|
||||
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=1/2
|
||||
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=1/3
|
||||
;;
|
||||
dist-vlt-2)
|
||||
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=2/3
|
||||
;;
|
||||
vltmt-0)
|
||||
"$MAKE" -C test_regress SCENARIOS=--vltmt DRIVER_HASHSET=--hashset=0/2
|
||||
|
|
|
|||
12
configure.ac
12
configure.ac
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[4.202 2021-04-24],
|
||||
AC_INIT([Verilator],[4.204 2021-06-12],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
# When releasing, also update header of Changes file
|
||||
|
|
@ -272,13 +272,15 @@ AC_DEFUN([_MY_LDLIBS_CHECK_OPT],
|
|||
|
||||
# Flag to select newest language standard supported
|
||||
# Macros work such that first option that passes is the one we take
|
||||
# gnu++17 code is clean, but SystemC in 2018 doesn't link with it (bug1339)
|
||||
# gnu++14 is the newest that Verilator supports
|
||||
# Currently enabled gnu++14/c++14 due to packaged SystemC dependency
|
||||
# gnu++17 is the newest that Verilator supports
|
||||
# std++03 is the oldest that Verilator supports
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++20)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++14)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++11)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++03)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++20)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++17)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++14)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++11)
|
||||
|
|
@ -289,11 +291,13 @@ AC_SUBST(CFG_CXXFLAGS_STD_NEWEST)
|
|||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++03)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++11)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++14)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=std++17)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++17)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++20)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++03)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++11)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++14)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++17)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++20)
|
||||
AC_SUBST(CFG_CXXFLAGS_STD_OLDEST)
|
||||
|
||||
# Flags for compiling Verilator internals including parser, and Verilated files
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ Please see the Verilator manual for 200+ additional contributors. Thanks to all.
|
|||
Ahmed El-Mahmoudy
|
||||
Alex Chadwick
|
||||
Àlex Torregrosa
|
||||
Ameya Vikram Singh
|
||||
Andreas Kuster
|
||||
Chris Randall
|
||||
Conor McCullough
|
||||
|
|
@ -38,6 +39,7 @@ Jean Berniolles
|
|||
Jeremy Bennett
|
||||
John Coiner
|
||||
John Demme
|
||||
Jonathan Drolet
|
||||
Josh Redford
|
||||
Julien Margetts
|
||||
Kaleb Barrett
|
||||
|
|
@ -53,9 +55,11 @@ Marco Widmer
|
|||
Markus Krause
|
||||
Marlon James
|
||||
Marshal Qiao
|
||||
Martin Schmidt
|
||||
Matthew Ballance
|
||||
Michael Killough
|
||||
Mike Popoloski
|
||||
Miodrag Milanović
|
||||
Morten Borup Petersen
|
||||
Nandu Raj
|
||||
Nathan Kohagen
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
# pylint: disable=C0112,C0114,C0116,C0301,R0903
|
||||
# pylint: disable=C0112,C0114,C0115,C0116,C0301,R0201,R0903
|
||||
# -*- Python -*- See copyright, etc below
|
||||
######################################################################
|
||||
|
||||
|
|
@ -8,6 +8,7 @@ import re
|
|||
|
||||
#######################################################################
|
||||
|
||||
|
||||
class VlSphinxExtract:
|
||||
debug = 0
|
||||
SkipBasenames = {}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
# pylint: disable=C0112,C0114,C0116,C0301,R0903
|
||||
# pylint: disable=C0112,C0114,C0115,C0116,C0301,R0903
|
||||
# -*- Python -*- See copyright, etc below
|
||||
######################################################################
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
.. comment: generated by t_lint_multidriven_bad
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 2,5
|
||||
|
||||
always @(posedge clk) begin
|
||||
out2[7:0] <= d0; // <--- Warning
|
||||
end
|
||||
always @(negedge clk) begin
|
||||
out2[15:8] <= d0; // <--- Warning
|
||||
end
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
.. comment: generated by t_lint_multidriven_bad
|
||||
.. code-block::
|
||||
|
||||
%Warning-MULTIDRIVEN: example.v:1:22 Signal has multiple driving blocks with different clocking: 'out2'
|
||||
example.v:1:7 ... Location of first driving block
|
||||
example.v:1:7 ... Location of other driving block
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# pylint: disable=E402
|
||||
# pylint: disable=C0103,C0114,C0116,E0402,W0622
|
||||
#
|
||||
# Configuration file for Verilator's Sphinx documentation builder.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
|
@ -12,11 +12,10 @@
|
|||
from datetime import datetime
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath('./_ext'))
|
||||
|
||||
import sphinx_rtd_theme
|
||||
import sphinx_rtd_theme # pylint: disable=wrong-import-position,
|
||||
|
||||
|
||||
def get_vlt_version():
|
||||
|
|
@ -94,7 +93,7 @@ today_fmt = datetime.now().strftime("%F")
|
|||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = True
|
||||
|
||||
# TODO could use this for internals<->guide references
|
||||
# Could use this for internals<->guide references
|
||||
# intersphinx_mapping = { 'sphinx': ('https://sphinx-doc.org/', None), }
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
|
@ -208,7 +207,7 @@ spelling_ignore_contributor_names = True
|
|||
# ----------------------------------------------------------------------
|
||||
# -- Options for doxygen
|
||||
|
||||
#if shutil.which("doxygen"):
|
||||
# if shutil.which("doxygen"):
|
||||
# breathe_projects = {
|
||||
# "verilated": "../_build/doxygen/verilated/xml",
|
||||
# }
|
||||
|
|
|
|||
|
|
@ -403,6 +403,12 @@ Summary:
|
|||
files on the command line that implement the main loop for your
|
||||
simulation.
|
||||
|
||||
.. option:: --expand-limit <value>
|
||||
|
||||
Rarely needed. Fine-tune optimizations to set the maximum size of an
|
||||
expression in 32-bit words to expand into separate word-based
|
||||
statements.
|
||||
|
||||
.. option:: -F <file>
|
||||
|
||||
Read the specified file, and act as if all text inside it was specified
|
||||
|
|
@ -418,8 +424,11 @@ Summary:
|
|||
fairly standard across Verilog tools.
|
||||
|
||||
The file may contain :code:`//` comments which are ignored to the end of
|
||||
the line. Any :code:`$VAR`, :code:`$(VAR)`, or :code:`${VAR}` will be
|
||||
replaced with the specified environment variable.
|
||||
the line. It may also contain :code:`/* .. */` comments which are
|
||||
ignored, be cautious that wildcards are not handled in -f files, and
|
||||
that :code:`directory/*` is the beginning of a comment, not a wildcard.
|
||||
Any :code:`$VAR`, :code:`$(VAR)`, or :code:`${VAR}` will be replaced
|
||||
with the specified environment variable.
|
||||
|
||||
.. option:: -FI <file>
|
||||
|
||||
|
|
@ -915,6 +924,15 @@ Summary:
|
|||
the path of the referencing file, instead of relative to the current
|
||||
directory.
|
||||
|
||||
.. option:: --reloop-limit
|
||||
|
||||
Rarely needed. Verilator attempts to turn some common sequences of
|
||||
statements into loops in the output. This argument specifies the minimum
|
||||
number of iterations the resulting loop needs to have in order to perform
|
||||
this transformation. Default limit is 40. A smaller number may slightly
|
||||
improve C++ compilation time on designs where these sequences are common,
|
||||
however effect on model performance requires benchmarking.
|
||||
|
||||
.. option:: --report-unoptflat
|
||||
|
||||
Extra diagnostics for UNOPTFLAT warnings. This includes for each loop,
|
||||
|
|
|
|||
|
|
@ -118,9 +118,9 @@ A. Pass the :vlopt:`--trace` option to Verilator, and in your top level C
|
|||
|
||||
B. Or, for finer-grained control, or C++ files with multiple Verilated
|
||||
modules you may also create the trace purely from C++. Create a
|
||||
VerilatedVcdC object, and in your main loop call
|
||||
``trace_object->dump(time)`` every time step, and finally call
|
||||
``trace_object->close()``.
|
||||
VerilatedVcdC object, and in your main loop right after ``eval()`` call
|
||||
``trace_object->dump(contextp->time())`` every time step, and finally
|
||||
call ``trace_object->close()``.
|
||||
|
||||
.. code-block:: C++
|
||||
:emphasize-lines: 1,5-8,12
|
||||
|
|
@ -128,15 +128,17 @@ B. Or, for finer-grained control, or C++ files with multiple Verilated
|
|||
#include "verilated_vcd_c.h"
|
||||
...
|
||||
int main(int argc, char** argv, char** env) {
|
||||
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
|
||||
...
|
||||
Verilated::traceEverOn(true);
|
||||
VerilatedVcdC* tfp = new VerilatedVcdC;
|
||||
topp->trace(tfp, 99); // Trace 99 levels of hierarchy
|
||||
tfp->open("obj_dir/t_trace_ena_cc/simx.vcd");
|
||||
...
|
||||
while (Verilated::time() < sim_time && !Verilated::gotFinish()) {
|
||||
Verilated::timeInc(1);
|
||||
tfp->dump(main_time);
|
||||
while (contextp->time() < sim_time && !contextp->gotFinish()) {
|
||||
contextp->timeInc(1);
|
||||
topp->eval();
|
||||
tfp->dump(contextp->time());
|
||||
}
|
||||
tfp->close();
|
||||
}
|
||||
|
|
@ -385,7 +387,7 @@ How do I get faster build times?
|
|||
identical source builds, even across different users. If ccache was
|
||||
installed when Verilator was built it is used, or see OBJCACHE
|
||||
environment variable to override this. Also see the
|
||||
:vlopt:`--output-split` option.
|
||||
:vlopt:`--output-split` option and :ref: `Profiling ccache efficiency`
|
||||
|
||||
* To reduce the compile time of classes that use a Verilated module (e.g. a
|
||||
top CPP file) you may wish to add a
|
||||
|
|
|
|||
|
|
@ -316,6 +316,35 @@ statistics.
|
|||
|
||||
For more information see :command:`verilator_gantt`.
|
||||
|
||||
.. _Profiling ccache efficiency:
|
||||
|
||||
Profiling ccache efficiency
|
||||
===========================
|
||||
|
||||
The Verilator generated Makefile provides support for basic profiling of
|
||||
ccache behavior during the build. This can be used to track down files that
|
||||
might be unnecessarily rebuilt, though as of today even small code changes
|
||||
will usually require rebuilding a large number of files. Improving ccache
|
||||
efficiency during the edit/compile/test loop is an active area of
|
||||
development.
|
||||
|
||||
To get a basic report of how well ccache is doing, add the `ccache-report`
|
||||
target when invoking the generated Makefile:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
make -C obj_dir -f Vout.mk Vout ccache-report
|
||||
|
||||
This will print a report based on all executions of ccache during this
|
||||
invocation of Make. The report is also written to a file, in this example
|
||||
`obj_dir/Vout__cache_report.txt`.
|
||||
|
||||
To use the `ccache-report` target, at least one other explicit build target
|
||||
must be specified, and OBJCACHE must be set to 'ccache'.
|
||||
|
||||
This feature is currently experimental and might change in subsequent
|
||||
releases.
|
||||
|
||||
.. _Save/Restore:
|
||||
|
||||
Save/Restore
|
||||
|
|
|
|||
|
|
@ -360,7 +360,8 @@ Verilate in CMake
|
|||
verilate(target SOURCES source ... [TOP_MODULE top] [PREFIX name]
|
||||
[TRACE] [TRACE_FST] [SYSTEMC] [COVERAGE]
|
||||
[INCLUDE_DIRS dir ...] [OPT_SLOW ...] [OPT_FAST ...]
|
||||
[OPT_GLOBAL ..] [DIRECTORY dir] [VERILATOR_ARGS ...])
|
||||
[OPT_GLOBAL ..] [DIRECTORY dir] [THREADS num]
|
||||
[TRACE_THREADS num] [VERILATOR_ARGS ...])
|
||||
|
||||
Lowercase and ... should be replaced with arguments, the uppercase parts
|
||||
delimit the arguments and can be passed in any order, or left out entirely
|
||||
|
|
@ -429,6 +430,15 @@ SystemC include directories and link to the SystemC libraries.
|
|||
the SystemC library. This can be specified using the SYSTEMC_CXX_FLAGS
|
||||
environment variable.
|
||||
|
||||
.. describe:: THREADS
|
||||
|
||||
Optional. Generated a multi-threaded model, same as "--threads".
|
||||
|
||||
.. describe:: TRACE_THREADS
|
||||
|
||||
Optional. Generated multi-threaded trace dumping, same as
|
||||
"--trace-threads".
|
||||
|
||||
.. describe:: TOP_MODULE
|
||||
|
||||
Optional. Sets the name of the top module. Defaults to the name of the
|
||||
|
|
|
|||
|
|
@ -128,6 +128,13 @@ List Of Warnings
|
|||
simulate correctly.
|
||||
|
||||
|
||||
.. option:: BADSTDPRAGMA
|
||||
|
||||
Error that a pragma is badly formed, when that pragma is defined by IEEE1800-2017.
|
||||
For example, an empty `pragma line, or an incorrect specified '`pragma protect'.
|
||||
Note that 3rd party pragmas not defined by IEEE1800-2017 are ignored.
|
||||
|
||||
|
||||
.. option:: BLKANDNBLK
|
||||
|
||||
.. TODO better example
|
||||
|
|
@ -796,15 +803,25 @@ List Of Warnings
|
|||
|
||||
.. option:: MULTIDRIVEN
|
||||
|
||||
.. TODO better example
|
||||
Warns that the specified signal comes from multiple always blocks each
|
||||
with different clocking. This warning does not look at individual bits
|
||||
(see example below).
|
||||
|
||||
Warns that the specified signal comes from multiple always blocks. This
|
||||
is often unsupported by synthesis tools, and is considered bad style.
|
||||
It will also cause longer simulation runtimes due to reduced
|
||||
optimizations.
|
||||
This is considered bad style, as the consumer of a given signal may be
|
||||
unaware of the inconsistent clocking, causing clock domain crossing
|
||||
or timing bugs.
|
||||
|
||||
Faulty example:
|
||||
|
||||
.. include:: ../../docs/gen/ex_MULTIDRIVEN_faulty.rst
|
||||
|
||||
Results in:
|
||||
|
||||
.. include:: ../../docs/gen/ex_MULTIDRIVEN_msg.rst
|
||||
|
||||
Ignoring this warning will only slow simulations, it will simulate
|
||||
correctly.
|
||||
correctly. It may however cause longer simulation runtimes due to
|
||||
reduced optimizations.
|
||||
|
||||
|
||||
.. option:: MULTITOP
|
||||
|
|
@ -988,6 +1005,16 @@ List Of Warnings
|
|||
a var/reg must be used as the target of procedural assignments.
|
||||
|
||||
|
||||
.. option:: PROTECTED
|
||||
|
||||
Warning that a '`pragma protected' section was encountered. The code
|
||||
inside the protected region will be partly checked for correctness, but is
|
||||
otherwise ignored.
|
||||
|
||||
Suppressing the warning may make Verilator differ from a simulator that
|
||||
accepts the protected code.
|
||||
|
||||
|
||||
.. option:: RANDC
|
||||
|
||||
Warns that the :code:`randc` keyword is currently unsupported, and that
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
# cmake --build .
|
||||
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(cmake_hello_sc)
|
||||
project(cmake_hello_sc CXX)
|
||||
|
||||
find_package(verilator HINTS $ENV{VERILATOR_ROOT} ${VERILATOR_ROOT})
|
||||
if (NOT verilator_FOUND)
|
||||
|
|
@ -37,6 +37,11 @@ find_package(SystemCLanguage QUIET)
|
|||
# Create a new executable target that will contain all your sources
|
||||
add_executable(example ../make_hello_sc/sc_main.cpp)
|
||||
|
||||
set_property(
|
||||
TARGET example
|
||||
PROPERTY CXX_STANDARD ${SystemC_CXX_STANDARD}
|
||||
)
|
||||
|
||||
# Add the Verilated circuit to the target
|
||||
verilate(example SYSTEMC
|
||||
INCLUDE_DIRS "../make_hello_sc"
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
# cmake --build .
|
||||
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(cmake_tracing_sc_example)
|
||||
project(cmake_tracing_sc_example CXX)
|
||||
|
||||
find_package(verilator HINTS $ENV{VERILATOR_ROOT} ${VERILATOR_ROOT})
|
||||
if (NOT verilator_FOUND)
|
||||
|
|
@ -37,6 +37,11 @@ find_package(SystemCLanguage QUIET)
|
|||
# Create a new executable target that will contain all your sources
|
||||
add_executable(example ../make_tracing_sc/sc_main.cpp)
|
||||
|
||||
set_property(
|
||||
TARGET example
|
||||
PROPERTY CXX_STANDARD ${SystemC_CXX_STANDARD}
|
||||
)
|
||||
|
||||
# Add the Verilated circuit to the target
|
||||
verilate(example SYSTEMC COVERAGE TRACE
|
||||
INCLUDE_DIRS "../make_tracing_sc"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2018 Tony Bybell.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef WIN_UNISTD_H
|
||||
#define WIN_UNISTD_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifdef _WIN64
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <sys/io.h>
|
||||
#endif
|
||||
|
||||
#include <process.h>
|
||||
|
||||
#define ftruncate _chsize_s
|
||||
#define unlink _unlink
|
||||
#define fileno _fileno
|
||||
#define lseek _lseeki64
|
||||
|
||||
#ifdef _WIN64
|
||||
#define ssize_t __int64
|
||||
#define SSIZE_MAX 9223372036854775807i64
|
||||
#else
|
||||
#define ssize_t long
|
||||
#define SSIZE_MAX 2147483647L
|
||||
#endif
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
#endif //WIN_UNISTD_H
|
||||
|
|
@ -80,6 +80,12 @@
|
|||
#define PATH_MAX (4096)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
typedef int64_t fst_off_t;
|
||||
#else
|
||||
typedef off_t fst_off_t;
|
||||
#endif
|
||||
|
||||
/* note that Judy versus Jenkins requires more experimentation: they are */
|
||||
/* functionally equivalent though it appears Jenkins is slightly faster. */
|
||||
/* in addition, Jenkins is not bound by the LGPL. */
|
||||
|
|
@ -155,8 +161,8 @@ void **JenkinsIns(void *base_i, const unsigned char *mem, uint32_t length, uint3
|
|||
#ifdef __MINGW32__
|
||||
#include <io.h>
|
||||
#ifndef HAVE_FSEEKO
|
||||
#define ftello ftell
|
||||
#define fseeko fseek
|
||||
#define ftello _ftelli64
|
||||
#define fseeko _fseeki64
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
|
@ -284,7 +290,7 @@ static size_t fstFwrite(const void *buf, size_t siz, size_t cnt, FILE *fp)
|
|||
return(fwrite(buf, siz, cnt, fp));
|
||||
}
|
||||
|
||||
static int fstFtruncate(int fd, off_t length)
|
||||
static int fstFtruncate(int fd, fst_off_t length)
|
||||
{
|
||||
return(ftruncate(fd, length));
|
||||
}
|
||||
|
|
@ -329,12 +335,12 @@ return(NULL);
|
|||
#define fstMmap(__addr,__len,__prot,__flags,__fd,__off) fstMmap2((__len), (__fd), (__off))
|
||||
#define fstMunmap(__addr,__len) free(__addr)
|
||||
|
||||
static void *fstMmap2(size_t __len, int __fd, off_t __off)
|
||||
static void *fstMmap2(size_t __len, int __fd, fst_off_t __off)
|
||||
{
|
||||
(void)__off;
|
||||
|
||||
unsigned char *pnt = (unsigned char *)malloc(__len);
|
||||
off_t cur_offs = lseek(__fd, 0, SEEK_CUR);
|
||||
fst_off_t cur_offs = lseek(__fd, 0, SEEK_CUR);
|
||||
size_t i;
|
||||
|
||||
lseek(__fd, 0, SEEK_SET);
|
||||
|
|
@ -734,7 +740,7 @@ FILE *tchn_handle;
|
|||
|
||||
unsigned char *vchg_mem;
|
||||
|
||||
off_t hier_file_len;
|
||||
fst_off_t hier_file_len;
|
||||
|
||||
uint32_t *valpos_mem;
|
||||
unsigned char *curval_mem;
|
||||
|
|
@ -754,7 +760,7 @@ unsigned fourpack : 1;
|
|||
unsigned fastpack : 1;
|
||||
|
||||
int64_t timezero;
|
||||
off_t section_header_truncpos;
|
||||
fst_off_t section_header_truncpos;
|
||||
uint32_t tchn_cnt, tchn_idx;
|
||||
uint64_t curtime;
|
||||
uint64_t firsttime;
|
||||
|
|
@ -762,7 +768,7 @@ uint32_t vchg_siz;
|
|||
uint32_t vchg_alloc_siz;
|
||||
|
||||
uint32_t secnum;
|
||||
off_t section_start;
|
||||
fst_off_t section_start;
|
||||
|
||||
uint32_t numscopes;
|
||||
double nan; /* nan value for uninitialized doubles */
|
||||
|
|
@ -820,7 +826,7 @@ fstEnumHandle max_enumhandle;
|
|||
};
|
||||
|
||||
|
||||
static int fstWriterFseeko(struct fstWriterContext *xc, FILE *stream, off_t offset, int whence)
|
||||
static int fstWriterFseeko(struct fstWriterContext *xc, FILE *stream, fst_off_t offset, int whence)
|
||||
{
|
||||
int rc = fseeko(stream, offset, whence);
|
||||
|
||||
|
|
@ -987,7 +993,7 @@ if(pnt == MAP_FAILED)
|
|||
|
||||
static void fstWriterCreateMmaps(struct fstWriterContext *xc)
|
||||
{
|
||||
off_t curpos = ftello(xc->handle);
|
||||
fst_off_t curpos = ftello(xc->handle);
|
||||
|
||||
fflush(xc->hier_handle);
|
||||
|
||||
|
|
@ -1041,7 +1047,7 @@ if(xc->curval_mem)
|
|||
{
|
||||
unsigned char *pnt = xc->curval_mem;
|
||||
int __fd = fileno(xc->curval_handle);
|
||||
off_t cur_offs = lseek(__fd, 0, SEEK_CUR);
|
||||
fst_off_t cur_offs = lseek(__fd, 0, SEEK_CUR);
|
||||
size_t i;
|
||||
size_t __len = xc->maxvalpos;
|
||||
|
||||
|
|
@ -1282,14 +1288,14 @@ int cnt = 0;
|
|||
unsigned int i;
|
||||
unsigned char *vchg_mem;
|
||||
FILE *f;
|
||||
off_t fpos, indxpos, endpos;
|
||||
fst_off_t fpos, indxpos, endpos;
|
||||
uint32_t prevpos;
|
||||
int zerocnt;
|
||||
unsigned char *scratchpad;
|
||||
unsigned char *scratchpnt;
|
||||
unsigned char *tmem;
|
||||
off_t tlen;
|
||||
off_t unc_memreq = 0; /* for reader */
|
||||
fst_off_t tlen;
|
||||
fst_off_t unc_memreq = 0; /* for reader */
|
||||
unsigned char *packmem;
|
||||
unsigned int packmemlen;
|
||||
uint32_t *vm4ip;
|
||||
|
|
@ -1733,7 +1739,7 @@ if(tmem)
|
|||
unsigned char *dmem = (unsigned char *)malloc(compressBound(destlen));
|
||||
int rc = compress2(dmem, &destlen, tmem, tlen, 9);
|
||||
|
||||
if((rc == Z_OK) && (((off_t)destlen) < tlen))
|
||||
if((rc == Z_OK) && (((fst_off_t)destlen) < tlen))
|
||||
{
|
||||
fstFwrite(dmem, destlen, 1, xc->handle);
|
||||
}
|
||||
|
|
@ -1781,7 +1787,7 @@ fstWriterFseeko(xc, xc->handle, endpos, SEEK_SET);
|
|||
xc2->section_header_truncpos = endpos; /* cache in case of need to truncate */
|
||||
if(xc->dump_size_limit)
|
||||
{
|
||||
if(endpos >= ((off_t)xc->dump_size_limit))
|
||||
if(endpos >= ((fst_off_t)xc->dump_size_limit))
|
||||
{
|
||||
xc2->skip_writing_section_hdr = 1;
|
||||
xc2->size_limit_locked = 1;
|
||||
|
|
@ -1931,7 +1937,7 @@ if(xc)
|
|||
if(xc && !xc->already_in_close && !xc->already_in_flush)
|
||||
{
|
||||
unsigned char *tmem = NULL;
|
||||
off_t fixup_offs, tlen, hlen;
|
||||
fst_off_t fixup_offs, tlen, hlen;
|
||||
|
||||
xc->already_in_close = 1; /* never need to zero this out as it is freed at bottom */
|
||||
|
||||
|
|
@ -1991,7 +1997,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush)
|
|||
unsigned char *dmem = (unsigned char *)malloc(compressBound(destlen));
|
||||
int rc = compress2(dmem, &destlen, tmem, tlen, 9);
|
||||
|
||||
if((rc != Z_OK) || (((off_t)destlen) > tlen))
|
||||
if((rc != Z_OK) || (((fst_off_t)destlen) > tlen))
|
||||
{
|
||||
destlen = tlen;
|
||||
}
|
||||
|
|
@ -2002,7 +2008,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush)
|
|||
fstWriterUint64(xc->handle, tlen); /* uncompressed */
|
||||
/* compressed len is section length - 24 */
|
||||
fstWriterUint64(xc->handle, xc->maxhandle); /* maxhandle */
|
||||
fstFwrite((((off_t)destlen) != tlen) ? dmem : tmem, destlen, 1, xc->handle);
|
||||
fstFwrite((((fst_off_t)destlen) != tlen) ? dmem : tmem, destlen, 1, xc->handle);
|
||||
fflush(xc->handle);
|
||||
|
||||
fstWriterFseeko(xc, xc->handle, fixup_offs, SEEK_SET);
|
||||
|
|
@ -2018,7 +2024,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush)
|
|||
if(xc->num_blackouts)
|
||||
{
|
||||
uint64_t cur_bl = 0;
|
||||
off_t bpos, eos;
|
||||
fst_off_t bpos, eos;
|
||||
uint32_t i;
|
||||
|
||||
fixup_offs = ftello(xc->handle);
|
||||
|
|
@ -2051,7 +2057,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush)
|
|||
|
||||
if(xc->compress_hier)
|
||||
{
|
||||
off_t hl, eos;
|
||||
fst_off_t hl, eos;
|
||||
gzFile zhandle;
|
||||
int zfd;
|
||||
int fourpack_duo = 0;
|
||||
|
|
@ -2174,7 +2180,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush)
|
|||
if(xc->repack_on_close)
|
||||
{
|
||||
FILE *fp;
|
||||
off_t offpnt, uclen;
|
||||
fst_off_t offpnt, uclen;
|
||||
int flen = strlen(xc->filename);
|
||||
char *hf = (char *)calloc(1, flen + 5);
|
||||
|
||||
|
|
@ -2281,7 +2287,7 @@ struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
|
|||
if(xc)
|
||||
{
|
||||
char s[FST_HDR_DATE_SIZE];
|
||||
off_t fpos = ftello(xc->handle);
|
||||
fst_off_t fpos = ftello(xc->handle);
|
||||
int len = strlen(dat);
|
||||
|
||||
fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_DATE, SEEK_SET);
|
||||
|
|
@ -2300,7 +2306,7 @@ struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
|
|||
if(xc && vers)
|
||||
{
|
||||
char s[FST_HDR_SIM_VERSION_SIZE];
|
||||
off_t fpos = ftello(xc->handle);
|
||||
fst_off_t fpos = ftello(xc->handle);
|
||||
int len = strlen(vers);
|
||||
|
||||
fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_SIM_VERSION, SEEK_SET);
|
||||
|
|
@ -2320,7 +2326,7 @@ if(xc)
|
|||
{
|
||||
if(/*(filetype >= FST_FT_MIN) &&*/ (filetype <= FST_FT_MAX))
|
||||
{
|
||||
off_t fpos = ftello(xc->handle);
|
||||
fst_off_t fpos = ftello(xc->handle);
|
||||
|
||||
xc->filetype = filetype;
|
||||
|
||||
|
|
@ -2461,7 +2467,7 @@ void fstWriterSetTimescale(void *ctx, int ts)
|
|||
struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
|
||||
if(xc)
|
||||
{
|
||||
off_t fpos = ftello(xc->handle);
|
||||
fst_off_t fpos = ftello(xc->handle);
|
||||
fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_TIMESCALE, SEEK_SET);
|
||||
fputc(ts & 255, xc->handle);
|
||||
fflush(xc->handle);
|
||||
|
|
@ -2519,7 +2525,7 @@ void fstWriterSetTimezero(void *ctx, int64_t tim)
|
|||
struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
|
||||
if(xc)
|
||||
{
|
||||
off_t fpos = ftello(xc->handle);
|
||||
fst_off_t fpos = ftello(xc->handle);
|
||||
fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_TIMEZERO, SEEK_SET);
|
||||
fstWriterUint64(xc->handle, (xc->timezero = tim));
|
||||
fflush(xc->handle);
|
||||
|
|
@ -3361,7 +3367,7 @@ char date[FST_HDR_DATE_SIZE + 1];
|
|||
int64_t timezero;
|
||||
|
||||
char *filename, *filename_unpacked;
|
||||
off_t hier_pos;
|
||||
fst_off_t hier_pos;
|
||||
|
||||
uint32_t num_blackouts;
|
||||
uint64_t *blackout_times;
|
||||
|
|
@ -3376,10 +3382,10 @@ uint64_t *rvat_time_table;
|
|||
uint64_t rvat_beg_tim, rvat_end_tim;
|
||||
unsigned char *rvat_frame_data;
|
||||
uint64_t rvat_frame_maxhandle;
|
||||
off_t *rvat_chain_table;
|
||||
fst_off_t *rvat_chain_table;
|
||||
uint32_t *rvat_chain_table_lengths;
|
||||
uint64_t rvat_vc_maxhandle;
|
||||
off_t rvat_vc_start;
|
||||
fst_off_t rvat_vc_start;
|
||||
uint32_t *rvat_sig_offs;
|
||||
int rvat_packtype;
|
||||
|
||||
|
|
@ -3418,7 +3424,7 @@ char *fh_nam;
|
|||
};
|
||||
|
||||
|
||||
int fstReaderFseeko(struct fstReaderContext *xc, FILE *stream, off_t offset, int whence)
|
||||
int fstReaderFseeko(struct fstReaderContext *xc, FILE *stream, fst_off_t offset, int whence)
|
||||
{
|
||||
int rc = fseeko(stream, offset, whence);
|
||||
|
||||
|
|
@ -3911,11 +3917,11 @@ int pass_status = 1;
|
|||
|
||||
if(!xc->fh)
|
||||
{
|
||||
off_t offs_cache = ftello(xc->f);
|
||||
fst_off_t offs_cache = ftello(xc->f);
|
||||
char *fnam = (char *)malloc(strlen(xc->filename) + 6 + 16 + 32 + 1);
|
||||
unsigned char *mem = (unsigned char *)malloc(FST_GZIO_LEN);
|
||||
off_t hl, uclen;
|
||||
off_t clen = 0;
|
||||
fst_off_t hl, uclen;
|
||||
fst_off_t clen = 0;
|
||||
gzFile zhandle = NULL;
|
||||
int zfd;
|
||||
int htyp = FST_BL_SKIP;
|
||||
|
|
@ -4535,8 +4541,8 @@ return(1);
|
|||
*/
|
||||
int fstReaderInit(struct fstReaderContext *xc)
|
||||
{
|
||||
off_t blkpos = 0;
|
||||
off_t endfile;
|
||||
fst_off_t blkpos = 0;
|
||||
fst_off_t endfile;
|
||||
uint64_t seclen;
|
||||
int sectype;
|
||||
uint64_t vc_section_count_actual = 0;
|
||||
|
|
@ -4548,7 +4554,7 @@ sectype = fgetc(xc->f);
|
|||
if(sectype == FST_BL_ZWRAPPER)
|
||||
{
|
||||
FILE *fcomp;
|
||||
off_t offpnt, uclen;
|
||||
fst_off_t offpnt, uclen;
|
||||
char gz_membuf[FST_GZIO_LEN];
|
||||
gzFile zhandle;
|
||||
int zfd;
|
||||
|
|
@ -4981,15 +4987,15 @@ uint64_t *time_table = NULL;
|
|||
uint64_t tsec_nitems;
|
||||
unsigned int secnum = 0;
|
||||
int blocks_skipped = 0;
|
||||
off_t blkpos = 0;
|
||||
fst_off_t blkpos = 0;
|
||||
uint64_t seclen, beg_tim;
|
||||
#ifdef FST_DEBUG
|
||||
uint64_t end_tim;
|
||||
#endif
|
||||
uint64_t frame_uclen, frame_clen, frame_maxhandle, vc_maxhandle;
|
||||
off_t vc_start;
|
||||
off_t indx_pntr, indx_pos;
|
||||
off_t *chain_table = NULL;
|
||||
fst_off_t vc_start;
|
||||
fst_off_t indx_pntr, indx_pos;
|
||||
fst_off_t *chain_table = NULL;
|
||||
uint32_t *chain_table_lengths = NULL;
|
||||
unsigned char *chain_cmem;
|
||||
unsigned char *pnt;
|
||||
|
|
@ -5105,7 +5111,7 @@ for(;;)
|
|||
destlen = tsec_uclen;
|
||||
sourcelen = tsec_clen;
|
||||
|
||||
fstReaderFseeko(xc, xc->f, -24 - ((off_t)tsec_clen), SEEK_CUR);
|
||||
fstReaderFseeko(xc, xc->f, -24 - ((fst_off_t)tsec_clen), SEEK_CUR);
|
||||
|
||||
if(tsec_uclen != tsec_clen)
|
||||
{
|
||||
|
|
@ -5346,11 +5352,11 @@ for(;;)
|
|||
}
|
||||
|
||||
free(mu);
|
||||
fstReaderFseeko(xc, xc->f, -((off_t)frame_clen), SEEK_CUR);
|
||||
fstReaderFseeko(xc, xc->f, -((fst_off_t)frame_clen), SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
fstReaderFseeko(xc, xc->f, (off_t)frame_clen, SEEK_CUR); /* skip past compressed data */
|
||||
fstReaderFseeko(xc, xc->f, (fst_off_t)frame_clen, SEEK_CUR); /* skip past compressed data */
|
||||
|
||||
vc_maxhandle = fstReaderVarint64(xc->f);
|
||||
vc_start = ftello(xc->f); /* points to '!' character */
|
||||
|
|
@ -5380,7 +5386,7 @@ for(;;)
|
|||
free(chain_table_lengths);
|
||||
|
||||
vc_maxhandle_largest = vc_maxhandle;
|
||||
chain_table = (off_t *)calloc((vc_maxhandle+1), sizeof(off_t));
|
||||
chain_table = (fst_off_t *)calloc((vc_maxhandle+1), sizeof(fst_off_t));
|
||||
chain_table_lengths = (uint32_t *)calloc((vc_maxhandle+1), sizeof(uint32_t));
|
||||
}
|
||||
|
||||
|
|
@ -6001,7 +6007,7 @@ return(buf);
|
|||
char *fstReaderGetValueFromHandleAtTime(void *ctx, uint64_t tim, fstHandle facidx, char *buf)
|
||||
{
|
||||
struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
|
||||
off_t blkpos = 0, prev_blkpos;
|
||||
fst_off_t blkpos = 0, prev_blkpos;
|
||||
uint64_t beg_tim, end_tim, beg_tim2, end_tim2;
|
||||
int sectype;
|
||||
unsigned int secnum = 0;
|
||||
|
|
@ -6012,7 +6018,7 @@ uint64_t frame_uclen, frame_clen;
|
|||
#ifdef FST_DEBUG
|
||||
uint64_t mem_required_for_traversal;
|
||||
#endif
|
||||
off_t indx_pntr, indx_pos;
|
||||
fst_off_t indx_pntr, indx_pos;
|
||||
long chain_clen;
|
||||
unsigned char *chain_cmem;
|
||||
unsigned char *pnt;
|
||||
|
|
@ -6074,7 +6080,7 @@ for(;;)
|
|||
{
|
||||
if((tim == end_tim) && (tim != xc->end_time))
|
||||
{
|
||||
off_t cached_pos = ftello(xc->f);
|
||||
fst_off_t cached_pos = ftello(xc->f);
|
||||
fstReaderFseeko(xc, xc->f, blkpos, SEEK_SET);
|
||||
|
||||
sectype = fgetc(xc->f);
|
||||
|
|
@ -6136,7 +6142,7 @@ ucdata = (unsigned char *)malloc(tsec_uclen);
|
|||
destlen = tsec_uclen;
|
||||
sourcelen = tsec_clen;
|
||||
|
||||
fstReaderFseeko(xc, xc->f, -24 - ((off_t)tsec_clen), SEEK_CUR);
|
||||
fstReaderFseeko(xc, xc->f, -24 - ((fst_off_t)tsec_clen), SEEK_CUR);
|
||||
if(tsec_uclen != tsec_clen)
|
||||
{
|
||||
cdata = (unsigned char *)malloc(tsec_clen);
|
||||
|
|
@ -6221,7 +6227,7 @@ chain_cmem = (unsigned char *)malloc(chain_clen);
|
|||
fstReaderFseeko(xc, xc->f, indx_pos, SEEK_SET);
|
||||
fstFread(chain_cmem, chain_clen, 1, xc->f);
|
||||
|
||||
xc->rvat_chain_table = (off_t *)calloc((xc->rvat_vc_maxhandle+1), sizeof(off_t));
|
||||
xc->rvat_chain_table = (fst_off_t *)calloc((xc->rvat_vc_maxhandle+1), sizeof(fst_off_t));
|
||||
xc->rvat_chain_table_lengths = (uint32_t *)calloc((xc->rvat_vc_maxhandle+1), sizeof(uint32_t));
|
||||
|
||||
pnt = chain_cmem;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,11 @@ extern "C" {
|
|||
#include <ctype.h>
|
||||
#include <zlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#if defined(_MSC_VER)
|
||||
#include "fst_win_unistd.h"
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <time.h>
|
||||
|
||||
#define FST_RDLOAD "FSTLOAD | "
|
||||
|
|
|
|||
|
|
@ -637,27 +637,80 @@ std::string VL_DECIMAL_NW(int width, WDataInP lwp) VL_MT_SAFE {
|
|||
return output;
|
||||
}
|
||||
|
||||
std::string _vl_vsformat_time(char* tmp, double ld, bool left, size_t width) {
|
||||
// Double may lose precision, but sc_time_stamp has similar limit
|
||||
std::string suffix = Verilated::threadContextp()->impp()->timeFormatSuffix();
|
||||
int userUnits = Verilated::threadContextp()->impp()->timeFormatUnits(); // 0..-15
|
||||
int fracDigits = Verilated::threadContextp()->impp()->timeFormatPrecision(); // 0..N
|
||||
int prec = Verilated::threadContextp()->timeprecision(); // 0..-15
|
||||
int shift = prec - userUnits + fracDigits; // 0..-15
|
||||
double shiftd = vl_time_multiplier(shift);
|
||||
double scaled = ld * shiftd;
|
||||
QData fracDiv = static_cast<QData>(vl_time_multiplier(fracDigits));
|
||||
QData whole = static_cast<QData>(scaled) / fracDiv;
|
||||
QData fraction = static_cast<QData>(scaled) % fracDiv;
|
||||
template <typename T>
|
||||
std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t width) {
|
||||
const VerilatedContextImp* const ctxImpp = Verilated::threadContextp()->impp();
|
||||
const std::string suffix = ctxImpp->timeFormatSuffix();
|
||||
const int userUnits = ctxImpp->timeFormatUnits(); // 0..-15
|
||||
const int fracDigits = ctxImpp->timeFormatPrecision(); // 0..N
|
||||
const int shift = -userUnits + fracDigits + timeunit; // 0..-15
|
||||
int digits = 0;
|
||||
if (!fracDigits) {
|
||||
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%" VL_PRI64 "u%s", whole,
|
||||
suffix.c_str());
|
||||
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];
|
||||
|
||||
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));
|
||||
shifted = VL_DIV_WWW(b, tmp2, shifted, pow10);
|
||||
} else {
|
||||
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
|
||||
= 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];
|
||||
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
|
||||
char* ptr = buf + sizeof(buf) - 1;
|
||||
*ptr = '\0';
|
||||
while (VL_GT_W(w, v, zero)) {
|
||||
--ptr;
|
||||
WDataInP mod = VL_MODDIV_WWW(b, tmp2, v, ten); // breaks max64Bit
|
||||
*ptr = "0123456789"[VL_SET_QW(mod)];
|
||||
WData divided[w];
|
||||
VL_DIV_WWW(b, divided, v, ten);
|
||||
VL_ASSIGN_W(b, v, divided);
|
||||
}
|
||||
if (!fracDigits) {
|
||||
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%s%s", ptr, suffix.c_str());
|
||||
} else {
|
||||
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%s.%0*" VL_PRI64 "u%s", ptr,
|
||||
fracDigits, VL_SET_QW(frac), suffix.c_str());
|
||||
}
|
||||
} else {
|
||||
const vluint64_t integer64 = VL_SET_QW(integer);
|
||||
if (!fracDigits) {
|
||||
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%" VL_PRI64 "u%s", integer64,
|
||||
suffix.c_str());
|
||||
} else {
|
||||
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH,
|
||||
"%" VL_PRI64 "u.%0*" VL_PRI64 "u%s", integer64, fracDigits,
|
||||
VL_SET_QW(frac), suffix.c_str());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%" VL_PRI64 "u.%0*" VL_PRI64 "u%s",
|
||||
whole, fracDigits, fraction, suffix.c_str());
|
||||
double shiftd = vl_time_multiplier(shift);
|
||||
double scaled = ld * shiftd;
|
||||
const double fracDiv = vl_time_multiplier(fracDigits);
|
||||
const double whole = scaled / fracDiv;
|
||||
if (!fracDigits) {
|
||||
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%.0f%s", whole, suffix.c_str());
|
||||
} else {
|
||||
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%.*f%s", fracDigits, whole,
|
||||
suffix.c_str());
|
||||
}
|
||||
}
|
||||
int needmore = width - digits;
|
||||
|
||||
const int needmore = width - digits;
|
||||
std::string padding;
|
||||
if (needmore > 0) padding.append(needmore, ' '); // Pad with spaces
|
||||
return left ? (tmp + padding) : (padding + tmp);
|
||||
|
|
@ -752,7 +805,8 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
|||
if (lbits) {} // UNUSED - always 64
|
||||
if (fmt == '^') { // Realtime
|
||||
if (!widthSet) width = Verilated::threadContextp()->impp()->timeFormatWidth();
|
||||
output += _vl_vsformat_time(t_tmp, d, left, width);
|
||||
const int timeunit = va_arg(ap, int);
|
||||
output += _vl_vsformat_time(t_tmp, d, timeunit, left, width);
|
||||
} else {
|
||||
std::string fmts(pctp, pos - pctp + 1);
|
||||
VL_SNPRINTF(t_tmp, VL_VALUE_STRING_MAX_WIDTH, fmts.c_str(), d);
|
||||
|
|
@ -851,7 +905,8 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
|||
}
|
||||
case 't': { // Time
|
||||
if (!widthSet) width = Verilated::threadContextp()->impp()->timeFormatWidth();
|
||||
output += _vl_vsformat_time(t_tmp, static_cast<double>(ld), left, width);
|
||||
const int timeunit = va_arg(ap, int);
|
||||
output += _vl_vsformat_time(t_tmp, ld, timeunit, left, width);
|
||||
break;
|
||||
}
|
||||
case 'b':
|
||||
|
|
@ -2154,6 +2209,30 @@ double vl_time_multiplier(int scale) VL_PURE {
|
|||
return pow10[scale];
|
||||
}
|
||||
}
|
||||
vluint64_t vl_time_pow10(int n) {
|
||||
static const vluint64_t pow10[20] = {
|
||||
1ULL,
|
||||
10ULL,
|
||||
100ULL,
|
||||
1000ULL,
|
||||
10000ULL,
|
||||
100000ULL,
|
||||
1000000ULL,
|
||||
10000000ULL,
|
||||
100000000ULL,
|
||||
1000000000ULL,
|
||||
10000000000ULL,
|
||||
100000000000ULL,
|
||||
1000000000000ULL,
|
||||
10000000000000ULL,
|
||||
100000000000000ULL,
|
||||
1000000000000000ULL,
|
||||
10000000000000000ULL,
|
||||
100000000000000000ULL,
|
||||
1000000000000000000ULL,
|
||||
};
|
||||
return pow10[n];
|
||||
}
|
||||
|
||||
void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp,
|
||||
const VerilatedContext* contextp) VL_MT_SAFE {
|
||||
|
|
|
|||
|
|
@ -1187,6 +1187,8 @@ inline vluint64_t VerilatedContext::time() const VL_MT_SAFE {
|
|||
|
||||
// Return time precision as multiplier of time units
|
||||
double vl_time_multiplier(int scale) VL_PURE;
|
||||
// Return power of 10. e.g. returns 100 if n==2
|
||||
vluint64_t vl_time_pow10(int n) VL_PURE;
|
||||
|
||||
#ifdef VL_DEBUG
|
||||
/// Evaluate statement if Verilated::debug() enabled
|
||||
|
|
@ -1793,7 +1795,7 @@ static inline WDataOutP VL_NEGATE_W(int words, WDataOutP owp, WDataInP lwp) VL_M
|
|||
}
|
||||
return owp;
|
||||
}
|
||||
static void VL_NEGATE_INPLACE_W(int words, WDataOutP owp_lwp) VL_MT_SAFE {
|
||||
static inline void VL_NEGATE_INPLACE_W(int words, WDataOutP owp_lwp) VL_MT_SAFE {
|
||||
EData carry = 1;
|
||||
for (int i = 0; i < words; ++i) {
|
||||
EData word = ~owp_lwp[i] + carry;
|
||||
|
|
@ -2487,8 +2489,7 @@ static inline IData VL_SHIFTL_IIW(int obits, int, int rbits, IData lhs, WDataInP
|
|||
}
|
||||
return VL_CLEAN_II(obits, obits, lhs << rwp[0]);
|
||||
}
|
||||
static inline IData VL_SHIFTL_IIQ(int obits, int lbits, int rbits, IData lhs,
|
||||
QData rhs) VL_MT_SAFE {
|
||||
static inline IData VL_SHIFTL_IIQ(int obits, int, int, IData lhs, QData rhs) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0;
|
||||
return VL_CLEAN_II(obits, obits, lhs << rhs);
|
||||
}
|
||||
|
|
@ -2501,8 +2502,7 @@ static inline QData VL_SHIFTL_QQW(int obits, int, int rbits, QData lhs, WDataInP
|
|||
// Above checks rwp[1]==0 so not needed in below shift
|
||||
return VL_CLEAN_QQ(obits, obits, lhs << (static_cast<QData>(rwp[0])));
|
||||
}
|
||||
static inline QData VL_SHIFTL_QQQ(int obits, int lbits, int rbits, QData lhs,
|
||||
QData rhs) VL_MT_SAFE {
|
||||
static inline QData VL_SHIFTL_QQQ(int obits, int, int, QData lhs, QData rhs) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0;
|
||||
return VL_CLEAN_QQ(obits, obits, lhs << rhs);
|
||||
}
|
||||
|
|
@ -2567,11 +2567,11 @@ static inline QData VL_SHIFTR_QQW(int obits, int, int rbits, QData lhs, WDataInP
|
|||
// Above checks rwp[1]==0 so not needed in below shift
|
||||
return VL_CLEAN_QQ(obits, obits, lhs >> (static_cast<QData>(rwp[0])));
|
||||
}
|
||||
static inline IData VL_SHIFTR_IIQ(int obits, int, int rbits, IData lhs, QData rhs) VL_MT_SAFE {
|
||||
static inline IData VL_SHIFTR_IIQ(int obits, int, int, IData lhs, QData rhs) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0;
|
||||
return VL_CLEAN_QQ(obits, obits, lhs >> rhs);
|
||||
}
|
||||
static inline QData VL_SHIFTR_QQQ(int obits, int, int rbits, QData lhs, QData rhs) VL_MT_SAFE {
|
||||
static inline QData VL_SHIFTR_QQQ(int obits, int, int, QData lhs, QData rhs) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0;
|
||||
return VL_CLEAN_QQ(obits, obits, lhs >> rhs);
|
||||
}
|
||||
|
|
@ -2705,7 +2705,7 @@ static inline IData VL_BITSEL_IWII(int, int lbits, int, int, WDataInP lwp, IData
|
|||
static inline IData VL_SEL_IWII(int, int lbits, int, int, WDataInP lwp, IData lsb,
|
||||
IData width) VL_MT_SAFE {
|
||||
int msb = lsb + width - 1;
|
||||
if (VL_UNLIKELY(msb > lbits)) {
|
||||
if (VL_UNLIKELY(msb >= lbits)) {
|
||||
return ~0; // Spec says you can go outside the range of a array. Don't coredump if so.
|
||||
} else if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast<int>(lsb))) {
|
||||
return VL_BITRSHIFT_W(lwp, lsb);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
######################################################################
|
||||
|
||||
PERL = @PERL@
|
||||
PYTHON3 = @PYTHON3@
|
||||
CXX = @CXX@
|
||||
LINK = @CXX@
|
||||
AR = ar
|
||||
|
|
@ -35,6 +36,7 @@ CFG_LDLIBS_THREADS = @CFG_LDLIBS_THREADS@
|
|||
|
||||
VERILATOR_COVERAGE = $(PERL) $(VERILATOR_ROOT)/bin/verilator_coverage
|
||||
VERILATOR_INCLUDER = $(PERL) $(VERILATOR_ROOT)/bin/verilator_includer
|
||||
VERILATOR_CCACHE_REPORT = $(PYTHON3) $(VERILATOR_ROOT)/bin/verilator_ccache_report
|
||||
|
||||
######################################################################
|
||||
# Make checks
|
||||
|
|
@ -202,16 +204,19 @@ else
|
|||
endif
|
||||
|
||||
# When archiving just objects (.o), use single $(AR) run
|
||||
# 1. Make .tmp file with list of objects so don't exceed commend line
|
||||
# 1. Make .verilator_deplist.tmp file with list of objects so don't exceed
|
||||
# the command line limits when calling $(AR).
|
||||
# The approach to write the dependency file is compatible with GNU Make 3,
|
||||
# and can be simplified using the file function once GNU Make 4.x becomes
|
||||
# the minimum supported version.
|
||||
# When merging objects (.o) and archives (.a) additionally:
|
||||
# 1. Extract object files from .a
|
||||
# 2. Create a new archive from extracted .o and given .o
|
||||
%.a:
|
||||
%.a: | %.verilator_deplist.tmp
|
||||
$(info Archive $(AR) -rcs $@ $^)
|
||||
$(file >$@.tmp)
|
||||
$(foreach L, $(filter-out %.a,$^), $(file >>$@.tmp, $L))
|
||||
$(foreach L, $(filter-out %.a,$^), $(shell echo $L >>$@.verilator_deplist.tmp))
|
||||
@if test $(words $(filter %.a,$^)) -eq 0; then \
|
||||
$(AR) -rcs $@ @$@.tmp; \
|
||||
$(AR) -rcs $@ @$@.verilator_deplist.tmp; \
|
||||
else \
|
||||
$(RM) -rf $@.tmpdir; \
|
||||
for archive in $(filter %.a,$^); do \
|
||||
|
|
@ -220,9 +225,13 @@ endif
|
|||
$(AR) -x ../../$${archive}; \
|
||||
cd ../..; \
|
||||
done; \
|
||||
$(AR) -rcs $@ @$@.tmp $@.tmpdir/*/*.o; \
|
||||
$(AR) -rcs $@ @$@.verilator_deplist.tmp $@.tmpdir/*/*.o; \
|
||||
fi \
|
||||
; $(RM) -rf $@.tmp $@.tmpdir
|
||||
; $(RM) -rf $@.verilator_deplist.tmp $@.tmpdir
|
||||
|
||||
# Truncate the dependency list file used in the %.a target above.
|
||||
%.verilator_deplist.tmp:
|
||||
echo "" > $@
|
||||
|
||||
$(VM_PREFIX)__ALL.a: $(VK_OBJS) $(VM_HIER_LIBS)
|
||||
|
||||
|
|
@ -247,6 +256,42 @@ endif
|
|||
#.cpp.o:
|
||||
# $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
|
||||
|
||||
######################################################################
|
||||
### ccache report
|
||||
|
||||
ifneq ($(findstring ccache-report,$(MAKECMDGOALS)),)
|
||||
ifneq ($(OBJCACHE),ccache)
|
||||
$(error ccache-report requires OBJCACHE to equal 'ccache')
|
||||
endif
|
||||
VK_OTHER_GOALS := $(strip $(subst ccache-report,,$(MAKECMDGOALS)))
|
||||
ifeq ($(VK_OTHER_GOALS),)
|
||||
$(error ccache-report must be used with at least one other explicit target)
|
||||
endif
|
||||
|
||||
# Report ccache behaviour for this invocation of make
|
||||
VK_CCACHE_LOGDIR := ccache-logs
|
||||
VK_CCACHE_REPORT := $(VM_PREFIX)__ccache_report.txt
|
||||
# Remove previous logfiles and report
|
||||
$(shell rm -rf $(VK_CCACHE_LOGDIR) $(VK_CCACHE_REPORT))
|
||||
|
||||
$(VK_CCACHE_LOGDIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(VK_OBJS): | $(VK_CCACHE_LOGDIR)
|
||||
|
||||
$(VK_OBJS): export CCACHE_LOGFILE=$(VK_CCACHE_LOGDIR)/$@.log
|
||||
|
||||
$(VK_CCACHE_REPORT): $(VK_OBJS)
|
||||
$(VERILATOR_CCACHE_REPORT) -o $@ $(VK_CCACHE_LOGDIR)
|
||||
|
||||
.PHONY: ccache-report
|
||||
ccache-report: $(VK_CCACHE_REPORT)
|
||||
@cat $<
|
||||
|
||||
# ccache-report runs last
|
||||
ccache-report: $(VK_OTHER_GOALS)
|
||||
endif
|
||||
|
||||
######################################################################
|
||||
### Debugging
|
||||
|
||||
|
|
|
|||
|
|
@ -152,6 +152,8 @@ public:
|
|||
/// Flush dump
|
||||
void flush() VL_MT_SAFE { m_sptrace.flush(); }
|
||||
/// Write one cycle of dump data
|
||||
/// Call with the current context's time just after eval'ed,
|
||||
/// e.g. ->dump(contextp->time())
|
||||
void dump(vluint64_t timeui) { m_sptrace.dump(timeui); }
|
||||
/// Write one cycle of dump data - backward compatible and to reduce
|
||||
/// conversion warnings. It's better to use a vluint64_t time instead.
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include "verilated.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ public: // But only for verilated*.cpp
|
|||
static vluint32_t randSeedEpoch() VL_MT_SAFE { return s().s_randSeedEpoch; }
|
||||
|
||||
// METHODS - timeformat
|
||||
int timeFormatUnits() VL_MT_SAFE {
|
||||
int timeFormatUnits() const VL_MT_SAFE {
|
||||
if (m_s.m_timeFormatUnits == VerilatedContext::Serialized::UNITS_NONE)
|
||||
return timeprecision();
|
||||
return m_s.m_timeFormatUnits;
|
||||
|
|
@ -255,7 +255,7 @@ public: // But only for verilated*.cpp
|
|||
void timeFormatPrecision(int value) VL_MT_SAFE { m_s.m_timeFormatPrecision = value; }
|
||||
int timeFormatWidth() const VL_MT_SAFE { return m_s.m_timeFormatWidth; }
|
||||
void timeFormatWidth(int value) VL_MT_SAFE { m_s.m_timeFormatWidth = value; }
|
||||
std::string timeFormatSuffix() VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
|
||||
std::string timeFormatSuffix() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
|
||||
const VerilatedLockGuard lock(m_timeDumpMutex);
|
||||
return m_timeFormatSuffix;
|
||||
}
|
||||
|
|
@ -333,17 +333,20 @@ public: // But only for verilated*.cpp
|
|||
}
|
||||
void fdClose(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock(m_fdMutex);
|
||||
if ((fdi & (1 << 31)) != 0) {
|
||||
if (VL_BITISSET_I(fdi, 31)) {
|
||||
// Non-MCD case
|
||||
IData idx = VL_MASK_I(31) & fdi;
|
||||
if (VL_UNLIKELY(idx >= m_fdps.size())) return;
|
||||
if (VL_UNLIKELY(idx <= 2)) return; // stdout/stdin/stderr
|
||||
if (VL_UNLIKELY(!m_fdps[idx])) return; // Already free
|
||||
std::fclose(m_fdps[idx]);
|
||||
m_fdps[idx] = (FILE*)0;
|
||||
m_fdFree.push_back(idx);
|
||||
} else {
|
||||
// MCD case
|
||||
for (int i = 0; (fdi != 0) && (i < 31); i++, fdi >>= 1) {
|
||||
// Starts at 1 to skip stdout
|
||||
fdi >>= 1;
|
||||
for (int i = 1; (fdi != 0) && (i < 31); i++, fdi >>= 1) {
|
||||
if (fdi & VL_MASK_I(1)) {
|
||||
std::fclose(m_fdps[i]);
|
||||
m_fdps[i] = nullptr;
|
||||
|
|
@ -375,7 +378,9 @@ private:
|
|||
}
|
||||
} else {
|
||||
// MCD Case
|
||||
for (size_t i = 0; (fdi != 0) && (i < fp.capacity()); ++i, fdi >>= 1) {
|
||||
if (fdi & 1) fp.push_back(stdout);
|
||||
fdi >>= 1;
|
||||
for (size_t i = 1; (fdi != 0) && (i < fp.capacity()); ++i, fdi >>= 1) {
|
||||
if (fdi & VL_MASK_I(1)) fp.push_back(m_fdps[i]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,7 +99,6 @@ VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep) {
|
|||
m_wrBufp = new char[m_wrChunkSize * 8];
|
||||
m_wrFlushp = m_wrBufp + m_wrChunkSize * 6;
|
||||
m_writep = m_wrBufp;
|
||||
m_suffixesp = nullptr;
|
||||
}
|
||||
|
||||
void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
|
|
@ -114,9 +113,6 @@ void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
|||
|
||||
dumpHeader();
|
||||
|
||||
// Get the direct access pointer to the code strings
|
||||
m_suffixesp = &m_suffixes[0]; // Note: C++11 m_suffixes.data();
|
||||
|
||||
// When using rollover, the first chunk contains the header only.
|
||||
if (m_rolloverMB) openNextImp(true);
|
||||
}
|
||||
|
|
@ -586,8 +582,10 @@ void VerilatedVcd::declTriArray(vluint32_t code, const char* name, bool array, i
|
|||
//=============================================================================
|
||||
// Trace rendering prinitives
|
||||
|
||||
void VerilatedVcd::finishLine(vluint32_t code, char* writep) {
|
||||
const char* const suffixp = m_suffixesp + code * VL_TRACE_SUFFIX_ENTRY_SIZE;
|
||||
static inline void
|
||||
VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) VL_ATTR_NO_SANITIZE_ALIGN;
|
||||
|
||||
static inline void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) {
|
||||
// Copy the whole suffix (this avoid having hard to predict branches which
|
||||
// helps a lot). Note: The maximum length of the suffix is
|
||||
// VL_TRACE_MAX_VCD_CODE_SIZE + 2 == 7, but we unroll this here for speed.
|
||||
|
|
@ -605,6 +603,12 @@ void VerilatedVcd::finishLine(vluint32_t code, char* writep) {
|
|||
writep[5] = suffixp[5];
|
||||
writep[6] = '\n'; // The 6th index is always '\n' if it's relevant, no need to fetch it.
|
||||
#endif
|
||||
}
|
||||
|
||||
void VerilatedVcd::finishLine(vluint32_t code, char* writep) {
|
||||
const char* const suffixp = m_suffixes.data() + code * VL_TRACE_SUFFIX_ENTRY_SIZE;
|
||||
VerilatedVcdCCopyAndAppendNewLine(writep, suffixp);
|
||||
|
||||
// Now write back the write pointer incremented by the actual size of the
|
||||
// suffix, which was stored in the last byte of the suffix buffer entry.
|
||||
m_writep = writep + suffixp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1];
|
||||
|
|
|
|||
|
|
@ -80,7 +80,6 @@ private:
|
|||
vluint64_t m_wroteBytes = 0; // Number of bytes written to this file
|
||||
|
||||
std::vector<char> m_suffixes; // VCD line end string codes + metadata
|
||||
const char* m_suffixesp; // Pointer to first element of above
|
||||
|
||||
using NameMap = std::map<const std::string, const std::string>;
|
||||
NameMap* m_namemapp = nullptr; // List of names for the header
|
||||
|
|
@ -369,6 +368,8 @@ public:
|
|||
/// Flush dump
|
||||
void flush() VL_MT_SAFE { m_sptrace.flush(); }
|
||||
/// Write one cycle of dump data
|
||||
/// Call with the current context's time just after eval'ed,
|
||||
/// e.g. ->dump(contextp->time())
|
||||
void dump(vluint64_t timeui) VL_MT_SAFE { m_sptrace.dump(timeui); }
|
||||
/// Write one cycle of dump data - backward compatible and to reduce
|
||||
/// conversion warnings. It's better to use a vluint64_t time instead.
|
||||
|
|
|
|||
|
|
@ -43,6 +43,12 @@
|
|||
# define VL_ATTR_COLD __attribute__((cold))
|
||||
# define VL_ATTR_HOT __attribute__((hot))
|
||||
# define VL_ATTR_NORETURN __attribute__((noreturn))
|
||||
// clang and gcc-8.0+ support no_sanitize("string") style attribute
|
||||
# if defined(__clang__) || (__GNUC__ >= 8)
|
||||
# define VL_ATTR_NO_SANITIZE_ALIGN __attribute__((no_sanitize("alignment")))
|
||||
#else // The entire undefined sanitizer has to be disabled for older gcc
|
||||
# define VL_ATTR_NO_SANITIZE_ALIGN __attribute__((no_sanitize_undefined))
|
||||
#endif
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) __attribute__((format(printf, (fmtArgNum), (fmtArgNum) + 1)))
|
||||
# define VL_ATTR_PURE __attribute__((pure))
|
||||
# define VL_ATTR_UNUSED __attribute__((unused))
|
||||
|
|
@ -85,6 +91,9 @@
|
|||
#ifndef VL_ATTR_NORETURN
|
||||
# define VL_ATTR_NORETURN ///< Attribute that function does not ever return
|
||||
#endif
|
||||
#ifndef VL_ATTR_NO_SANITIZE_ALIGN
|
||||
# define VL_ATTR_NO_SANITIZE_ALIGN ///< Attribute that the function contains intended unaligned access
|
||||
#endif
|
||||
#ifndef VL_ATTR_PRINTF
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) ///< Attribute for function with printf format checking
|
||||
#endif
|
||||
|
|
@ -428,6 +437,7 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
|||
(((nbits) & VL_SIZEBITS_Q) ? ((1ULL << ((nbits) & VL_SIZEBITS_Q)) - 1ULL) : ~0ULL)
|
||||
/// Return mask for EData with 1's where relevant bits are (0=all bits)
|
||||
#define VL_MASK_E(nbits) VL_MASK_I(nbits)
|
||||
|
||||
#define VL_EUL(n) VL_UL(n) // Make constant number EData sized
|
||||
|
||||
#define VL_BITWORD_I(bit) ((bit) / VL_IDATASIZE) ///< Word number for sv DPI vectors
|
||||
|
|
|
|||
|
|
@ -47,25 +47,25 @@ obj_dbg:
|
|||
|
||||
.SUFFIXES:
|
||||
|
||||
.PHONY: ../bin/verilator_bin ../bin/verilator_bin_dbg ../bin/verilator_coverage_bin_dbg
|
||||
.PHONY: ../bin/verilator_bin$(EXEEXT) ../bin/verilator_bin_dbg$(EXEEXT) ../bin/verilator_coverage_bin_dbg$(EXEEXT)
|
||||
|
||||
opt: ../bin/verilator_bin
|
||||
opt: ../bin/verilator_bin$(EXEEXT)
|
||||
ifeq ($(VERILATOR_NO_OPT_BUILD),1) # Faster laptop development... One build
|
||||
../bin/verilator_bin: ../bin/verilator_bin_dbg
|
||||
-cp -p $<$(EXEEXT) $@$(EXEEXT).tmp
|
||||
-mv -f $@$(EXEEXT).tmp $@$(EXEEXT)
|
||||
../bin/verilator_bin$(EXEEXT): ../bin/verilator_bin_dbg$(EXEEXT)
|
||||
-cp -p $< $@.tmp
|
||||
-mv -f $@.tmp $@
|
||||
else
|
||||
../bin/verilator_bin: obj_opt ../bin prefiles
|
||||
../bin/verilator_bin$(EXEEXT): obj_opt ../bin prefiles
|
||||
$(MAKE) -C obj_opt -j 1 TGT=../$@ -f ../Makefile_obj serial
|
||||
$(MAKE) -C obj_opt TGT=../$@ -f ../Makefile_obj
|
||||
endif
|
||||
|
||||
dbg: ../bin/verilator_bin_dbg ../bin/verilator_coverage_bin_dbg
|
||||
../bin/verilator_bin_dbg: obj_dbg ../bin prefiles
|
||||
dbg: ../bin/verilator_bin_dbg$(EXEEXT) ../bin/verilator_coverage_bin_dbg$(EXEEXT)
|
||||
../bin/verilator_bin_dbg$(EXEEXT): obj_dbg ../bin prefiles
|
||||
$(MAKE) -C obj_dbg -j 1 TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj serial
|
||||
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj
|
||||
|
||||
../bin/verilator_coverage_bin_dbg: obj_dbg ../bin prefiles
|
||||
../bin/verilator_coverage_bin_dbg$(EXEEXT): obj_dbg ../bin prefiles
|
||||
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj serial_vlcov
|
||||
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ CFG_LIBS = @CFG_LIBS@
|
|||
#### End of system configuration section. ####
|
||||
|
||||
VPATH += . $(bldsrc) $(srcdir)
|
||||
TGT = ../../verilator_bin
|
||||
TGT = ../../verilator_bin$(EXEEXT)
|
||||
|
||||
#################
|
||||
ifeq ($(VL_DEBUG),)
|
||||
|
|
@ -181,6 +181,7 @@ RAW_OBJS = \
|
|||
V3Depth.o \
|
||||
V3DepthBlock.o \
|
||||
V3Descope.o \
|
||||
V3DupFinder.o \
|
||||
V3EmitC.o \
|
||||
V3EmitCInlines.o \
|
||||
V3EmitCSyms.o \
|
||||
|
|
@ -202,7 +203,8 @@ RAW_OBJS = \
|
|||
V3GraphDfa.o \
|
||||
V3GraphPathChecker.o \
|
||||
V3GraphTest.o \
|
||||
V3Hashed.o \
|
||||
V3Hash.o \
|
||||
V3Hasher.o \
|
||||
V3HierBlock.o \
|
||||
V3Inline.o \
|
||||
V3Inst.o \
|
||||
|
|
@ -221,6 +223,7 @@ RAW_OBJS = \
|
|||
V3MergeCond.o \
|
||||
V3Name.o \
|
||||
V3Number.o \
|
||||
V3OptionParser.o \
|
||||
V3Options.o \
|
||||
V3Order.o \
|
||||
V3Os.o \
|
||||
|
|
|
|||
|
|
@ -309,16 +309,16 @@ private:
|
|||
}
|
||||
}
|
||||
virtual void visit(AstNodeIf* nodep) {
|
||||
LatchDetectGraphVertex* parentp = m_graph.currentp();
|
||||
LatchDetectGraphVertex* branchp = m_graph.addPathVertex(parentp, "BRANCH", true);
|
||||
m_graph.addPathVertex(branchp, "IF");
|
||||
iterateAndNextNull(nodep->ifsp());
|
||||
m_graph.addPathVertex(branchp, "ELSE");
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
m_graph.currentp(parentp);
|
||||
if (!nodep->isBoundsCheck()) {
|
||||
LatchDetectGraphVertex* parentp = m_graph.currentp();
|
||||
LatchDetectGraphVertex* branchp = m_graph.addPathVertex(parentp, "BRANCH", true);
|
||||
m_graph.addPathVertex(branchp, "IF");
|
||||
iterateAndNextNull(nodep->ifsp());
|
||||
m_graph.addPathVertex(branchp, "ELSE");
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
m_graph.currentp(parentp);
|
||||
}
|
||||
}
|
||||
// Empty visitors, speed things up
|
||||
virtual void visit(AstNodeMath* nodep) {}
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) { iterateChildren(nodep); }
|
||||
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ private:
|
|||
// Like newFireAssert() but omits the asserts-on check
|
||||
AstDisplay* dispp = new AstDisplay(nodep->fileline(), AstDisplayType::DT_ERROR, message,
|
||||
nullptr, nullptr);
|
||||
dispp->fmtp()->timeunit(m_modp->timeunit());
|
||||
AstNode* bodysp = dispp;
|
||||
replaceDisplay(dispp, "%%Error"); // Convert to standard DISPLAY format
|
||||
bodysp->addNext(new AstStop(nodep->fileline(), true));
|
||||
|
|
|
|||
|
|
@ -970,20 +970,6 @@ bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ig
|
|||
&& (ignNext || sameTreeIter(node1p->m_nextp, node2p->m_nextp, false, gateOnly)));
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Static utilities
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const V3Hash& rhs) {
|
||||
return os << std::hex << std::setw(2) << std::setfill('0') << rhs.depth() << "_"
|
||||
<< std::setw(6) << std::setfill('0') << rhs.hshval();
|
||||
}
|
||||
|
||||
V3Hash::V3Hash(const string& name) {
|
||||
uint32_t val = 0;
|
||||
for (const auto& c : name) val = val * 31 + c;
|
||||
setBoth(1, val);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Debugging
|
||||
|
||||
|
|
|
|||
280
src/V3Ast.h
280
src/V3Ast.h
|
|
@ -1271,6 +1271,8 @@ public:
|
|||
virtual ~AstNVisitor() { doDeletes(); }
|
||||
/// Call visit()s on nodep
|
||||
void iterate(AstNode* nodep);
|
||||
/// Call visit()s on nodep
|
||||
void iterateNull(AstNode* nodep);
|
||||
/// Call visit()s on nodep's children
|
||||
void iterateChildren(AstNode* nodep);
|
||||
/// Call visit()s on nodep's children in backp() order
|
||||
|
|
@ -1321,59 +1323,6 @@ inline std::ostream& operator<<(std::ostream& os, const AstNRelinker& rhs) {
|
|||
return os;
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// V3Hash -- Node hashing for V3Combine
|
||||
|
||||
class V3Hash final {
|
||||
// A hash of a tree of nodes, consisting of 8 bits with the number of nodes in the hash
|
||||
// and 24 bit value hash of relevant information about the node.
|
||||
// A value of 0 is illegal
|
||||
uint32_t m_both;
|
||||
static const uint32_t M24 = ((1 << 24) - 1);
|
||||
void setBoth(uint32_t depth, uint32_t hshval) {
|
||||
if (depth == 0) depth = 1;
|
||||
if (depth > 255) depth = 255;
|
||||
m_both = (depth << 24) | (hshval & M24);
|
||||
}
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
bool isIllegal() const { return m_both == 0; }
|
||||
uint32_t fullValue() const { return m_both; }
|
||||
uint32_t depth() const { return (m_both >> 24) & 255; }
|
||||
uint32_t hshval() const { return m_both & M24; }
|
||||
// OPERATORS
|
||||
bool operator==(const V3Hash& rh) const { return m_both == rh.m_both; }
|
||||
bool operator!=(const V3Hash& rh) const { return m_both != rh.m_both; }
|
||||
bool operator<(const V3Hash& rh) const { return m_both < rh.m_both; }
|
||||
// CONSTRUCTORS
|
||||
class Illegal {}; // for creator type-overload selection
|
||||
class FullValue {}; // for creator type-overload selection
|
||||
explicit V3Hash(Illegal) { m_both = 0; }
|
||||
// Saving and restoring inside a userp
|
||||
explicit V3Hash(const VNUser& u) { m_both = u.toInt(); }
|
||||
V3Hash operator+=(const V3Hash& rh) {
|
||||
setBoth(depth() + rh.depth(), (hshval() * 31 + rh.hshval()));
|
||||
return *this;
|
||||
}
|
||||
// Creating from raw data (sameHash functions)
|
||||
V3Hash() { setBoth(1, 0); }
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
V3Hash(uint32_t val) { setBoth(1, val); }
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
V3Hash(const void* vp) { setBoth(1, cvtToHash(vp)); }
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
V3Hash(const string& name);
|
||||
V3Hash(V3Hash h1, V3Hash h2) { setBoth(1, h1.hshval() * 31 + h2.hshval()); }
|
||||
V3Hash(V3Hash h1, V3Hash h2, V3Hash h3) {
|
||||
setBoth(1, (h1.hshval() * 31 + h2.hshval()) * 31 + h3.hshval());
|
||||
}
|
||||
V3Hash(V3Hash h1, V3Hash h2, V3Hash h3, V3Hash h4) {
|
||||
setBoth(1, ((h1.hshval() * 31 + h2.hshval()) * 31 + h3.hshval()) * 31 + h4.hshval());
|
||||
}
|
||||
};
|
||||
std::ostream& operator<<(std::ostream& os, const V3Hash& rhs);
|
||||
|
||||
//######################################################################
|
||||
// Callback base class to determine if node matches some formula
|
||||
|
||||
|
|
@ -1835,9 +1784,6 @@ public:
|
|||
// statement is unlikely to be taken
|
||||
virtual bool isUnlikely() const { return false; }
|
||||
virtual int instrCount() const { return 0; }
|
||||
virtual V3Hash sameHash() const {
|
||||
return V3Hash(V3Hash::Illegal()); // Not a node that supports it
|
||||
}
|
||||
virtual bool same(const AstNode*) const { return true; }
|
||||
// Iff has a data type; dtype() must be non null
|
||||
virtual bool hasDType() const { return false; }
|
||||
|
|
@ -1912,9 +1858,11 @@ inline void AstNRelinker::relink(AstNode* newp) { newp->AstNode::relink(this); }
|
|||
|
||||
class AstNodeMath VL_NOT_FINAL : public AstNode {
|
||||
// Math -- anything that's part of an expression tree
|
||||
public:
|
||||
protected:
|
||||
AstNodeMath(AstType t, FileLine* fl)
|
||||
: AstNode{t, fl} {}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeMath)
|
||||
// METHODS
|
||||
virtual void dump(std::ostream& str) const override;
|
||||
|
|
@ -1932,9 +1880,11 @@ public:
|
|||
|
||||
class AstNodeTermop VL_NOT_FINAL : public AstNodeMath {
|
||||
// Terminal operator -- a operator with no "inputs"
|
||||
public:
|
||||
protected:
|
||||
AstNodeTermop(AstType t, FileLine* fl)
|
||||
: AstNodeMath{t, fl} {}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeTermop)
|
||||
// Know no children, and hot function, so skip iterator for speed
|
||||
// See checkTreeIter also that asserts no children
|
||||
|
|
@ -1945,12 +1895,14 @@ public:
|
|||
|
||||
class AstNodeUniop VL_NOT_FINAL : public AstNodeMath {
|
||||
// Unary math
|
||||
public:
|
||||
protected:
|
||||
AstNodeUniop(AstType t, FileLine* fl, AstNode* lhsp)
|
||||
: AstNodeMath{t, fl} {
|
||||
dtypeFrom(lhsp);
|
||||
setOp1p(lhsp);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeUniop)
|
||||
AstNode* lhsp() const { return op1p(); }
|
||||
void lhsp(AstNode* nodep) { return setOp1p(nodep); }
|
||||
|
|
@ -1965,18 +1917,19 @@ public:
|
|||
virtual bool signedFlavor() const { return false; }
|
||||
virtual bool stringFlavor() const { return false; } // N flavor of nodes with both flavors?
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
virtual V3Hash sameHash() const override { return V3Hash(); }
|
||||
virtual bool same(const AstNode*) const override { return true; }
|
||||
};
|
||||
|
||||
class AstNodeBiop VL_NOT_FINAL : public AstNodeMath {
|
||||
// Binary math
|
||||
public:
|
||||
protected:
|
||||
AstNodeBiop(AstType t, FileLine* fl, AstNode* lhs, AstNode* rhs)
|
||||
: AstNodeMath{t, fl} {
|
||||
setOp1p(lhs);
|
||||
setOp2p(rhs);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeBiop)
|
||||
// Clone single node, just get same type back.
|
||||
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) = 0;
|
||||
|
|
@ -1997,19 +1950,20 @@ public:
|
|||
virtual bool signedFlavor() const { return false; }
|
||||
virtual bool stringFlavor() const { return false; } // N flavor of nodes with both flavors?
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
virtual V3Hash sameHash() const override { return V3Hash(); }
|
||||
virtual bool same(const AstNode*) const override { return true; }
|
||||
};
|
||||
|
||||
class AstNodeTriop VL_NOT_FINAL : public AstNodeMath {
|
||||
// Trinary math
|
||||
public:
|
||||
protected:
|
||||
AstNodeTriop(AstType t, FileLine* fl, AstNode* lhs, AstNode* rhs, AstNode* ths)
|
||||
: AstNodeMath{t, fl} {
|
||||
setOp1p(lhs);
|
||||
setOp2p(rhs);
|
||||
setOp3p(ths);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeTriop)
|
||||
AstNode* lhsp() const { return op1p(); }
|
||||
AstNode* rhsp() const { return op2p(); }
|
||||
|
|
@ -2030,13 +1984,12 @@ public:
|
|||
virtual bool sizeMattersRhs() const = 0; // True if output result depends on rhs size
|
||||
virtual bool sizeMattersThs() const = 0; // True if output result depends on ths size
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
virtual V3Hash sameHash() const override { return V3Hash(); }
|
||||
virtual bool same(const AstNode*) const override { return true; }
|
||||
};
|
||||
|
||||
class AstNodeQuadop VL_NOT_FINAL : public AstNodeMath {
|
||||
// Quaternary math
|
||||
public:
|
||||
protected:
|
||||
AstNodeQuadop(AstType t, FileLine* fl, AstNode* lhs, AstNode* rhs, AstNode* ths, AstNode* fhs)
|
||||
: AstNodeMath{t, fl} {
|
||||
setOp1p(lhs);
|
||||
|
|
@ -2044,6 +1997,8 @@ public:
|
|||
setOp3p(ths);
|
||||
setOp4p(fhs);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeQuadop)
|
||||
AstNode* lhsp() const { return op1p(); }
|
||||
AstNode* rhsp() const { return op2p(); }
|
||||
|
|
@ -2067,27 +2022,31 @@ public:
|
|||
virtual bool sizeMattersThs() const = 0; // True if output result depends on ths size
|
||||
virtual bool sizeMattersFhs() const = 0; // True if output result depends on ths size
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
virtual V3Hash sameHash() const override { return V3Hash(); }
|
||||
virtual bool same(const AstNode*) const override { return true; }
|
||||
};
|
||||
|
||||
class AstNodeBiCom VL_NOT_FINAL : public AstNodeBiop {
|
||||
// Binary math with commutative properties
|
||||
public:
|
||||
protected:
|
||||
AstNodeBiCom(AstType t, FileLine* fl, AstNode* lhs, AstNode* rhs)
|
||||
: AstNodeBiop{t, fl, lhs, rhs} {}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeBiCom)
|
||||
};
|
||||
|
||||
class AstNodeBiComAsv VL_NOT_FINAL : public AstNodeBiCom {
|
||||
// Binary math with commutative & associative properties
|
||||
public:
|
||||
protected:
|
||||
AstNodeBiComAsv(AstType t, FileLine* fl, AstNode* lhs, AstNode* rhs)
|
||||
: AstNodeBiCom{t, fl, lhs, rhs} {}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeBiComAsv)
|
||||
};
|
||||
|
||||
class AstNodeCond VL_NOT_FINAL : public AstNodeTriop {
|
||||
public:
|
||||
protected:
|
||||
AstNodeCond(AstType t, FileLine* fl, AstNode* condp, AstNode* expr1p, AstNode* expr2p)
|
||||
: AstNodeTriop{t, fl, condp, expr1p, expr2p} {
|
||||
if (expr1p) {
|
||||
|
|
@ -2096,6 +2055,8 @@ public:
|
|||
dtypeFrom(expr2p);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeCond)
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
|
||||
const V3Number& ths) override;
|
||||
|
|
@ -2124,13 +2085,15 @@ class AstNodeBlock VL_NOT_FINAL : public AstNode {
|
|||
private:
|
||||
string m_name; // Name of block
|
||||
bool m_unnamed; // Originally unnamed (name change does not affect this)
|
||||
public:
|
||||
protected:
|
||||
AstNodeBlock(AstType t, FileLine* fl, const string& name, AstNode* stmtsp)
|
||||
: AstNode{t, fl}
|
||||
, m_name{name} {
|
||||
addNOp1p(stmtsp);
|
||||
m_unnamed = (name == "");
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeBlock)
|
||||
virtual void dump(std::ostream& str) const override;
|
||||
virtual string name() const override { return m_name; } // * = Block name
|
||||
|
|
@ -2143,13 +2106,15 @@ public:
|
|||
|
||||
class AstNodePreSel VL_NOT_FINAL : public AstNode {
|
||||
// Something that becomes an AstSel
|
||||
public:
|
||||
protected:
|
||||
AstNodePreSel(AstType t, FileLine* fl, AstNode* fromp, AstNode* rhs, AstNode* ths)
|
||||
: AstNode{t, fl} {
|
||||
setOp1p(fromp);
|
||||
setOp2p(rhs);
|
||||
setNOp3p(ths);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodePreSel)
|
||||
AstNode* fromp() const { return op1p(); }
|
||||
AstNode* rhsp() const { return op2p(); }
|
||||
|
|
@ -2160,17 +2125,18 @@ public:
|
|||
void thsp(AstNode* nodep) { return setOp3p(nodep); }
|
||||
void attrp(AstAttrOf* nodep) { return setOp4p((AstNode*)nodep); }
|
||||
// METHODS
|
||||
virtual V3Hash sameHash() const override { return V3Hash(); }
|
||||
virtual bool same(const AstNode*) const override { return true; }
|
||||
};
|
||||
|
||||
class AstNodeProcedure VL_NOT_FINAL : public AstNode {
|
||||
// IEEE procedure: initial, final, always
|
||||
public:
|
||||
protected:
|
||||
AstNodeProcedure(AstType t, FileLine* fl, AstNode* bodysp)
|
||||
: AstNode{t, fl} {
|
||||
addNOp2p(bodysp);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeProcedure)
|
||||
// METHODS
|
||||
virtual void dump(std::ostream& str) const override;
|
||||
|
|
@ -2182,10 +2148,12 @@ public:
|
|||
class AstNodeStmt VL_NOT_FINAL : public AstNode {
|
||||
// Statement -- anything that's directly under a function
|
||||
bool m_statement; // Really a statement (e.g. not a function with return)
|
||||
public:
|
||||
protected:
|
||||
AstNodeStmt(AstType t, FileLine* fl, bool statement = true)
|
||||
: AstNode{t, fl}
|
||||
, m_statement{statement} {}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeStmt)
|
||||
// METHODS
|
||||
bool isStatement() const { return m_statement; } // Really a statement
|
||||
|
|
@ -2198,13 +2166,15 @@ public:
|
|||
};
|
||||
|
||||
class AstNodeAssign VL_NOT_FINAL : public AstNodeStmt {
|
||||
public:
|
||||
protected:
|
||||
AstNodeAssign(AstType t, FileLine* fl, AstNode* lhsp, AstNode* rhsp)
|
||||
: AstNodeStmt{t, fl} {
|
||||
setOp1p(rhsp);
|
||||
setOp2p(lhsp);
|
||||
dtypeFrom(lhsp);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeAssign)
|
||||
// Clone single node, just get same type back.
|
||||
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) = 0;
|
||||
|
|
@ -2216,14 +2186,13 @@ public:
|
|||
virtual bool hasDType() const override { return true; }
|
||||
virtual bool cleanRhs() const { return true; }
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
virtual V3Hash sameHash() const override { return V3Hash(); }
|
||||
virtual bool same(const AstNode*) const override { return true; }
|
||||
virtual string verilogKwd() const override { return "="; }
|
||||
virtual bool brokeLhsMustBeLvalue() const = 0;
|
||||
};
|
||||
|
||||
class AstNodeFor VL_NOT_FINAL : public AstNodeStmt {
|
||||
public:
|
||||
protected:
|
||||
AstNodeFor(AstType t, FileLine* fl, AstNode* initsp, AstNode* condp, AstNode* incsp,
|
||||
AstNode* bodysp)
|
||||
: AstNodeStmt{t, fl} {
|
||||
|
|
@ -2232,6 +2201,8 @@ public:
|
|||
addNOp3p(incsp);
|
||||
addNOp4p(bodysp);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeFor)
|
||||
AstNode* initsp() const { return op1p(); } // op1 = initial statements
|
||||
AstNode* condp() const { return op2p(); } // op2 = condition to continue
|
||||
|
|
@ -2239,20 +2210,23 @@ public:
|
|||
AstNode* bodysp() const { return op4p(); } // op4 = body of loop
|
||||
virtual bool isGateOptimizable() const override { return false; }
|
||||
virtual int instrCount() const override { return instrCountBranch(); }
|
||||
virtual V3Hash sameHash() const override { return V3Hash(); }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
};
|
||||
|
||||
class AstNodeIf VL_NOT_FINAL : public AstNodeStmt {
|
||||
private:
|
||||
VBranchPred m_branchPred; // Branch prediction as taken/untaken?
|
||||
public:
|
||||
bool m_isBoundsCheck; // True if this if node was inserted for array bounds checking
|
||||
protected:
|
||||
AstNodeIf(AstType t, FileLine* fl, AstNode* condp, AstNode* ifsp, AstNode* elsesp)
|
||||
: AstNodeStmt{t, fl} {
|
||||
setOp1p(condp);
|
||||
addNOp2p(ifsp);
|
||||
addNOp3p(elsesp);
|
||||
isBoundsCheck(false);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeIf)
|
||||
AstNode* condp() const { return op1p(); } // op1 = condition
|
||||
AstNode* ifsp() const { return op2p(); } // op2 = list of true statements
|
||||
|
|
@ -2263,19 +2237,22 @@ public:
|
|||
virtual bool isGateOptimizable() const override { return false; }
|
||||
virtual bool isGateDedupable() const override { return true; }
|
||||
virtual int instrCount() const override { return instrCountBranch(); }
|
||||
virtual V3Hash sameHash() const override { return V3Hash(); }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
void branchPred(VBranchPred flag) { m_branchPred = flag; }
|
||||
VBranchPred branchPred() const { return m_branchPred; }
|
||||
void isBoundsCheck(bool flag) { m_isBoundsCheck = flag; }
|
||||
bool isBoundsCheck() const { return m_isBoundsCheck; }
|
||||
};
|
||||
|
||||
class AstNodeCase VL_NOT_FINAL : public AstNodeStmt {
|
||||
public:
|
||||
protected:
|
||||
AstNodeCase(AstType t, FileLine* fl, AstNode* exprp, AstNode* casesp)
|
||||
: AstNodeStmt{t, fl} {
|
||||
setOp1p(exprp);
|
||||
addNOp2p(casesp);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeCase)
|
||||
virtual int instrCount() const override { return instrCountBranch(); }
|
||||
AstNode* exprp() const { return op1p(); } // op1 = case condition <expression>
|
||||
|
|
@ -2299,7 +2276,7 @@ private:
|
|||
string m_hiernameToUnprot; // Scope converted into name-> for emitting
|
||||
bool m_hierThis = false; // Hiername points to "this" function
|
||||
|
||||
public:
|
||||
protected:
|
||||
AstNodeVarRef(AstType t, FileLine* fl, const string& name, const VAccess& access)
|
||||
: AstNodeMath{t, fl}
|
||||
, m_access{access}
|
||||
|
|
@ -2313,6 +2290,8 @@ public:
|
|||
// May have varp==nullptr
|
||||
this->varp(varp);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeVarRef)
|
||||
virtual void dump(std::ostream& str) const override;
|
||||
virtual bool hasDType() const override { return true; }
|
||||
|
|
@ -2346,15 +2325,16 @@ class AstNodeText VL_NOT_FINAL : public AstNode {
|
|||
private:
|
||||
string m_text;
|
||||
|
||||
public:
|
||||
protected:
|
||||
// Node that simply puts text into the output stream
|
||||
AstNodeText(AstType t, FileLine* fl, const string& textp)
|
||||
: AstNode{t, fl} {
|
||||
m_text = textp; // Copy it
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeText)
|
||||
virtual void dump(std::ostream& str = std::cout) const override;
|
||||
virtual V3Hash sameHash() const override { return V3Hash(text()); }
|
||||
virtual bool same(const AstNode* samep) const override {
|
||||
const AstNodeText* asamep = static_cast<const AstNodeText*>(samep);
|
||||
return text() == asamep->text();
|
||||
|
|
@ -2375,7 +2355,7 @@ private:
|
|||
// Unique number assigned to each dtype during creation for IEEE matching
|
||||
static int s_uniqueNum;
|
||||
|
||||
public:
|
||||
protected:
|
||||
// CONSTRUCTORS
|
||||
AstNodeDType(AstType t, FileLine* fl)
|
||||
: AstNode{t, fl} {
|
||||
|
|
@ -2383,6 +2363,8 @@ public:
|
|||
m_widthMin = 0;
|
||||
m_generic = false;
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeDType)
|
||||
// ACCESSORS
|
||||
virtual void dump(std::ostream& str) const override;
|
||||
|
|
@ -2473,16 +2455,21 @@ private:
|
|||
bool m_packed;
|
||||
bool m_isFourstate;
|
||||
MemberNameMap m_members;
|
||||
const int m_uniqueNum;
|
||||
|
||||
public:
|
||||
protected:
|
||||
AstNodeUOrStructDType(AstType t, FileLine* fl, VSigning numericUnpack)
|
||||
: AstNodeDType{t, fl} {
|
||||
: AstNodeDType{t, fl}
|
||||
, m_uniqueNum{uniqueNumInc()} {
|
||||
// VSigning::NOSIGN overloaded to indicate not packed
|
||||
m_packed = (numericUnpack != VSigning::NOSIGN);
|
||||
m_isFourstate = false; // V3Width computes
|
||||
numeric(VSigning::fromBool(numericUnpack.isSigned()));
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeUOrStructDType)
|
||||
int uniqueNum() const { return m_uniqueNum; }
|
||||
virtual const char* broken() const override;
|
||||
virtual void dump(std::ostream& str) const override;
|
||||
virtual bool isCompound() const override { return false; } // Because don't support unpacked
|
||||
|
|
@ -2534,9 +2521,11 @@ class AstNodeArrayDType VL_NOT_FINAL : public AstNodeDType {
|
|||
private:
|
||||
AstNodeDType* m_refDTypep = nullptr; // Elements of this type (after widthing)
|
||||
AstNode* rangenp() const { return op2p(); } // op2 = Array(s) of variable
|
||||
public:
|
||||
protected:
|
||||
AstNodeArrayDType(AstType t, FileLine* fl)
|
||||
: AstNodeDType{t, fl} {}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeArrayDType)
|
||||
virtual void dump(std::ostream& str) const override;
|
||||
virtual void dumpSmall(std::ostream& str) const override;
|
||||
|
|
@ -2559,9 +2548,6 @@ public:
|
|||
&& rangenp()->sameTree(asamep->rangenp())
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp()));
|
||||
}
|
||||
virtual V3Hash sameHash() const override {
|
||||
return V3Hash(V3Hash(m_refDTypep), V3Hash(hi()), V3Hash(lo()));
|
||||
}
|
||||
virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); }
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
|
|
@ -2594,9 +2580,11 @@ public:
|
|||
|
||||
class AstNodeSel VL_NOT_FINAL : public AstNodeBiop {
|
||||
// Single bit range extraction, perhaps with non-constant selection or array selection
|
||||
public:
|
||||
protected:
|
||||
AstNodeSel(AstType t, FileLine* fl, AstNode* fromp, AstNode* bitp)
|
||||
: AstNodeBiop{t, fl, fromp, bitp} {}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeSel)
|
||||
AstNode* fromp() const {
|
||||
return op1p();
|
||||
|
|
@ -2610,11 +2598,13 @@ public:
|
|||
|
||||
class AstNodeStream VL_NOT_FINAL : public AstNodeBiop {
|
||||
// Verilog {rhs{lhs}} - Note rhsp() is the slice size, not the lhsp()
|
||||
public:
|
||||
protected:
|
||||
AstNodeStream(AstType t, FileLine* fl, AstNode* lhsp, AstNode* rhsp)
|
||||
: AstNodeBiop{t, fl, lhsp, rhsp} {
|
||||
if (lhsp->dtypep()) dtypeSetLogicSized(lhsp->dtypep()->width(), VSigning::UNSIGNED);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeStream)
|
||||
};
|
||||
|
||||
|
|
@ -2629,28 +2619,19 @@ class AstNodeCCall VL_NOT_FINAL : public AstNodeStmt {
|
|||
string m_hiernameToUnprot;
|
||||
string m_argTypes;
|
||||
|
||||
public:
|
||||
protected:
|
||||
AstNodeCCall(AstType t, FileLine* fl, AstCFunc* funcp, AstNode* argsp = nullptr)
|
||||
: AstNodeStmt{t, fl, true}
|
||||
, m_funcp{funcp} {
|
||||
addNOp2p(argsp);
|
||||
}
|
||||
// Replacement form for V3Combine
|
||||
// Note this removes old attachments from the oldp
|
||||
AstNodeCCall(AstType t, AstNodeCCall* oldp, AstCFunc* funcp)
|
||||
: AstNodeStmt{t, oldp->fileline(), true}
|
||||
, m_funcp{funcp} {
|
||||
m_hiernameToProt = oldp->hiernameToProt();
|
||||
m_hiernameToUnprot = oldp->hiernameToUnprot();
|
||||
m_argTypes = oldp->argTypes();
|
||||
if (oldp->argsp()) addNOp2p(oldp->argsp()->unlinkFrBackWithNext());
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeCCall)
|
||||
virtual void dump(std::ostream& str = std::cout) const override;
|
||||
virtual void cloneRelink() override;
|
||||
virtual const char* broken() const override;
|
||||
virtual int instrCount() const override { return instrCountCall(); }
|
||||
virtual V3Hash sameHash() const override { return V3Hash(funcp()); }
|
||||
virtual bool same(const AstNode* samep) const override {
|
||||
const AstNodeCCall* asamep = static_cast<const AstNodeCCall*>(samep);
|
||||
return (funcp() == asamep->funcp() && argTypes() == asamep->argTypes());
|
||||
|
|
@ -2696,7 +2677,7 @@ private:
|
|||
bool m_pureVirtual : 1; // Pure virtual
|
||||
bool m_virtual : 1; // Virtual method in class
|
||||
VLifetime m_lifetime; // Lifetime
|
||||
public:
|
||||
protected:
|
||||
AstNodeFTask(AstType t, FileLine* fl, const string& name, AstNode* stmtsp)
|
||||
: AstNode{t, fl}
|
||||
, m_name{name}
|
||||
|
|
@ -2720,6 +2701,8 @@ public:
|
|||
addNOp3p(stmtsp);
|
||||
cname(name); // Might be overridden by dpi import/export
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeFTask)
|
||||
virtual void dump(std::ostream& str = std::cout) const override;
|
||||
virtual string name() const override { return m_name; } // * = Var name
|
||||
|
|
@ -2796,7 +2779,7 @@ private:
|
|||
string m_dotted; // Dotted part of scope the name()ed task/func is under or ""
|
||||
string m_inlinedDots; // Dotted hierarchy flattened out
|
||||
bool m_pli = false; // Pli system call ($name)
|
||||
public:
|
||||
protected:
|
||||
AstNodeFTaskRef(AstType t, FileLine* fl, bool statement, AstNode* namep, AstNode* pinsp)
|
||||
: AstNodeStmt{t, fl, statement} {
|
||||
setOp1p(namep);
|
||||
|
|
@ -2807,6 +2790,8 @@ public:
|
|||
, m_name{name} {
|
||||
addNOp3p(pinsp);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeFTaskRef)
|
||||
virtual const char* broken() const override;
|
||||
virtual void cloneRelink() override {
|
||||
|
|
@ -2862,7 +2847,7 @@ private:
|
|||
VLifetime m_lifetime; // Lifetime
|
||||
VTimescale m_timeunit; // Global time unit
|
||||
VOptionBool m_unconnectedDrive; // State of `unconnected_drive
|
||||
public:
|
||||
protected:
|
||||
AstNodeModule(AstType t, FileLine* fl, const string& name)
|
||||
: AstNode{t, fl}
|
||||
, m_name{name}
|
||||
|
|
@ -2875,6 +2860,8 @@ public:
|
|||
, m_internal{false}
|
||||
, m_recursive{false}
|
||||
, m_recursiveClone{false} {}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeModule)
|
||||
virtual void dump(std::ostream& str) const override;
|
||||
virtual bool maybePointedTo() const override { return true; }
|
||||
|
|
@ -2922,21 +2909,22 @@ public:
|
|||
|
||||
class AstNodeRange VL_NOT_FINAL : public AstNode {
|
||||
// A range, sized or unsized
|
||||
public:
|
||||
protected:
|
||||
AstNodeRange(AstType t, FileLine* fl)
|
||||
: AstNode{t, fl} {}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeRange)
|
||||
virtual void dump(std::ostream& str) const override;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
#include "V3AstNodes__gen.h"
|
||||
|
||||
//######################################################################
|
||||
// Inline AstNVisitor METHODS
|
||||
|
||||
inline void AstNVisitor::iterate(AstNode* nodep) { nodep->accept(*this); }
|
||||
inline void AstNVisitor::iterateNull(AstNode* nodep) {
|
||||
if (VL_LIKELY(nodep)) nodep->accept(*this);
|
||||
}
|
||||
inline void AstNVisitor::iterateChildren(AstNode* nodep) { nodep->iterateChildren(*this); }
|
||||
inline void AstNVisitor::iterateChildrenBackwards(AstNode* nodep) {
|
||||
nodep->iterateChildrenBackwards(*this);
|
||||
|
|
@ -2955,71 +2943,7 @@ inline AstNode* AstNVisitor::iterateSubtreeReturnEdits(AstNode* nodep) {
|
|||
}
|
||||
|
||||
//######################################################################
|
||||
// Inline ACCESSORS
|
||||
|
||||
inline int AstNode::width() const { return dtypep() ? dtypep()->width() : 0; }
|
||||
inline int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() : 0; }
|
||||
inline bool AstNode::width1() const { // V3Const uses to know it can optimize
|
||||
return dtypep() && dtypep()->width() == 1;
|
||||
}
|
||||
inline int AstNode::widthInstrs() const {
|
||||
return (!dtypep() ? 1 : (dtypep()->isWide() ? dtypep()->widthWords() : 1));
|
||||
}
|
||||
inline bool AstNode::isDouble() const {
|
||||
return dtypep() && VN_IS(dtypep(), BasicDType) && VN_CAST(dtypep(), BasicDType)->isDouble();
|
||||
}
|
||||
inline bool AstNode::isString() const {
|
||||
return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isString();
|
||||
}
|
||||
inline bool AstNode::isSigned() const { return dtypep() && dtypep()->isSigned(); }
|
||||
|
||||
inline bool AstNode::isZero() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isEqZero());
|
||||
}
|
||||
inline bool AstNode::isNeqZero() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isNeqZero());
|
||||
}
|
||||
inline bool AstNode::isOne() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isEqOne());
|
||||
}
|
||||
inline bool AstNode::isAllOnes() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnes());
|
||||
}
|
||||
inline bool AstNode::isAllOnesV() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnesV());
|
||||
}
|
||||
inline bool AstNode::sameTree(const AstNode* node2p) const {
|
||||
return sameTreeIter(this, node2p, true, false);
|
||||
}
|
||||
inline bool AstNode::sameGateTree(const AstNode* node2p) const {
|
||||
return sameTreeIter(this, node2p, true, true);
|
||||
}
|
||||
|
||||
inline void AstNodeVarRef::varp(AstVar* varp) {
|
||||
m_varp = varp;
|
||||
dtypeFrom(varp);
|
||||
}
|
||||
|
||||
inline bool AstNodeDType::isFourstate() const { return basicp()->isFourstate(); }
|
||||
|
||||
inline void AstNodeArrayDType::rangep(AstRange* nodep) { setOp2p(nodep); }
|
||||
inline int AstNodeArrayDType::left() const { return rangep()->leftConst(); }
|
||||
inline int AstNodeArrayDType::right() const { return rangep()->rightConst(); }
|
||||
inline int AstNodeArrayDType::hi() const { return rangep()->hiConst(); }
|
||||
inline int AstNodeArrayDType::lo() const { return rangep()->loConst(); }
|
||||
inline int AstNodeArrayDType::elementsConst() const { return rangep()->elementsConst(); }
|
||||
inline VNumRange AstNodeArrayDType::declRange() const { return VNumRange{left(), right()}; }
|
||||
|
||||
inline const char* AstNodeFTaskRef::broken() const {
|
||||
BROKEN_RTN(m_taskp && !m_taskp->brokeExists());
|
||||
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline void AstIfaceRefDType::cloneRelink() {
|
||||
if (m_cellp && m_cellp->clonep()) m_cellp = m_cellp->clonep();
|
||||
if (m_ifacep && m_ifacep->clonep()) m_ifacep = m_ifacep->clonep();
|
||||
if (m_modportp && m_modportp->clonep()) m_modportp = m_modportp->clonep();
|
||||
}
|
||||
#include "V3AstNodes.h"
|
||||
|
||||
#endif // Guard
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Ast node inline functions
|
||||
//
|
||||
// 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_V3ASTINLINES_H_
|
||||
#define VERILATOR_V3ASTINLINES_H_
|
||||
|
||||
#ifndef VERILATOR_V3ASTNODES_H_
|
||||
#error "Use V3Ast.h as the include"
|
||||
#include "V3AstNodes.h" // This helps code analysis tools pick up symbols in V3Ast.h and V3AstNodes.h
|
||||
#endif
|
||||
|
||||
//######################################################################
|
||||
// Inline ACCESSORS
|
||||
|
||||
inline int AstNode::width() const { return dtypep() ? dtypep()->width() : 0; }
|
||||
inline int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() : 0; }
|
||||
inline bool AstNode::width1() const { // V3Const uses to know it can optimize
|
||||
return dtypep() && dtypep()->width() == 1;
|
||||
}
|
||||
inline int AstNode::widthInstrs() const {
|
||||
return (!dtypep() ? 1 : (dtypep()->isWide() ? dtypep()->widthWords() : 1));
|
||||
}
|
||||
inline bool AstNode::isDouble() const {
|
||||
return dtypep() && VN_IS(dtypep(), BasicDType) && VN_CAST(dtypep(), BasicDType)->isDouble();
|
||||
}
|
||||
inline bool AstNode::isString() const {
|
||||
return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isString();
|
||||
}
|
||||
inline bool AstNode::isSigned() const { return dtypep() && dtypep()->isSigned(); }
|
||||
|
||||
inline bool AstNode::isZero() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isEqZero());
|
||||
}
|
||||
inline bool AstNode::isNeqZero() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isNeqZero());
|
||||
}
|
||||
inline bool AstNode::isOne() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isEqOne());
|
||||
}
|
||||
inline bool AstNode::isAllOnes() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnes());
|
||||
}
|
||||
inline bool AstNode::isAllOnesV() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnesV());
|
||||
}
|
||||
inline bool AstNode::sameTree(const AstNode* node2p) const {
|
||||
return sameTreeIter(this, node2p, true, false);
|
||||
}
|
||||
inline bool AstNode::sameGateTree(const AstNode* node2p) const {
|
||||
return sameTreeIter(this, node2p, true, true);
|
||||
}
|
||||
|
||||
inline void AstNodeVarRef::varp(AstVar* varp) {
|
||||
m_varp = varp;
|
||||
dtypeFrom(varp);
|
||||
}
|
||||
|
||||
inline bool AstNodeDType::isFourstate() const { return basicp()->isFourstate(); }
|
||||
|
||||
inline void AstNodeArrayDType::rangep(AstRange* nodep) { setOp2p(nodep); }
|
||||
inline int AstNodeArrayDType::left() const { return rangep()->leftConst(); }
|
||||
inline int AstNodeArrayDType::right() const { return rangep()->rightConst(); }
|
||||
inline int AstNodeArrayDType::hi() const { return rangep()->hiConst(); }
|
||||
inline int AstNodeArrayDType::lo() const { return rangep()->loConst(); }
|
||||
inline int AstNodeArrayDType::elementsConst() const { return rangep()->elementsConst(); }
|
||||
inline VNumRange AstNodeArrayDType::declRange() const { return VNumRange{left(), right()}; }
|
||||
|
||||
inline const char* AstNodeFTaskRef::broken() const {
|
||||
BROKEN_RTN(m_taskp && !m_taskp->brokeExists());
|
||||
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline void AstIfaceRefDType::cloneRelink() {
|
||||
if (m_cellp && m_cellp->clonep()) m_cellp = m_cellp->clonep();
|
||||
if (m_ifacep && m_ifacep->clonep()) m_ifacep = m_ifacep->clonep();
|
||||
if (m_modportp && m_modportp->clonep()) m_modportp = m_modportp->clonep();
|
||||
}
|
||||
|
||||
#endif // Guard
|
||||
|
|
@ -25,6 +25,8 @@
|
|||
#include "V3String.h"
|
||||
#include "V3EmitCBase.h"
|
||||
|
||||
#include "V3AstNodes__gen_macros.h" // Generated by 'astgen'
|
||||
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -214,7 +216,7 @@ AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
|||
}
|
||||
|
||||
AstExecGraph::AstExecGraph(FileLine* fileline)
|
||||
: AstNode{AstType::atExecGraph, fileline} {
|
||||
: ASTGEN_SUPER_ExecGraph(fileline) {
|
||||
m_depGraphp = new V3Graph;
|
||||
}
|
||||
AstExecGraph::~AstExecGraph() { VL_DO_DANGLING(delete m_depGraphp, m_depGraphp); }
|
||||
|
|
|
|||
1010
src/V3AstNodes.h
1010
src/V3AstNodes.h
File diff suppressed because it is too large
Load Diff
150
src/V3CCtors.cpp
150
src/V3CCtors.cpp
|
|
@ -32,63 +32,99 @@
|
|||
#include "V3CCtors.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <list>
|
||||
|
||||
class VCtorType final {
|
||||
public:
|
||||
enum en : uint8_t { MODULE, CLASS, COVERAGE };
|
||||
|
||||
class V3CCtorsVisitor final {
|
||||
private:
|
||||
string m_basename;
|
||||
string m_argsp;
|
||||
string m_callargsp;
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstCFunc* m_tlFuncp; // Top level function being built
|
||||
AstCFunc* m_funcp; // Current function
|
||||
enum en m_e;
|
||||
|
||||
public:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline VCtorType(en _e)
|
||||
: m_e{_e} {}
|
||||
bool isClass() const { return m_e == CLASS; }
|
||||
bool isCoverage() const { return m_e == COVERAGE; }
|
||||
};
|
||||
|
||||
class V3CCtorsBuilder final {
|
||||
private:
|
||||
AstNodeModule* const m_modp; // Current module/class
|
||||
const string m_basename;
|
||||
const VCtorType m_type; // What kind of constructor are we creating
|
||||
std::list<AstCFunc*> m_newFunctions; // Created functions, latest is at back
|
||||
int m_numStmts = 0; // Number of statements output
|
||||
int m_funcNum = 0; // Function number being built
|
||||
|
||||
AstCFunc* makeNewFunc() {
|
||||
const int funcNum = m_newFunctions.size();
|
||||
const string funcName = m_basename + "_" + cvtToStr(funcNum);
|
||||
AstCFunc* const funcp = new AstCFunc(m_modp->fileline(), funcName, nullptr, "void");
|
||||
funcp->isStatic(!m_type.isClass()); // Class constructors are non static
|
||||
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) {}";
|
||||
} 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) {}";
|
||||
}
|
||||
preventUnusedStmt += " // Prevent unused\n";
|
||||
funcp->addStmtsp(new AstCStmt(m_modp->fileline(), preventUnusedStmt));
|
||||
m_modp->addStmtp(funcp);
|
||||
m_numStmts = 0;
|
||||
return funcp;
|
||||
}
|
||||
|
||||
public:
|
||||
void add(AstNode* nodep) {
|
||||
if (v3Global.opt.outputSplitCFuncs() && v3Global.opt.outputSplitCFuncs() < m_numStmts) {
|
||||
m_funcp = nullptr;
|
||||
if (v3Global.opt.outputSplitCFuncs() && m_numStmts > v3Global.opt.outputSplitCFuncs()) {
|
||||
m_newFunctions.push_back(makeNewFunc());
|
||||
}
|
||||
if (!m_funcp) {
|
||||
m_funcp = new AstCFunc(m_modp->fileline(), m_basename + "_" + cvtToStr(++m_funcNum),
|
||||
nullptr, "void");
|
||||
m_funcp->isStatic(false);
|
||||
m_funcp->declPrivate(true);
|
||||
m_funcp->slow(!VN_IS(m_modp, Class)); // Only classes construct on fast path
|
||||
m_funcp->argTypes(m_argsp);
|
||||
m_modp->addStmtp(m_funcp);
|
||||
|
||||
// Add a top call to it
|
||||
AstCCall* callp = new AstCCall(m_modp->fileline(), m_funcp);
|
||||
callp->argTypes(m_callargsp);
|
||||
|
||||
m_tlFuncp->addStmtsp(callp);
|
||||
m_numStmts = 0;
|
||||
}
|
||||
m_funcp->addStmtsp(nodep);
|
||||
m_newFunctions.back()->addStmtsp(nodep);
|
||||
m_numStmts += 1;
|
||||
}
|
||||
|
||||
V3CCtorsVisitor(AstNodeModule* nodep, const string& basename, const string& argsp = "",
|
||||
const string& callargsp = "", const string& stmt = "") {
|
||||
m_basename = basename;
|
||||
m_argsp = argsp;
|
||||
m_callargsp = callargsp;
|
||||
m_modp = nodep;
|
||||
m_tlFuncp = new AstCFunc(nodep->fileline(), basename, nullptr, "void");
|
||||
m_tlFuncp->declPrivate(true);
|
||||
m_tlFuncp->isStatic(false);
|
||||
m_tlFuncp->slow(!VN_IS(m_modp, Class)); // Only classes construct on fast path
|
||||
m_tlFuncp->argTypes(m_argsp);
|
||||
if (stmt != "") m_tlFuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
|
||||
m_funcp = m_tlFuncp;
|
||||
m_modp->addStmtp(m_tlFuncp);
|
||||
V3CCtorsBuilder(AstNodeModule* nodep, const string& basename, VCtorType type)
|
||||
: m_modp(nodep)
|
||||
, m_basename{basename}
|
||||
, m_type(type) {
|
||||
// Note: The constructor is always called, even if empty, so we must always create at least
|
||||
// one.
|
||||
m_newFunctions.push_back(makeNewFunc());
|
||||
}
|
||||
~V3CCtorsVisitor() = default;
|
||||
|
||||
~V3CCtorsBuilder() {
|
||||
if (m_newFunctions.size() == 1) {
|
||||
// No split was necessary, rename the one function to the basename
|
||||
m_newFunctions.front()->name(m_basename);
|
||||
} else {
|
||||
// Split was necessary, create root function and call all others from that
|
||||
AstCFunc* const rootFuncp = makeNewFunc();
|
||||
rootFuncp->name(m_basename);
|
||||
for (AstCFunc* const funcp : m_newFunctions) {
|
||||
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");
|
||||
}
|
||||
rootFuncp->addStmtsp(callp);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
VL_UNCOPYABLE(V3CCtorsVisitor);
|
||||
VL_UNCOPYABLE(V3CCtorsBuilder);
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
|
@ -140,32 +176,24 @@ void V3CCtors::cctorsAll() {
|
|||
modp = VN_CAST(modp->nextp(), NodeModule)) {
|
||||
// Process each module in turn
|
||||
{
|
||||
V3CCtorsVisitor var_reset(
|
||||
modp, "_ctor_var_reset",
|
||||
(VN_IS(modp, Class) ? EmitCBaseVisitor::symClassVar() : ""),
|
||||
(VN_IS(modp, Class) ? "vlSymsp" : ""),
|
||||
(VN_IS(modp, Class) ? "if (false && vlSymsp) {} // Prevent unused\n" : ""));
|
||||
V3CCtorsBuilder var_reset(modp, "_ctor_var_reset",
|
||||
VN_IS(modp, Class) ? VCtorType::CLASS : VCtorType::MODULE);
|
||||
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstVar* varp = VN_CAST(np, Var)) {
|
||||
if (AstVar* const varp = VN_CAST(np, Var)) {
|
||||
if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset()) {
|
||||
var_reset.add(
|
||||
new AstCReset(varp->fileline(),
|
||||
new AstVarRef(varp->fileline(), varp, VAccess::WRITE)));
|
||||
const auto vrefp = new AstVarRef(varp->fileline(), varp, VAccess::WRITE);
|
||||
var_reset.add(new AstCReset(varp->fileline(), vrefp));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (v3Global.opt.coverage()) {
|
||||
V3CCtorsVisitor configure_coverage(
|
||||
modp, "_configure_coverage", EmitCBaseVisitor::symClassVar() + ", bool first",
|
||||
"vlSymsp, first", "if (false && vlSymsp && first) {} // Prevent unused\n");
|
||||
V3CCtorsBuilder configure_coverage(modp, "_configure_coverage", VCtorType::COVERAGE);
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstCoverDecl* coverp = VN_CAST(np, CoverDecl)) {
|
||||
AstNode* backp = coverp->backp();
|
||||
coverp->unlinkFrBack();
|
||||
configure_coverage.add(coverp);
|
||||
np = backp;
|
||||
if (AstCoverDecl* const coverp = VN_CAST(np, CoverDecl)) {
|
||||
np = coverp->backp();
|
||||
configure_coverage.add(coverp->unlinkFrBack());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -301,20 +301,6 @@ private:
|
|||
}
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
virtual void visit(AstAlwaysPost* nodep) override {
|
||||
if (AstNode* stmtsp = nodep->bodysp()) {
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
nodep->addNextHere(stmtsp);
|
||||
}
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
virtual void visit(AstAlwaysPostponed* nodep) override {
|
||||
if (AstNode* stmtsp = nodep->bodysp()) {
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
nodep->addNextHere(stmtsp);
|
||||
}
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
virtual void visit(AstCoverToggle* nodep) override {
|
||||
// nodep->dumpTree(cout, "ct:");
|
||||
// COVERTOGGLE(INC, ORIG, CHANGE) ->
|
||||
|
|
|
|||
|
|
@ -15,19 +15,8 @@
|
|||
//*************************************************************************
|
||||
// V3Combine's Transformations:
|
||||
//
|
||||
// For every function that we spit out
|
||||
// Examine code to find largest common blocks
|
||||
// Hash each node depth first
|
||||
// Hash includes varp name and operator type, and constants
|
||||
// Form lookup table based on hash of each statement w/ nodep and next nodep
|
||||
// GO through table
|
||||
// Lookup in hash, while next of each statement match, grow that common block
|
||||
// Foreach common block
|
||||
// If common block large enough (> 20 statements) & used 2x or more
|
||||
// Make new function
|
||||
// Move common block to function
|
||||
// Replace each common block ref with funccall
|
||||
//
|
||||
// Combine identical CFuncs by retaining only a single copy
|
||||
// Also drop empty CFuncs
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
|
|
@ -35,7 +24,7 @@
|
|||
|
||||
#include "V3Global.h"
|
||||
#include "V3Combine.h"
|
||||
#include "V3Hashed.h"
|
||||
#include "V3DupFinder.h"
|
||||
#include "V3Stats.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
|
|
@ -45,12 +34,6 @@
|
|||
|
||||
//######################################################################
|
||||
|
||||
#ifdef VL_COMBINE_STATEMENTS
|
||||
constexpr int COMBINE_MIN_STATEMENTS = 50; // Min # of statements to be worth making a function
|
||||
#endif
|
||||
|
||||
//######################################################################
|
||||
|
||||
class CombBaseVisitor VL_NOT_FINAL : public AstNVisitor {
|
||||
protected:
|
||||
// STATE
|
||||
|
|
@ -67,8 +50,7 @@ class CombCallVisitor final : CombBaseVisitor {
|
|||
// Find all CCALLS of each CFUNC, so that we can later rename them
|
||||
private:
|
||||
// NODE STATE
|
||||
using CallMmap = std::multimap<AstCFunc*, AstCCall*>;
|
||||
CallMmap m_callMmap; // Associative array of {function}{call}
|
||||
std::multimap<AstCFunc*, AstCCall*> m_callMmap; // Associative array of {function}{call}
|
||||
// METHODS
|
||||
public:
|
||||
void replaceFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
|
||||
|
|
@ -81,44 +63,32 @@ public:
|
|||
// Note: m_callMmap modified in loop, so not using equal_range.
|
||||
for (auto it = m_callMmap.find(oldfuncp); it != m_callMmap.end();
|
||||
it = m_callMmap.find(oldfuncp)) {
|
||||
AstCCall* callp = it->second;
|
||||
if (!callp->user3()) { // !already done
|
||||
UINFO(4, " Called " << callp << endl);
|
||||
UASSERT_OBJ(callp->funcp() == oldfuncp, callp,
|
||||
"Call list broken, points to call w/different func");
|
||||
if (newfuncp) {
|
||||
AstCCall* newp = new AstCCall(callp, newfuncp);
|
||||
// Special new AstCCall form above transfers children of callp to newfuncp
|
||||
callp->replaceWith(newp);
|
||||
addCall(newp); // Fix the table
|
||||
} else { // Just deleting empty function
|
||||
callp->unlinkFrBack();
|
||||
}
|
||||
callp->user3(true); // Dead now
|
||||
VL_DO_DANGLING(pushDeletep(callp), callp);
|
||||
AstCCall* const oldp = it->second;
|
||||
UINFO(4, " Called " << oldp << endl);
|
||||
UASSERT_OBJ(oldp->funcp() == oldfuncp, oldp,
|
||||
"Call list broken, points to call w/different func");
|
||||
if (newfuncp) {
|
||||
// Replace call to oldfuncp with call to newfuncp
|
||||
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->argTypes(oldp->argTypes());
|
||||
addCall(newp); // Fix the table, in case the newfuncp itself gets replaced
|
||||
oldp->replaceWith(newp);
|
||||
} else {
|
||||
// Just deleting empty function
|
||||
oldp->unlinkFrBack();
|
||||
}
|
||||
// It is safe to unconditionally remove this entry here as the above
|
||||
// 'if' would never be entered again for this entry (we set user3).
|
||||
// The only other place where m_callMmap is looked up is deleteCall
|
||||
// below, but that is only ever called straight after an addCall
|
||||
// of the node being deleted, so it won't miss this entry.
|
||||
m_callMmap.erase(it); // Fix the table
|
||||
VL_DO_DANGLING(pushDeletep(oldp), oldp);
|
||||
m_callMmap.erase(it); // Fix the table, This call has been replaced
|
||||
}
|
||||
}
|
||||
// METHODS
|
||||
void addCall(AstCCall* nodep) { m_callMmap.emplace(nodep->funcp(), nodep); }
|
||||
void deleteCall(AstCCall* nodep) {
|
||||
std::pair<CallMmap::iterator, CallMmap::iterator> eqrange
|
||||
= m_callMmap.equal_range(nodep->funcp());
|
||||
for (auto nextit = eqrange.first; nextit != eqrange.second;) {
|
||||
const auto eqit = nextit++;
|
||||
AstCCall* callp = eqit->second;
|
||||
if (callp == nodep) {
|
||||
m_callMmap.erase(eqit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
nodep->v3fatalSrc("deleteCall node not found in table");
|
||||
void addCall(AstCCall* nodep) {
|
||||
if (nodep->funcp()->dontCombine()) return;
|
||||
m_callMmap.emplace(nodep->funcp(), nodep);
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -136,26 +106,6 @@ public:
|
|||
void main(AstNetlist* nodep) { iterate(nodep); }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Combine marking function
|
||||
|
||||
class CombMarkVisitor final : CombBaseVisitor {
|
||||
// Mark all nodes under specified one.
|
||||
private:
|
||||
// OUTPUT:
|
||||
// AstNode::user3() -> bool. True to indicate duplicated
|
||||
// VISITORS
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
nodep->user3(true);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CombMarkVisitor(AstNode* nodep) { iterate(nodep); }
|
||||
virtual ~CombMarkVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Combine state, as a visitor of each AstNode
|
||||
|
||||
|
|
@ -163,312 +113,100 @@ class CombineVisitor final : CombBaseVisitor {
|
|||
private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstNodeStmt::user() -> bool. True if iterated already
|
||||
// AstCFunc::user3p() -> AstCFunc*, If set, replace ccalls to this func with new func
|
||||
// AstNodeStmt::user3() -> AstNode*. True if to ignore this cell
|
||||
// AstNodeStmt::user4() -> V3Hashed::V3Hash. Hash value of this node (hash of 0 is
|
||||
// illegal)
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser3InUse m_inuser3;
|
||||
// AstUser4InUse part of V3Hashed
|
||||
AstUser3InUse m_user3InUse; // Marks replaced AstCFuncs
|
||||
// AstUser4InUse part of V3Hasher in V3DupFinder
|
||||
|
||||
// STATE
|
||||
enum CombineState : uint8_t { STATE_IDLE, STATE_HASH, STATE_DUP };
|
||||
VDouble0 m_statCombs; // Statistic tracking
|
||||
CombineState m_state = STATE_IDLE; // Major state
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstCFunc* m_cfuncp = nullptr; // Current function
|
||||
VDouble0 m_cfuncsCombined; // Statistic tracking
|
||||
CombCallVisitor m_call; // Tracking of function call users
|
||||
int m_modNFuncs = 0; // Number of functions made
|
||||
#ifdef VL_COMBINE_STATEMENTS
|
||||
AstNode* m_walkLast1p = nullptr; // Final node that is the same in duplicate list
|
||||
#endif
|
||||
AstNode* m_walkLast2p = nullptr; // Final node that is the same in duplicate list
|
||||
V3Hashed m_hashed; // Hash for every node in module
|
||||
V3DupFinder m_dupFinder; // Duplicate finder for CFuncs in module
|
||||
|
||||
// METHODS
|
||||
void hashStatement(AstNode* nodep) {
|
||||
// Compute hash on entire tree of this statement
|
||||
m_hashed.hashAndInsert(nodep);
|
||||
// UINFO(9, " stmthash " << hex << nodep->user4() << " " << nodep << endl);
|
||||
}
|
||||
#ifdef VL_COMBINE_STATEMENTS
|
||||
void hashFunctions(AstCFunc* nodep) {
|
||||
// Compute hash of all statement trees in the function
|
||||
VL_RESTORER(m_state);
|
||||
{
|
||||
m_state = STATE_HASH;
|
||||
iterate(nodep);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
void walkEmptyFuncs() {
|
||||
for (const auto& itr : m_hashed) {
|
||||
AstNode* node1p = itr.second;
|
||||
AstCFunc* oldfuncp = VN_CAST(node1p, CFunc);
|
||||
if (oldfuncp && oldfuncp->emptyBody() && !oldfuncp->dontCombine()) {
|
||||
UINFO(5, " EmptyFunc " << std::hex << V3Hash(oldfuncp->user4p()) << " "
|
||||
<< oldfuncp << endl);
|
||||
// Mark user3p on entire old tree, so we don't process it more
|
||||
CombMarkVisitor visitor(oldfuncp);
|
||||
m_call.replaceFunc(oldfuncp, nullptr);
|
||||
oldfuncp->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(oldfuncp), oldfuncp);
|
||||
}
|
||||
for (const auto& itr : m_dupFinder) {
|
||||
AstCFunc* const oldfuncp = VN_CAST(itr.second, CFunc);
|
||||
UASSERT_OBJ(oldfuncp, itr.second, "Not a CFunc in hash");
|
||||
if (!oldfuncp->emptyBody()) continue;
|
||||
UASSERT_OBJ(!oldfuncp->dontCombine(), oldfuncp,
|
||||
"dontCombine function should not be in hash");
|
||||
|
||||
// Remove calls to empty function
|
||||
UASSERT_OBJ(!oldfuncp->user3(), oldfuncp, "Should not be processed yet");
|
||||
UINFO(5, " Drop empty CFunc " << itr.first << " " << oldfuncp << endl);
|
||||
oldfuncp->user3SetOnce(); // Mark replaced
|
||||
m_call.replaceFunc(oldfuncp, nullptr);
|
||||
oldfuncp->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(oldfuncp), oldfuncp);
|
||||
}
|
||||
}
|
||||
|
||||
void walkDupFuncs() {
|
||||
// Do non-slow first as then favors naming functions based on fast name
|
||||
for (int slow = 0; slow < 2; ++slow) {
|
||||
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
|
||||
AstNode* node1p = it->second;
|
||||
AstCFunc* cfunc1p = VN_CAST(node1p, CFunc);
|
||||
if (!cfunc1p) continue;
|
||||
// cppcheck-suppress compareBoolExpressionWithInt
|
||||
if (cfunc1p->slow() != slow) continue;
|
||||
V3Hash hashval = it->first;
|
||||
UASSERT_OBJ(!hashval.isIllegal(), node1p, "Illegal (unhashed) nodes");
|
||||
for (V3Hashed::iterator eqit = it; eqit != m_hashed.end(); ++eqit) {
|
||||
AstNode* node2p = eqit->second;
|
||||
if (!(eqit->first == hashval)) break;
|
||||
if (node1p == node2p) continue; // Identical iterator
|
||||
if (node1p->user3p() || node2p->user3p()) continue; // Already merged
|
||||
if (node1p->sameTree(node2p)) { // walk of tree has same comparison
|
||||
// Replace AstCCall's that point here
|
||||
replaceFuncWFunc(VN_CAST(node2p, CFunc), cfunc1p);
|
||||
// Replacement may promote a slow routine to fast path
|
||||
if (!VN_CAST(node2p, CFunc)->slow()) cfunc1p->slow(false);
|
||||
}
|
||||
for (const bool slow : {false, true}) {
|
||||
for (auto newIt = m_dupFinder.begin(); newIt != m_dupFinder.end(); ++newIt) {
|
||||
AstCFunc* const newfuncp = VN_CAST(newIt->second, CFunc);
|
||||
UASSERT_OBJ(newfuncp, newIt->second, "Not a CFunc in hash");
|
||||
if (newfuncp->user3()) continue; // Already replaced
|
||||
if (newfuncp->slow() != slow) continue;
|
||||
auto oldIt = newIt;
|
||||
++oldIt; // Skip over current position
|
||||
for (; oldIt != m_dupFinder.end(); ++oldIt) {
|
||||
AstCFunc* const oldfuncp = VN_CAST(oldIt->second, CFunc);
|
||||
UASSERT_OBJ(oldfuncp, oldIt->second, "Not a CFunc in hash");
|
||||
UASSERT_OBJ(newfuncp != oldfuncp, newfuncp,
|
||||
"Same function hashed multiple times");
|
||||
if (newIt->first != oldIt->first) break; // Iterate over same hashes only
|
||||
if (oldfuncp->user3()) continue; // Already replaced
|
||||
if (!newfuncp->sameTree(oldfuncp)) continue; // Different functions
|
||||
|
||||
// Replace calls to oldfuncp with calls to newfuncp
|
||||
UINFO(5, " Replace CFunc " << newIt->first << " " << newfuncp << endl);
|
||||
UINFO(5, " with " << oldIt->first << " " << oldfuncp << endl);
|
||||
++m_cfuncsCombined;
|
||||
oldfuncp->user3SetOnce(); // Mark replaced
|
||||
m_call.replaceFunc(oldfuncp, newfuncp);
|
||||
oldfuncp->unlinkFrBack();
|
||||
// Replacement may promote a slow routine to fast path
|
||||
if (!oldfuncp->slow()) newfuncp->slow(false);
|
||||
VL_DO_DANGLING(pushDeletep(oldfuncp), oldfuncp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void replaceFuncWFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
|
||||
UINFO(5, " DupFunc " << std::hex << V3Hash(newfuncp->user4p()) << " " << newfuncp
|
||||
<< endl);
|
||||
UINFO(5, " and " << std::hex << V3Hash(oldfuncp->user4p()) << " " << oldfuncp
|
||||
<< endl);
|
||||
// Mark user3p on entire old tree, so we don't process it more
|
||||
++m_statCombs;
|
||||
CombMarkVisitor visitor(oldfuncp);
|
||||
m_call.replaceFunc(oldfuncp, newfuncp);
|
||||
oldfuncp->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(oldfuncp), oldfuncp);
|
||||
}
|
||||
|
||||
#ifdef VL_COMBINE_STATEMENTS
|
||||
void replaceOnlyCallFunc(AstCCall* nodep) {
|
||||
if (AstCFunc* oldfuncp = VN_CAST(nodep->backp(), CFunc)) {
|
||||
// oldfuncp->dumpTree(cout, "MAYDEL: ");
|
||||
if (nodep->nextp() == nullptr && oldfuncp->initsp() == nullptr
|
||||
&& oldfuncp->stmtsp() == nodep && oldfuncp->finalsp() == nullptr) {
|
||||
UINFO(9, " Function only has call " << oldfuncp << endl);
|
||||
m_call.deleteCall(nodep);
|
||||
CombMarkVisitor visitor(oldfuncp);
|
||||
VL_DO_DANGLING(replaceFuncWFunc(oldfuncp, nodep->funcp()), nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void walkDupCodeStart(AstNode* node1p) {
|
||||
V3Hash hashval(node1p->user4p());
|
||||
// UINFO(4," STMT " << hashval << " " << node1p << endl);
|
||||
//
|
||||
int bestDepth = 0; // Best substitution found in the search
|
||||
AstNode* bestNode2p = nullptr;
|
||||
AstNode* bestLast1p = nullptr;
|
||||
AstNode* bestLast2p = nullptr;
|
||||
//
|
||||
std::pair<V3Hashed::iterator, V3Hashed::iterator> eqrange
|
||||
= m_hashed.mmap().equal_range(hashval);
|
||||
for (V3Hashed::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) {
|
||||
AstNode* node2p = eqit->second;
|
||||
if (node1p == node2p) continue;
|
||||
//
|
||||
// We need to mark iteration to prevent matching code inside
|
||||
// code (abab matching in ababab)
|
||||
AstNode::user1ClearTree(); // user1p() used on entire tree
|
||||
m_walkLast1p = nullptr;
|
||||
m_walkLast2p = nullptr;
|
||||
int depth = walkDupCodeNext(node1p, node2p, 1);
|
||||
if (depth > COMBINE_MIN_STATEMENTS && depth > bestDepth) {
|
||||
bestDepth = depth;
|
||||
bestNode2p = node2p;
|
||||
bestLast1p = m_walkLast1p;
|
||||
bestLast2p = m_walkLast2p;
|
||||
}
|
||||
}
|
||||
if (bestDepth) {
|
||||
// Found a replacement
|
||||
UINFO(5, " Duplicate of depth " << bestDepth << endl);
|
||||
UINFO(5, " DupFunc "
|
||||
<< " " << node1p << endl);
|
||||
UINFO(5, " and "
|
||||
<< " " << bestNode2p << endl);
|
||||
UINFO(5, " Through "
|
||||
<< " " << bestLast1p << endl);
|
||||
UINFO(5, " and "
|
||||
<< " " << bestLast2p << endl);
|
||||
//
|
||||
walkReplace(node1p, bestNode2p, bestLast1p, bestLast2p);
|
||||
}
|
||||
}
|
||||
|
||||
int walkDupCodeNext(AstNode* node1p, AstNode* node2p, int level) {
|
||||
// Find number of common statements between the two node1p_nextp's...
|
||||
if (node1p->user1p() || node2p->user1p()) return 0; // Already iterated
|
||||
if (node1p->user3p() || node2p->user3p()) return 0; // Already merged
|
||||
if (!m_hashed.sameNodes(node1p, node2p)) return 0; // walk of tree has same comparison
|
||||
V3Hash hashval(node1p->user4p());
|
||||
// UINFO(9, " wdup1 "<<level<<" "<<V3Hash(node1p->user4p())<<" "<<node1p<<endl);
|
||||
// UINFO(9, " wdup2 "<<level<<" "<<V3Hash(node2p->user4p())<<" "<<node2p<<endl);
|
||||
m_walkLast1p = node1p;
|
||||
m_walkLast2p = node2p;
|
||||
node1p->user1(true);
|
||||
node2p->user1(true);
|
||||
if (node1p->nextp() && node2p->nextp()) {
|
||||
return hashval.depth() + walkDupCodeNext(node1p->nextp(), node2p->nextp(), level + 1);
|
||||
}
|
||||
return hashval.depth();
|
||||
}
|
||||
|
||||
void walkReplace(AstNode* node1p, AstNode* node2p, AstNode* last1p,
|
||||
AstNode* last2p) { // Final node in linked list, maybe null if all statements
|
||||
// to be grabbed
|
||||
// Make new function
|
||||
string oldname = m_cfuncp->name();
|
||||
string::size_type pos;
|
||||
if ((pos = oldname.find("_common")) != string::npos) oldname.erase(pos);
|
||||
if ((pos = oldname.find("__")) != string::npos) oldname.erase(pos);
|
||||
AstCFunc* newfuncp = new AstCFunc(node1p->fileline(),
|
||||
oldname + "_common" + cvtToStr(++m_modNFuncs), nullptr);
|
||||
m_modp->addStmtp(newfuncp);
|
||||
// Create calls
|
||||
AstCCall* call1p = new AstCCall(node1p->fileline(), newfuncp);
|
||||
AstCCall* call2p = new AstCCall(node2p->fileline(), newfuncp);
|
||||
// Grab statement bodies
|
||||
AstNRelinker relink1Handle;
|
||||
AstNRelinker relink2Handle;
|
||||
for (AstNode *nextp, *walkp = node1p; true; walkp = nextp) {
|
||||
nextp = walkp->nextp();
|
||||
if (walkp == node1p) {
|
||||
walkp->unlinkFrBack(&relink1Handle);
|
||||
} else {
|
||||
walkp->unlinkFrBack();
|
||||
node1p->addNext(walkp);
|
||||
}
|
||||
if (walkp == last1p) break;
|
||||
}
|
||||
for (AstNode *nextp, *walkp = node2p; true; walkp = nextp) {
|
||||
nextp = walkp->nextp();
|
||||
if (walkp == node2p) {
|
||||
walkp->unlinkFrBack(&relink2Handle);
|
||||
} else {
|
||||
walkp->unlinkFrBack();
|
||||
node2p->addNext(walkp);
|
||||
}
|
||||
if (walkp == last2p) break;
|
||||
}
|
||||
// Move node1 statements to new function
|
||||
newfuncp->addStmtsp(node1p);
|
||||
// newfuncp->dumpTree(cout, " newfunctree: ");
|
||||
// Mark node2 statements as dead
|
||||
CombMarkVisitor visitor(node2p);
|
||||
pushDeletep(node2p); // Delete later
|
||||
// Link in new function
|
||||
relink1Handle.relink(call1p);
|
||||
relink2Handle.relink(call2p);
|
||||
// Hash the new function
|
||||
hashFunctions(newfuncp);
|
||||
m_call.addCall(call1p);
|
||||
m_call.addCall(call2p);
|
||||
// If either new statement makes a func with only a single call, replace
|
||||
// the above callers to call it directly
|
||||
VL_DO_DANGLING(replaceOnlyCallFunc(call1p), call1p);
|
||||
VL_DO_DANGLING(replaceOnlyCallFunc(call2p), call2p);
|
||||
}
|
||||
#endif
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep) override {
|
||||
// Track all callers of each function
|
||||
m_call.main(nodep);
|
||||
//
|
||||
// In V3Hashed AstNode::user4ClearTree(); // user4p() used on entire tree
|
||||
// Iterate modules backwards, in bottom-up order.
|
||||
// Required so that a module instantiating another can benefit from collapsing.
|
||||
iterateChildrenBackwards(nodep);
|
||||
m_call.main(nodep); // Track all call sites of each function
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
UINFO(4, " MOD " << nodep << endl);
|
||||
m_modp = nodep;
|
||||
m_modNFuncs = 0;
|
||||
m_walkLast2p = nullptr;
|
||||
m_hashed.clear();
|
||||
// Compute hash of all statement trees in the function
|
||||
m_state = STATE_HASH;
|
||||
m_dupFinder.clear();
|
||||
// Compute hash of all CFuncs in the module
|
||||
iterateChildren(nodep);
|
||||
m_state = STATE_IDLE;
|
||||
if (debug() >= 9) m_hashed.dumpFilePrefixed("combine");
|
||||
if (debug() >= 9) m_dupFinder.dumpFilePrefixed("combine");
|
||||
// Walk the hashes removing empty functions
|
||||
walkEmptyFuncs();
|
||||
// Walk the hashes looking for duplicate functions
|
||||
walkDupFuncs();
|
||||
// Walk the statements looking for large replicated code sections
|
||||
// Note this is disabled, it still needed work
|
||||
// Also repair it for DPI functions; when make __common need to ensure proper
|
||||
// flags get inherited from the old to new AstCFunc, and that AstText doesn't
|
||||
// get split between functions causing the text to have a dangling reference.
|
||||
#ifdef VL_COMBINE_STATEMENTS
|
||||
{
|
||||
m_state = STATE_DUP;
|
||||
iterateChildren(nodep);
|
||||
m_state = STATE_IDLE;
|
||||
}
|
||||
#endif
|
||||
m_modp = nullptr;
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
VL_RESTORER(m_cfuncp);
|
||||
m_cfuncp = nodep;
|
||||
if (!nodep->dontCombine()) {
|
||||
if (m_state == STATE_HASH) {
|
||||
hashStatement(nodep); // Hash the entire function - it might be identical
|
||||
}
|
||||
#ifdef VL_COMBINE_STATEMENTS
|
||||
else if (m_state == STATE_DUP) {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeStmt* nodep) override {
|
||||
if (!nodep->isStatement()) {
|
||||
iterateChildren(nodep);
|
||||
return;
|
||||
}
|
||||
if (m_state == STATE_HASH && m_cfuncp) {
|
||||
hashStatement(nodep);
|
||||
}
|
||||
#ifdef VL_COMBINE_STATEMENTS
|
||||
else if (m_state == STATE_DUP && m_cfuncp) {
|
||||
walkDupCodeStart(nodep);
|
||||
}
|
||||
#endif
|
||||
if (nodep->dontCombine()) return;
|
||||
// Hash the entire function
|
||||
m_dupFinder.insert(nodep);
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstVar*) override {}
|
||||
virtual void visit(AstTraceDecl*) override {}
|
||||
virtual void visit(AstTraceInc*) override {}
|
||||
virtual void visit(AstVar*) override {} // Accelerate
|
||||
virtual void visit(AstNodeStmt* nodep) override {} // Accelerate
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CombineVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~CombineVisitor() override { //
|
||||
V3Stats::addStat("Optimizations, Combined CFuncs", m_statCombs);
|
||||
virtual ~CombineVisitor() override {
|
||||
V3Stats::addStat("Optimizations, Combined CFuncs", m_cfuncsCombined);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -96,18 +96,37 @@ class ConstBitOpTreeVisitor final : public AstNVisitor {
|
|||
ConstBitOpTreeVisitor* m_parentp; // ConstBitOpTreeVisitor that holds this VarInfo
|
||||
AstVarRef* m_refp; // Points the variable that this VarInfo covers
|
||||
V3Number m_bitPolarity; // Coefficient of each bit
|
||||
static int widthOfRef(AstVarRef* refp) {
|
||||
if (AstWordSel* selp = VN_CAST(refp->backp(), WordSel)) return selp->width();
|
||||
if (AstCCast* castp = VN_CAST(refp->backp(), CCast)) return castp->width();
|
||||
return refp->width();
|
||||
}
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
bool hasConstantResult() const { return m_constResult >= 0; }
|
||||
bool sameVarAs(const AstNodeVarRef* otherp) const { return m_refp->sameGateTree(otherp); }
|
||||
bool sameVarAs(const AstNodeVarRef* otherp) const { return m_refp->same(otherp); }
|
||||
void setPolarity(bool compBit, int bit) {
|
||||
UASSERT_OBJ(!hasConstantResult(), m_refp, "Already has result of " << m_constResult);
|
||||
UASSERT_OBJ(bit < VL_QUADSIZE, m_refp,
|
||||
"bit:" << bit << " is too big after V3Expand"
|
||||
<< " back:" << m_refp->backp());
|
||||
if (bit >= m_bitPolarity.width()) { // Need to expand m_bitPolarity
|
||||
const V3Number oldPol = std::move(m_bitPolarity);
|
||||
// oldPol.width() is 8, 16, or 32 because this visitor is called after V3Expand
|
||||
// newWidth is increased by 2x because
|
||||
// - CCast will cast to such bitwidth anyway
|
||||
// - can avoid frequent expansion
|
||||
int newWidth = oldPol.width();
|
||||
while (bit >= newWidth) newWidth *= 2;
|
||||
m_bitPolarity = V3Number{m_refp, newWidth};
|
||||
UASSERT_OBJ(newWidth == 16 || newWidth == 32 || newWidth == 64, m_refp,
|
||||
"bit:" << bit << " newWidth:" << newWidth);
|
||||
m_bitPolarity.setAllBitsX();
|
||||
for (int i = 0; i < oldPol.width(); ++i) {
|
||||
if (oldPol.bitIs0(i))
|
||||
m_bitPolarity.setBit(i, '0');
|
||||
else if (oldPol.bitIs1(i))
|
||||
m_bitPolarity.setBit(i, '1');
|
||||
}
|
||||
}
|
||||
UASSERT_OBJ(bit < m_bitPolarity.width(), m_refp,
|
||||
"bit:" << bit << " width:" << m_bitPolarity.width() << m_refp);
|
||||
if (m_bitPolarity.bitIsX(bit)) { // The bit is not yet set
|
||||
m_bitPolarity.setBit(bit, compBit);
|
||||
} else { // Priviously set the bit
|
||||
|
|
@ -129,7 +148,7 @@ class ConstBitOpTreeVisitor final : public AstNVisitor {
|
|||
FileLine* fl = m_refp->fileline();
|
||||
AstNode* srcp = VN_CAST(m_refp->backp(), WordSel);
|
||||
if (!srcp) srcp = m_refp;
|
||||
const int width = widthOfRef(m_refp);
|
||||
const int width = m_bitPolarity.width();
|
||||
|
||||
if (hasConstantResult())
|
||||
return new AstConst{fl,
|
||||
|
|
@ -160,7 +179,7 @@ class ConstBitOpTreeVisitor final : public AstNVisitor {
|
|||
VarInfo(ConstBitOpTreeVisitor* parent, AstVarRef* refp)
|
||||
: m_parentp(parent)
|
||||
, m_refp(refp)
|
||||
, m_bitPolarity(refp, widthOfRef(refp)) {
|
||||
, m_bitPolarity(refp, refp->isWide() ? VL_EDATASIZE : refp->width()) {
|
||||
m_bitPolarity.setAllBitsX();
|
||||
}
|
||||
};
|
||||
|
|
@ -1700,7 +1719,7 @@ private:
|
|||
VL_DO_DANGLING(streamp->deleteTree(), streamp);
|
||||
// Further reduce, any of the nodes may have more reductions.
|
||||
return true;
|
||||
} else if (replaceAssignMultiSel(nodep)) {
|
||||
} else if (m_doV && replaceAssignMultiSel(nodep)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
#include "V3Global.h"
|
||||
#include "V3CoverageJoin.h"
|
||||
#include "V3Hashed.h"
|
||||
#include "V3DupFinder.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include <vector>
|
||||
|
|
@ -33,10 +33,7 @@
|
|||
class CoverageJoinVisitor final : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// V3Hashed
|
||||
// AstCoverToggle->VarRef::user4() // V3Hashed calculation
|
||||
|
||||
// AstUser4InUse In V3Hashed
|
||||
// AstUser4InUse In V3Hasher via V3DupFinder
|
||||
|
||||
// STATE
|
||||
std::vector<AstCoverToggle*> m_toggleps; // List of of all AstCoverToggle's
|
||||
|
|
@ -49,9 +46,9 @@ private:
|
|||
void detectDuplicates() {
|
||||
UINFO(9, "Finding duplicates\n");
|
||||
// Note uses user4
|
||||
V3Hashed hashed; // Duplicate code detection
|
||||
V3DupFinder dupFinder; // Duplicate code detection
|
||||
// Hash all of the original signals we toggle cover
|
||||
for (AstCoverToggle* nodep : m_toggleps) hashed.hashAndInsert(nodep->origp());
|
||||
for (AstCoverToggle* nodep : m_toggleps) dupFinder.insert(nodep->origp());
|
||||
// Find if there are any duplicates
|
||||
for (AstCoverToggle* nodep : m_toggleps) {
|
||||
// nodep->backp() is null if we already detected it's a duplicate and unlinked it.
|
||||
|
|
@ -60,10 +57,10 @@ private:
|
|||
// This prevents making chains where a->b, then c->d, then b->c, as we'll
|
||||
// find a->b, a->c, a->d directly.
|
||||
while (true) {
|
||||
const auto dupit = hashed.findDuplicate(nodep->origp());
|
||||
if (dupit == hashed.end()) break;
|
||||
const auto dupit = dupFinder.findDuplicate(nodep->origp());
|
||||
if (dupit == dupFinder.end()) break;
|
||||
//
|
||||
AstNode* duporigp = hashed.iteratorNodep(dupit);
|
||||
AstNode* duporigp = dupit->second;
|
||||
// Note hashed will point to the original variable (what's
|
||||
// duplicated), not the covertoggle, but we need to get back to the
|
||||
// covertoggle which is immediately above, so:
|
||||
|
|
@ -82,7 +79,7 @@ private:
|
|||
removep->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(removep), removep);
|
||||
// Remove node from comparison so don't hit it again
|
||||
hashed.erase(dupit);
|
||||
dupFinder.erase(dupit);
|
||||
++m_statToggleJoins;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -351,7 +351,7 @@ private:
|
|||
new AstVarRef(nodep->fileline(), setvscp, VAccess::READ),
|
||||
nullptr, nullptr);
|
||||
UINFO(9, " Created " << postLogicp << endl);
|
||||
finalp->addBodysp(postLogicp);
|
||||
finalp->addStmtp(postLogicp);
|
||||
finalp->user3p(setvscp); // Remember IF's vset variable
|
||||
finalp->user4p(postLogicp); // and the associated IF, as we may be able to reuse it
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Hashed common code into functions
|
||||
//
|
||||
// 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 "V3DupFinder.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3File.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
//######################################################################
|
||||
// V3DupFinder class functions
|
||||
|
||||
V3DupFinder::iterator V3DupFinder::findDuplicate(AstNode* nodep, V3DupFinderUserSame* checkp) {
|
||||
const auto& er = equal_range(m_hasher(nodep));
|
||||
for (iterator it = er.first; it != er.second; ++it) {
|
||||
AstNode* const node2p = it->second;
|
||||
if (nodep == node2p) continue; // Same node is not a duplicate
|
||||
if (checkp && !checkp->isSame(nodep, node2p)) continue; // User says it is not a duplicate
|
||||
if (!nodep->sameTree(node2p)) continue; // Not the same trees
|
||||
// Found duplicate!
|
||||
return it;
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
void V3DupFinder::dumpFile(const string& filename, bool tree) {
|
||||
const std::unique_ptr<std::ofstream> logp(V3File::new_ofstream(filename));
|
||||
if (logp->fail()) v3fatal("Can't write " << filename);
|
||||
|
||||
std::unordered_map<int, int> dist;
|
||||
|
||||
V3Hash lasthash;
|
||||
int num_in_bucket = 0;
|
||||
for (auto it = cbegin(); true; ++it) {
|
||||
if (it == cend() || lasthash != it->first) {
|
||||
if (it != cend()) lasthash = it->first;
|
||||
if (num_in_bucket) {
|
||||
if (dist.find(num_in_bucket) == dist.end()) {
|
||||
dist.emplace(num_in_bucket, 1);
|
||||
} else {
|
||||
++dist[num_in_bucket];
|
||||
}
|
||||
}
|
||||
num_in_bucket = 0;
|
||||
}
|
||||
if (it == cend()) break;
|
||||
num_in_bucket++;
|
||||
}
|
||||
*logp << "\n*** STATS:\n\n";
|
||||
*logp << " #InBucket Occurrences\n";
|
||||
for (const auto& i : dist) {
|
||||
*logp << " " << std::setw(9) << i.first << " " << std::setw(12) << i.second << '\n';
|
||||
}
|
||||
|
||||
*logp << "\n*** Dump:\n\n";
|
||||
for (const auto& it : *this) {
|
||||
if (lasthash != it.first) {
|
||||
lasthash = it.first;
|
||||
*logp << " " << it.first << '\n';
|
||||
}
|
||||
*logp << "\t" << it.second << '\n';
|
||||
// Dumping the entire tree may make nearly N^2 sized dumps,
|
||||
// because the nodes under this one may also be in the hash table!
|
||||
if (tree) it.second->dumpTree(*logp, " ");
|
||||
}
|
||||
}
|
||||
|
||||
void V3DupFinder::dumpFilePrefixed(const string& nameComment, bool tree) {
|
||||
if (v3Global.opt.dumpTree()) { //
|
||||
dumpFile(v3Global.debugFilename(nameComment) + ".hash", tree);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Hash AST trees to find duplicates
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2005-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
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Datastructure for finding duplicate AstNode trees via hashing
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_V3DUPFINDER_H_
|
||||
#define VERILATOR_V3DUPFINDER_H_
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3Hasher.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
//============================================================================
|
||||
|
||||
struct V3DupFinderUserSame {
|
||||
// Functor for V3DupFinder::findDuplicate
|
||||
virtual bool isSame(AstNode*, AstNode*) = 0;
|
||||
V3DupFinderUserSame() = default;
|
||||
virtual ~V3DupFinderUserSame() = default;
|
||||
};
|
||||
|
||||
// This really is just a multimap from 'node hash' to 'node pointer', with some minor extensions.
|
||||
class V3DupFinder final : private std::multimap<V3Hash, AstNode*> {
|
||||
using Super = std::multimap<V3Hash, AstNode*>;
|
||||
|
||||
// MEMBERS
|
||||
const V3Hasher m_hasher;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
V3DupFinder(){};
|
||||
~V3DupFinder() = default;
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// Expose minimal set of superclass interface
|
||||
using Super::begin;
|
||||
using Super::cbegin;
|
||||
using Super::cend;
|
||||
using Super::clear;
|
||||
using Super::const_iterator;
|
||||
using Super::empty;
|
||||
using Super::end;
|
||||
using Super::erase;
|
||||
using Super::iterator;
|
||||
|
||||
// Insert node into data structure
|
||||
iterator insert(AstNode* nodep) { return emplace(m_hasher(nodep), nodep); }
|
||||
|
||||
// Return duplicate, if one was inserted, with optional user check for sameness
|
||||
iterator findDuplicate(AstNode* nodep, V3DupFinderUserSame* checkp = nullptr);
|
||||
|
||||
// Dump for debug
|
||||
void dumpFile(const string& filename, bool tree);
|
||||
void dumpFilePrefixed(const string& nameComment, bool tree = false);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
186
src/V3EmitC.cpp
186
src/V3EmitC.cpp
|
|
@ -459,7 +459,7 @@ public:
|
|||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCoverDecl* nodep) override {
|
||||
puts("__vlCoverInsert("); // As Declared in emitCoverageDecl
|
||||
puts("self->__vlCoverInsert("); // As Declared in emitCoverageDecl
|
||||
puts("&(vlSymsp->__Vcoverage[");
|
||||
puts(cvtToStr(nodep->dataDeclThisp()->binNum()));
|
||||
puts("])");
|
||||
|
|
@ -812,11 +812,19 @@ public:
|
|||
if (!nodep->branchPred().unknown()) puts(")");
|
||||
puts(") {\n");
|
||||
iterateAndNextNull(nodep->ifsp());
|
||||
if (nodep->elsesp()) {
|
||||
puts("} else {\n");
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
puts("}");
|
||||
if (!nodep->elsesp()) {
|
||||
puts("\n");
|
||||
} else {
|
||||
if (VN_IS(nodep->elsesp(), NodeIf) && !nodep->elsesp()->nextp()) {
|
||||
puts(" else ");
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
} else {
|
||||
puts(" else {\n");
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
puts("}\n");
|
||||
}
|
||||
}
|
||||
puts("}\n");
|
||||
}
|
||||
virtual void visit(AstExprStmt* nodep) override {
|
||||
// GCC allows compound statements in expressions, but this is not standard.
|
||||
|
|
@ -1335,6 +1343,7 @@ unsigned EmitVarTspSorter::s_serialNext = 0;
|
|||
class EmitCImp final : EmitCStmts {
|
||||
// MEMBERS
|
||||
AstNodeModule* m_modp = nullptr;
|
||||
AstNodeModule* m_fileModp = nullptr; // Files (names, headers) constructed using this module
|
||||
std::vector<AstChangeDet*> m_blkChangeDetVec; // All encountered changes in block
|
||||
bool m_slow = false; // Creating __Slow file
|
||||
bool m_fast = false; // Creating non __Slow file (or both)
|
||||
|
|
@ -1383,8 +1392,8 @@ class EmitCImp final : EmitCStmts {
|
|||
}
|
||||
}
|
||||
|
||||
V3OutCFile* newOutCFile(AstNodeModule* modp, bool slow, bool source, int filenum = 0) {
|
||||
string filenameNoExt = v3Global.opt.makeDir() + "/" + prefixNameProtect(modp);
|
||||
V3OutCFile* newOutCFile(bool slow, bool source, int filenum = 0) {
|
||||
string filenameNoExt = v3Global.opt.makeDir() + "/" + prefixNameProtect(m_fileModp);
|
||||
if (filenum) filenameNoExt += "__" + cvtToStr(filenum);
|
||||
filenameNoExt += (slow ? "__Slow" : "");
|
||||
V3OutCFile* ofp = nullptr;
|
||||
|
|
@ -1405,7 +1414,7 @@ class EmitCImp final : EmitCStmts {
|
|||
}
|
||||
|
||||
ofp->putsHeader();
|
||||
if (modp->isTop() && !source) {
|
||||
if (m_fileModp->isTop() && !source) {
|
||||
ofp->puts("// DESCR"
|
||||
"IPTION: Verilator output: Primary design header\n");
|
||||
ofp->puts("//\n");
|
||||
|
|
@ -1493,7 +1502,10 @@ class EmitCImp final : EmitCStmts {
|
|||
}
|
||||
|
||||
virtual void visit(AstMTaskBody* nodep) override {
|
||||
ExecMTask* mtp = nodep->execMTaskp();
|
||||
maybeSplit();
|
||||
splitSizeInc(10);
|
||||
|
||||
const ExecMTask* const mtp = nodep->execMTaskp();
|
||||
puts("\n");
|
||||
puts("void ");
|
||||
puts(prefixNameProtect(m_modp) + "::" + protect(mtp->cFuncName()));
|
||||
|
|
@ -1517,6 +1529,8 @@ class EmitCImp final : EmitCStmts {
|
|||
if (nodep->dpiImport()) return;
|
||||
if (!(nodep->slow() ? m_slow : m_fast)) return;
|
||||
|
||||
maybeSplit();
|
||||
|
||||
m_blkChangeDetVec.clear();
|
||||
|
||||
splitSizeInc(nodep);
|
||||
|
|
@ -1739,7 +1753,9 @@ class EmitCImp final : EmitCStmts {
|
|||
}
|
||||
|
||||
void emitVarReset(AstVar* varp) {
|
||||
AstNodeDType* dtypep = varp->dtypep()->skipRefp();
|
||||
AstNodeDType* const dtypep = varp->dtypep()->skipRefp();
|
||||
const string varNameProtected
|
||||
= VN_IS(m_modp, Class) ? varp->nameProtect() : "self->" + varp->nameProtect();
|
||||
if (varp->isIO() && m_modp->isTop() && optSystemC()) {
|
||||
// System C top I/O doesn't need loading, as the lower level subinst code does it.}
|
||||
} else if (varp->isParam()) {
|
||||
|
|
@ -1752,53 +1768,53 @@ class EmitCImp final : EmitCStmts {
|
|||
if (initarp->defaultp()) {
|
||||
puts("for (int __Vi=0; __Vi<" + cvtToStr(adtypep->elementsConst()));
|
||||
puts("; ++__Vi) {\n");
|
||||
emitSetVarConstant(varp->nameProtect() + "[__Vi]",
|
||||
emitSetVarConstant(varNameProtected + "[__Vi]",
|
||||
VN_CAST(initarp->defaultp(), Const));
|
||||
puts("}\n");
|
||||
}
|
||||
const AstInitArray::KeyItemMap& mapr = initarp->map();
|
||||
for (const auto& itr : mapr) {
|
||||
AstNode* valuep = itr.second->valuep();
|
||||
emitSetVarConstant(varp->nameProtect() + "[" + cvtToStr(itr.first) + "]",
|
||||
emitSetVarConstant(varNameProtected + "[" + cvtToStr(itr.first) + "]",
|
||||
VN_CAST(valuep, Const));
|
||||
}
|
||||
} else {
|
||||
varp->v3fatalSrc("InitArray under non-arrayed var");
|
||||
}
|
||||
} else {
|
||||
puts(emitVarResetRecurse(varp, dtypep, 0, ""));
|
||||
puts(emitVarResetRecurse(varp, varNameProtected, dtypep, 0, ""));
|
||||
}
|
||||
}
|
||||
string emitVarResetRecurse(AstVar* varp, AstNodeDType* dtypep, int depth,
|
||||
const string& suffix) {
|
||||
string emitVarResetRecurse(const AstVar* varp, const string& varNameProtected,
|
||||
AstNodeDType* dtypep, int depth, const string& suffix) {
|
||||
dtypep = dtypep->skipRefp();
|
||||
AstBasicDType* basicp = dtypep->basicp();
|
||||
// Returns string to do resetting, empty to do nothing (which caller should handle)
|
||||
if (AstAssocArrayDType* adtypep = VN_CAST(dtypep, AssocArrayDType)) {
|
||||
// Access std::array as C array
|
||||
string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
|
||||
return emitVarResetRecurse(varp, adtypep->subDTypep(), depth + 1,
|
||||
".atDefault()" + cvtarray);
|
||||
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
|
||||
suffix + ".atDefault()" + cvtarray);
|
||||
} else if (VN_IS(dtypep, ClassRefDType)) {
|
||||
return ""; // Constructor does it
|
||||
} else if (AstDynArrayDType* adtypep = VN_CAST(dtypep, DynArrayDType)) {
|
||||
// Access std::array as C array
|
||||
string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
|
||||
return emitVarResetRecurse(varp, adtypep->subDTypep(), depth + 1,
|
||||
".atDefault()" + cvtarray);
|
||||
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
|
||||
suffix + ".atDefault()" + cvtarray);
|
||||
} else if (AstQueueDType* adtypep = VN_CAST(dtypep, QueueDType)) {
|
||||
// Access std::array as C array
|
||||
string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
|
||||
return emitVarResetRecurse(varp, adtypep->subDTypep(), depth + 1,
|
||||
".atDefault()" + cvtarray);
|
||||
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
|
||||
suffix + ".atDefault()" + cvtarray);
|
||||
} else if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
UASSERT_OBJ(adtypep->hi() >= adtypep->lo(), varp,
|
||||
"Should have swapped msb & lsb earlier.");
|
||||
string ivar = string("__Vi") + cvtToStr(depth);
|
||||
string pre = ("for (int " + ivar + "=" + cvtToStr(0) + "; " + ivar + "<"
|
||||
+ cvtToStr(adtypep->elementsConst()) + "; ++" + ivar + ") {\n");
|
||||
string below = emitVarResetRecurse(varp, adtypep->subDTypep(), depth + 1,
|
||||
suffix + "[" + ivar + "]");
|
||||
string below = emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(),
|
||||
depth + 1, suffix + "[" + ivar + "]");
|
||||
string post = "}\n";
|
||||
return below.empty() ? "" : pre + below + post;
|
||||
} else if (basicp && basicp->keyword() == AstBasicDTypeKwd::STRING) {
|
||||
|
|
@ -1814,16 +1830,21 @@ class EmitCImp final : EmitCStmts {
|
|||
splitSizeInc(1);
|
||||
if (dtypep->isWide()) { // Handle unpacked; not basicp->isWide
|
||||
string out;
|
||||
if (zeroit) {
|
||||
out += "VL_ZERO_RESET_W(";
|
||||
if (varp->valuep()) {
|
||||
AstConst* const constp = VN_CAST(varp->valuep(), Const);
|
||||
if (!constp) varp->v3fatalSrc("non-const initializer for variable");
|
||||
for (int w = 0; w < varp->widthWords(); ++w) {
|
||||
out += varNameProtected + suffix + "[" + cvtToStr(w) + "] = ";
|
||||
out += cvtToStr(constp->num().edataWord(w)) + "U;\n";
|
||||
}
|
||||
} else {
|
||||
out += "VL_RAND_RESET_W(";
|
||||
out += zeroit ? "VL_ZERO_RESET_W(" : "VL_RAND_RESET_W(";
|
||||
out += cvtToStr(dtypep->widthMin());
|
||||
out += ", " + varNameProtected + suffix + ");\n";
|
||||
}
|
||||
out += cvtToStr(dtypep->widthMin());
|
||||
out += ", " + varp->nameProtect() + suffix + ");\n";
|
||||
return out;
|
||||
} else {
|
||||
string out = varp->nameProtect() + suffix;
|
||||
string out = varNameProtected + suffix;
|
||||
// If --x-initial-edge is set, we want to force an initial
|
||||
// edge on uninitialized clocks (from 'X' to whatever the
|
||||
// first value is). Since the class is instantiated before
|
||||
|
|
@ -1855,8 +1876,8 @@ class EmitCImp final : EmitCStmts {
|
|||
void emitSavableImp(AstNodeModule* modp);
|
||||
void emitTextSection(AstType type);
|
||||
// High level
|
||||
void emitImpTop(AstNodeModule* modp);
|
||||
void emitImp(AstNodeModule* fileModp, AstNodeModule* modp);
|
||||
void emitImpTop();
|
||||
void emitImp(AstNodeModule* modp);
|
||||
void emitSettleLoop(const std::string& eval_call, bool initial);
|
||||
void emitWrapEval(AstNodeModule* modp);
|
||||
void emitWrapFast(AstNodeModule* modp);
|
||||
|
|
@ -1864,7 +1885,7 @@ class EmitCImp final : EmitCStmts {
|
|||
void emitMTaskVertexCtors(bool* firstp);
|
||||
void emitIntTop(AstNodeModule* modp);
|
||||
void emitInt(AstNodeModule* modp);
|
||||
void maybeSplit(AstNodeModule* modp);
|
||||
void maybeSplit();
|
||||
|
||||
public:
|
||||
EmitCImp() = default;
|
||||
|
|
@ -2263,6 +2284,19 @@ void EmitCStmts::displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const
|
|||
emitDispState.pushArg(' ', nullptr, cvtToStr(argp->widthMin()));
|
||||
}
|
||||
emitDispState.pushArg(fmtLetter, argp, "");
|
||||
if (fmtLetter == 't' || fmtLetter == '^') {
|
||||
AstSFormatF* fmtp = nullptr;
|
||||
if (AstDisplay* nodep = VN_CAST(dispp, Display))
|
||||
fmtp = nodep->fmtp();
|
||||
else if (AstSFormat* nodep = VN_CAST(dispp, SFormat))
|
||||
fmtp = nodep->fmtp();
|
||||
else
|
||||
fmtp = VN_CAST(dispp, SFormatF);
|
||||
UASSERT_OBJ(fmtp, dispp,
|
||||
"Use of %t must be under AstDisplay, AstSFormat, or AstSFormatF");
|
||||
UASSERT_OBJ(!fmtp->timeunit().isNone(), fmtp, "timenunit must be set");
|
||||
emitDispState.pushArg(' ', nullptr, cvtToStr((int)fmtp->timeunit().powerOfTen()));
|
||||
}
|
||||
} else {
|
||||
emitDispState.pushArg(fmtLetter, nullptr, "");
|
||||
}
|
||||
|
|
@ -2449,7 +2483,7 @@ void EmitCImp::emitCtorImp(AstNodeModule* modp) {
|
|||
puts("\n");
|
||||
}
|
||||
putsDecoration("// Reset structure values\n");
|
||||
puts(protect("_ctor_var_reset") + "();\n");
|
||||
puts(protect("_ctor_var_reset") + "(this);\n");
|
||||
emitTextSection(AstType::atScCtor);
|
||||
|
||||
if (modp->isTop() && v3Global.opt.mtasks()) {
|
||||
|
|
@ -2492,7 +2526,9 @@ void EmitCImp::emitConfigureImp(AstNodeModule* modp) {
|
|||
puts("if (false && first) {} // Prevent unused\n");
|
||||
puts("this->__VlSymsp = vlSymsp;\n"); // First, as later stuff needs it.
|
||||
puts("if (false && this->__VlSymsp) {} // Prevent unused\n");
|
||||
if (v3Global.opt.coverage()) { puts(protect("_configure_coverage") + "(vlSymsp, first);\n"); }
|
||||
if (v3Global.opt.coverage()) {
|
||||
puts(protect("_configure_coverage") + "(this, vlSymsp, first);\n");
|
||||
}
|
||||
if (modp->isTop() && !v3Global.rootp()->timeunit().isNone()) {
|
||||
puts("vlSymsp->_vm_contextp__->timeunit("
|
||||
+ cvtToStr(v3Global.rootp()->timeunit().powerOfTen()) + ");\n");
|
||||
|
|
@ -3088,10 +3124,7 @@ void EmitCImp::emitIntTop(AstNodeModule*) {
|
|||
}
|
||||
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.opt.savable()) v3error("--coverage and --savable not supported together");
|
||||
}
|
||||
if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n");
|
||||
if (v3Global.dpi()) {
|
||||
// do this before including our main .h file so that any references to
|
||||
// types defined in svdpi.h are available
|
||||
|
|
@ -3338,9 +3371,9 @@ void EmitCImp::emitInt(AstNodeModule* modp) {
|
|||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void EmitCImp::emitImpTop(AstNodeModule* fileModp) {
|
||||
void EmitCImp::emitImpTop() {
|
||||
puts("\n");
|
||||
puts("#include \"" + prefixNameProtect(fileModp) + ".h\"\n");
|
||||
puts("#include \"" + prefixNameProtect(m_fileModp) + ".h\"\n");
|
||||
puts("#include \"" + symClassName() + ".h\"\n");
|
||||
|
||||
if (v3Global.dpi()) {
|
||||
|
|
@ -3348,13 +3381,13 @@ void EmitCImp::emitImpTop(AstNodeModule* fileModp) {
|
|||
puts("#include \"verilated_dpi.h\"\n");
|
||||
}
|
||||
|
||||
emitModCUse(fileModp, VUseType::IMP_INCLUDE);
|
||||
emitModCUse(fileModp, VUseType::IMP_FWD_CLASS);
|
||||
emitModCUse(m_fileModp, VUseType::IMP_INCLUDE);
|
||||
emitModCUse(m_fileModp, VUseType::IMP_FWD_CLASS);
|
||||
|
||||
emitTextSection(AstType::atScImpHdr);
|
||||
}
|
||||
|
||||
void EmitCImp::emitImp(AstNodeModule* fileModp, AstNodeModule* modp) {
|
||||
void EmitCImp::emitImp(AstNodeModule* modp) {
|
||||
puts("\n//==========\n");
|
||||
if (m_slow) {
|
||||
string section;
|
||||
|
|
@ -3376,37 +3409,33 @@ void EmitCImp::emitImp(AstNodeModule* fileModp, AstNodeModule* modp) {
|
|||
|
||||
// Blocks
|
||||
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (AstCFunc* funcp = VN_CAST(nodep, CFunc)) {
|
||||
maybeSplit(fileModp);
|
||||
mainDoFunc(funcp);
|
||||
}
|
||||
if (AstCFunc* funcp = VN_CAST(nodep, CFunc)) { mainDoFunc(funcp); }
|
||||
}
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
|
||||
void EmitCImp::maybeSplit(AstNodeModule* fileModp) {
|
||||
if (splitNeeded()) {
|
||||
// Splitting file, so using parallel build.
|
||||
v3Global.useParallelBuild(true);
|
||||
// Close old file
|
||||
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
|
||||
// Open a new file
|
||||
m_ofp = newOutCFile(fileModp, !m_fast, true /*source*/, splitFilenumInc());
|
||||
emitImpTop(fileModp);
|
||||
}
|
||||
splitSizeInc(10); // Even blank functions get a file with a low csplit
|
||||
void EmitCImp::maybeSplit() {
|
||||
if (!splitNeeded()) return;
|
||||
|
||||
// Splitting file, so using parallel build.
|
||||
v3Global.useParallelBuild(true);
|
||||
// Close old file
|
||||
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
|
||||
// Open a new file
|
||||
m_ofp = newOutCFile(!m_fast, true /*source*/, splitFilenumInc());
|
||||
emitImpTop();
|
||||
}
|
||||
|
||||
void EmitCImp::mainInt(AstNodeModule* modp) {
|
||||
AstNodeModule* fileModp = modp; // Filename constructed using this module
|
||||
m_modp = modp;
|
||||
m_fileModp = modp;
|
||||
m_slow = true;
|
||||
m_fast = true;
|
||||
|
||||
UINFO(5, " Emitting " << prefixNameProtect(modp) << endl);
|
||||
|
||||
m_ofp = newOutCFile(fileModp, false /*slow*/, false /*source*/);
|
||||
m_ofp = newOutCFile(false /*slow*/, false /*source*/);
|
||||
emitIntTop(modp);
|
||||
emitInt(modp);
|
||||
if (AstClassPackage* packagep = VN_CAST(modp, ClassPackage)) {
|
||||
|
|
@ -3421,23 +3450,23 @@ void EmitCImp::mainInt(AstNodeModule* modp) {
|
|||
|
||||
void EmitCImp::mainImp(AstNodeModule* modp, bool slow) {
|
||||
// Output a module
|
||||
AstNodeModule* fileModp = modp; // Filename constructed using this module
|
||||
m_modp = modp;
|
||||
m_fileModp = modp;
|
||||
m_slow = slow;
|
||||
m_fast = !slow;
|
||||
|
||||
UINFO(5, " Emitting " << prefixNameProtect(modp) << endl);
|
||||
|
||||
m_ofp = newOutCFile(fileModp, !m_fast, true /*source*/);
|
||||
emitImpTop(fileModp);
|
||||
emitImp(fileModp, modp);
|
||||
m_ofp = newOutCFile(!m_fast, true /*source*/);
|
||||
emitImpTop();
|
||||
emitImp(modp);
|
||||
|
||||
if (AstClassPackage* packagep = VN_CAST(modp, ClassPackage)) {
|
||||
// Put the non-static class implementation in same C++ files as
|
||||
// often optimizations are possible when both are seen by the
|
||||
// compiler together
|
||||
m_modp = packagep->classp();
|
||||
emitImp(fileModp, packagep->classp());
|
||||
emitImp(packagep->classp());
|
||||
m_modp = modp;
|
||||
}
|
||||
|
||||
|
|
@ -3450,7 +3479,6 @@ void EmitCImp::mainImp(AstNodeModule* modp, bool slow) {
|
|||
vxp = vxp->verticesNextp()) {
|
||||
const ExecMTask* mtaskp = dynamic_cast<const ExecMTask*>(vxp);
|
||||
if (mtaskp->threadRoot()) {
|
||||
maybeSplit(fileModp);
|
||||
// Only define one function for all the mtasks packed on
|
||||
// a given thread. We'll name this function after the
|
||||
// root mtask though it contains multiple mtasks' worth
|
||||
|
|
@ -3925,21 +3953,29 @@ void V3EmitC::emitc() {
|
|||
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep;
|
||||
nodep = VN_CAST(nodep->nextp(), NodeModule)) {
|
||||
if (VN_IS(nodep, Class)) continue; // Imped with ClassPackage
|
||||
// clang-format off
|
||||
EmitCImp cint; cint.mainInt(nodep);
|
||||
cint.mainImp(nodep, true);
|
||||
{ EmitCImp fast; fast.mainImp(nodep, false); }
|
||||
// clang-format on
|
||||
{
|
||||
EmitCImp cint;
|
||||
cint.mainInt(nodep);
|
||||
cint.mainImp(nodep, true);
|
||||
}
|
||||
{
|
||||
EmitCImp fast;
|
||||
fast.mainImp(nodep, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void V3EmitC::emitcTrace() {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
if (v3Global.opt.trace()) {
|
||||
// clang-format off
|
||||
{ EmitCTrace slow(true); slow.main(); }
|
||||
{ EmitCTrace fast(false); fast.main(); }
|
||||
// clang-format on
|
||||
{
|
||||
EmitCTrace slow(true);
|
||||
slow.main();
|
||||
}
|
||||
{
|
||||
EmitCTrace fast(false);
|
||||
fast.main();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -113,6 +113,10 @@ class CMakeEmitter final {
|
|||
cmake_set_raw(*of, name + "_COVERAGE", v3Global.opt.coverage() ? "1" : "0");
|
||||
*of << "# Threaded output mode? 0/1/N threads (from --threads)\n";
|
||||
cmake_set_raw(*of, name + "_THREADS", cvtToStr(v3Global.opt.threads()));
|
||||
*of << "# Threaded tracing output mode? 0/1/N threads (from --trace-threads)\n";
|
||||
cmake_set_raw(*of, name + "_TRACE_THREADS", cvtToStr(v3Global.opt.traceThreads()));
|
||||
*of << "# Struct output mode? 0/1 (from --trace-structs)\n";
|
||||
cmake_set_raw(*of, name + "_TRACE_STRUCTS", cvtToStr(v3Global.opt.traceStructs()));
|
||||
*of << "# VCD Tracing output mode? 0/1 (from --trace)\n";
|
||||
cmake_set_raw(*of, name + "_TRACE_VCD",
|
||||
(v3Global.opt.trace() && (v3Global.opt.traceFormat() == TraceFormat::VCD))
|
||||
|
|
@ -167,11 +171,6 @@ class CMakeEmitter final {
|
|||
global.emplace_back("${VERILATOR_ROOT}/include/" + v3Global.opt.traceSourceBase()
|
||||
+ "_c.cpp");
|
||||
if (v3Global.opt.systemC()) {
|
||||
if (v3Global.opt.traceFormat() != TraceFormat::VCD) {
|
||||
v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: This trace format is not supported in SystemC, "
|
||||
"use VCD format.");
|
||||
}
|
||||
global.emplace_back("${VERILATOR_ROOT}/include/" + v3Global.opt.traceSourceLang()
|
||||
+ ".cpp");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,7 +170,12 @@ class EmitCSyms final : EmitCBaseVisitor {
|
|||
const auto scpit = m_vpiScopeCandidates.find(scp);
|
||||
if ((scpit != m_vpiScopeCandidates.end())
|
||||
&& (m_scopeNames.find(scp) == m_scopeNames.end())) {
|
||||
m_scopeNames.emplace(scpit->second.m_symName, scpit->second);
|
||||
auto scopeNameit = m_scopeNames.find(scpit->second.m_symName);
|
||||
if (scopeNameit == m_scopeNames.end()) {
|
||||
m_scopeNames.emplace(scpit->second.m_symName, scpit->second);
|
||||
} else {
|
||||
scopeNameit->second.m_type = scpit->second.m_type;
|
||||
}
|
||||
}
|
||||
string::size_type pos = scp.rfind("__DOT__");
|
||||
if (pos == string::npos) {
|
||||
|
|
@ -233,20 +238,20 @@ class EmitCSyms final : EmitCBaseVisitor {
|
|||
++it) {
|
||||
if (it->second.m_type != "SCOPE_MODULE") continue;
|
||||
|
||||
string name = it->second.m_prettyName;
|
||||
if (name.substr(0, 4) == "TOP.") name.replace(0, 4, "");
|
||||
string symName = it->second.m_symName;
|
||||
string above = symName;
|
||||
if (above.substr(0, 4) == "TOP.") above.replace(0, 4, "");
|
||||
|
||||
string above = name;
|
||||
while (!above.empty()) {
|
||||
string::size_type pos = above.rfind('.');
|
||||
string::size_type pos = above.rfind("__");
|
||||
if (pos == string::npos) break;
|
||||
above.resize(pos);
|
||||
if (m_vpiScopeHierarchy.find(above) != m_vpiScopeHierarchy.end()) {
|
||||
m_vpiScopeHierarchy[above].push_back(name);
|
||||
m_vpiScopeHierarchy[above].push_back(symName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_vpiScopeHierarchy[name] = std::vector<string>();
|
||||
m_vpiScopeHierarchy[symName] = std::vector<string>();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -757,6 +762,7 @@ void EmitCSyms::emitSymImp() {
|
|||
AstScope* scopep = it->second.m_scopep;
|
||||
AstVar* varp = it->second.m_varp;
|
||||
//
|
||||
int pwidth = 1;
|
||||
int pdim = 0;
|
||||
int udim = 0;
|
||||
string bounds;
|
||||
|
|
@ -768,6 +774,7 @@ void EmitCSyms::emitSymImp() {
|
|||
bounds += ",";
|
||||
bounds += cvtToStr(basicp->lo());
|
||||
pdim++;
|
||||
pwidth *= basicp->elements();
|
||||
}
|
||||
for (AstNodeDType* dtypep = varp->dtypep(); dtypep;) {
|
||||
dtypep
|
||||
|
|
@ -779,6 +786,7 @@ void EmitCSyms::emitSymImp() {
|
|||
bounds += cvtToStr(adtypep->right());
|
||||
if (VN_IS(dtypep, PackArrayDType)) {
|
||||
pdim++;
|
||||
pwidth *= adtypep->elementsConst();
|
||||
} else {
|
||||
udim++;
|
||||
}
|
||||
|
|
@ -788,8 +796,14 @@ void EmitCSyms::emitSymImp() {
|
|||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
if (udim > 1 && (pdim && udim)) {
|
||||
// TODO: actually expose packed arrays as vpiRegArray
|
||||
if (pdim > 1 && udim == 0) {
|
||||
bounds = ", ";
|
||||
bounds += cvtToStr(pwidth - 1);
|
||||
bounds += ",0";
|
||||
pdim = 1;
|
||||
}
|
||||
if (pdim > 1 || udim > 1) {
|
||||
puts("//UNSUP "); // VerilatedImp can't deal with >2d or packed arrays
|
||||
}
|
||||
puts(protect("__Vscope_" + it->second.m_scopeName) + ".varInsert(__Vfinal,");
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ public:
|
|||
ALWCOMBORDER, // Always_comb with unordered statements
|
||||
ASSIGNDLY, // Assignment delays
|
||||
ASSIGNIN, // Assigning to input
|
||||
BADSTDPRAGMA, // Any error related to pragmas
|
||||
BLKANDNBLK, // Blocked and non-blocking assignments to same variable
|
||||
BLKLOOPINIT, // Delayed assignment to array inside for loops
|
||||
BLKSEQ, // Blocking assignments in sequential block
|
||||
|
|
@ -109,6 +110,7 @@ public:
|
|||
PINNOTFOUND, // instance port name not found in it's module
|
||||
PKGNODECL, // Error: Package/class needs to be predeclared
|
||||
PROCASSWIRE, // Procedural assignment on wire
|
||||
PROTECTED, // detected `pragma protected
|
||||
RANDC, // Unsupported: 'randc' converted to 'rand'
|
||||
REALCVT, // Real conversion
|
||||
REDEFMACRO, // Redefining existing define macro
|
||||
|
|
@ -159,7 +161,7 @@ public:
|
|||
"DETECTARRAY", "ENCAPSULATED", "PORTSHORT", "UNSUPPORTED", "TASKNSVAR",
|
||||
// Warnings
|
||||
" EC_FIRST_WARN",
|
||||
"ALWCOMBORDER", "ASSIGNDLY", "ASSIGNIN",
|
||||
"ALWCOMBORDER", "ASSIGNDLY", "ASSIGNIN", "BADSTDPRAGMA",
|
||||
"BLKANDNBLK", "BLKLOOPINIT", "BLKSEQ", "BSSPACE",
|
||||
"CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA",
|
||||
"CMPCONST", "COLONPLUS", "COMBDLY", "CONTASSREG",
|
||||
|
|
@ -171,7 +173,7 @@ public:
|
|||
"LATCH", "LITENDIAN", "MODDUP",
|
||||
"MULTIDRIVEN", "MULTITOP","NOLATCH", "NULLPORT", "PINCONNECTEMPTY",
|
||||
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PROCASSWIRE",
|
||||
"RANDC", "REALCVT", "REDEFMACRO",
|
||||
"PROTECTED", "RANDC", "REALCVT", "REDEFMACRO",
|
||||
"SELRANGE", "SHORTREAL", "SPLITVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET",
|
||||
"TICKCOUNT", "TIMESCALEMOD",
|
||||
"UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS",
|
||||
|
|
@ -192,8 +194,8 @@ public:
|
|||
// Warnings we'll present to the user as errors
|
||||
// Later -Werror- options may make more of these.
|
||||
bool pretendError() const {
|
||||
return (m_e == ASSIGNIN || m_e == BLKANDNBLK || m_e == BLKLOOPINIT || m_e == CONTASSREG
|
||||
|| m_e == IMPURE || m_e == PINNOTFOUND || m_e == PKGNODECL
|
||||
return (m_e == ASSIGNIN || m_e == BADSTDPRAGMA || m_e == BLKANDNBLK || m_e == BLKLOOPINIT
|
||||
|| m_e == CONTASSREG || m_e == IMPURE || m_e == PINNOTFOUND || m_e == PKGNODECL
|
||||
|| m_e == PROCASSWIRE); // Says IEEE
|
||||
}
|
||||
// Warnings to mention manual
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "V3Global.h"
|
||||
#include "V3Expand.h"
|
||||
#include "V3Stats.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
|
@ -45,10 +46,24 @@ private:
|
|||
|
||||
// STATE
|
||||
AstNode* m_stmtp = nullptr; // Current statement
|
||||
VDouble0 m_statWides; // Statistic tracking
|
||||
VDouble0 m_statWideWords; // Statistic tracking
|
||||
VDouble0 m_statWideLimited; // Statistic tracking
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
bool doExpand(AstNode* nodep) {
|
||||
++m_statWides;
|
||||
if (nodep->widthWords() <= v3Global.opt.expandLimit()) {
|
||||
m_statWideWords += nodep->widthWords();
|
||||
return true;
|
||||
} else {
|
||||
++m_statWideLimited;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int longOrQuadWidth(AstNode* nodep) {
|
||||
return (nodep->width() + (VL_EDATASIZE - 1)) & ~(VL_EDATASIZE - 1);
|
||||
}
|
||||
|
|
@ -204,6 +219,7 @@ private:
|
|||
|
||||
bool expandWide(AstNodeAssign* nodep, AstConst* rhsp) {
|
||||
UINFO(8, " Wordize ASSIGN(CONST) " << nodep << endl);
|
||||
if (!doExpand(nodep)) return false;
|
||||
// -> {for each_word{ ASSIGN(WORDSEL(wide,#),WORDSEL(CONST,#))}}
|
||||
if (rhsp->num().isFourState()) {
|
||||
rhsp->v3warn(E_UNSUPPORTED, // LCOV_EXCL_LINE // impossible?
|
||||
|
|
@ -219,6 +235,7 @@ private:
|
|||
//-------- Uniops
|
||||
bool expandWide(AstNodeAssign* nodep, AstVarRef* rhsp) {
|
||||
UINFO(8, " Wordize ASSIGN(VARREF) " << nodep << endl);
|
||||
if (!doExpand(nodep)) return false;
|
||||
for (int w = 0; w < nodep->widthWords(); w++) {
|
||||
addWordAssign(nodep, w, newAstWordSelClone(rhsp, w));
|
||||
}
|
||||
|
|
@ -228,6 +245,7 @@ private:
|
|||
UINFO(8, " Wordize ASSIGN(ARRAYSEL) " << nodep << endl);
|
||||
UASSERT_OBJ(!VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType), nodep,
|
||||
"ArraySel with unpacked arrays should have been removed in V3Slice");
|
||||
if (!doExpand(nodep)) return false;
|
||||
for (int w = 0; w < nodep->widthWords(); w++) {
|
||||
addWordAssign(nodep, w, newAstWordSelClone(rhsp, w));
|
||||
}
|
||||
|
|
@ -236,6 +254,7 @@ private:
|
|||
bool expandWide(AstNodeAssign* nodep, AstNot* rhsp) {
|
||||
UINFO(8, " Wordize ASSIGN(NOT) " << nodep << endl);
|
||||
// -> {for each_word{ ASSIGN(WORDSEL(wide,#),NOT(WORDSEL(lhs,#))) }}
|
||||
if (!doExpand(nodep)) return false;
|
||||
for (int w = 0; w < nodep->widthWords(); w++) {
|
||||
addWordAssign(nodep, w,
|
||||
new AstNot(rhsp->fileline(), newAstWordSelClone(rhsp->lhsp(), w)));
|
||||
|
|
@ -245,6 +264,7 @@ private:
|
|||
//-------- Biops
|
||||
bool expandWide(AstNodeAssign* nodep, AstAnd* rhsp) {
|
||||
UINFO(8, " Wordize ASSIGN(AND) " << nodep << endl);
|
||||
if (!doExpand(nodep)) return false;
|
||||
for (int w = 0; w < nodep->widthWords(); w++) {
|
||||
addWordAssign(nodep, w,
|
||||
new AstAnd(nodep->fileline(), newAstWordSelClone(rhsp->lhsp(), w),
|
||||
|
|
@ -254,6 +274,7 @@ private:
|
|||
}
|
||||
bool expandWide(AstNodeAssign* nodep, AstOr* rhsp) {
|
||||
UINFO(8, " Wordize ASSIGN(OR) " << nodep << endl);
|
||||
if (!doExpand(nodep)) return false;
|
||||
for (int w = 0; w < nodep->widthWords(); w++) {
|
||||
addWordAssign(nodep, w,
|
||||
new AstOr(nodep->fileline(), newAstWordSelClone(rhsp->lhsp(), w),
|
||||
|
|
@ -263,6 +284,7 @@ private:
|
|||
}
|
||||
bool expandWide(AstNodeAssign* nodep, AstXor* rhsp) {
|
||||
UINFO(8, " Wordize ASSIGN(XOR) " << nodep << endl);
|
||||
if (!doExpand(nodep)) return false;
|
||||
for (int w = 0; w < nodep->widthWords(); w++) {
|
||||
addWordAssign(nodep, w,
|
||||
new AstXor(nodep->fileline(), newAstWordSelClone(rhsp->lhsp(), w),
|
||||
|
|
@ -273,6 +295,7 @@ private:
|
|||
//-------- Triops
|
||||
bool expandWide(AstNodeAssign* nodep, AstNodeCond* rhsp) {
|
||||
UINFO(8, " Wordize ASSIGN(COND) " << nodep << endl);
|
||||
if (!doExpand(nodep)) return false;
|
||||
for (int w = 0; w < nodep->widthWords(); w++) {
|
||||
addWordAssign(nodep, w,
|
||||
new AstCond(nodep->fileline(), rhsp->condp()->cloneTree(true),
|
||||
|
|
@ -417,6 +440,7 @@ private:
|
|||
|
||||
bool expandWide(AstNodeAssign* nodep, AstSel* rhsp) {
|
||||
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();
|
||||
UINFO(8, " Wordize ASSIGN(SEL,align) " << nodep << endl);
|
||||
|
|
@ -647,6 +671,7 @@ private:
|
|||
}
|
||||
bool expandWide(AstNodeAssign* nodep, AstConcat* rhsp) {
|
||||
UINFO(8, " Wordize ASSIGN(CONCAT) " << nodep << endl);
|
||||
if (!doExpand(rhsp)) return false;
|
||||
// Lhs or Rhs may be word, long, or quad.
|
||||
// newAstWordSelClone nicely abstracts the difference.
|
||||
int rhsshift = rhsp->rhsp()->widthMin();
|
||||
|
|
@ -701,6 +726,7 @@ private:
|
|||
}
|
||||
bool expandWide(AstNodeAssign* nodep, AstReplicate* rhsp) {
|
||||
UINFO(8, " Wordize ASSIGN(REPLICATE) " << nodep << endl);
|
||||
if (!doExpand(rhsp)) return false;
|
||||
AstNode* lhsp = rhsp->lhsp();
|
||||
int lhswidth = lhsp->widthMin();
|
||||
const AstConst* constp = VN_CAST(rhsp->rhsp(), Const);
|
||||
|
|
@ -857,6 +883,7 @@ private:
|
|||
iterateChildren(nodep);
|
||||
bool did = false;
|
||||
if (nodep->isWide() && ((VN_IS(nodep->lhsp(), VarRef) || VN_IS(nodep->lhsp(), ArraySel)))
|
||||
&& ((VN_IS(nodep->lhsp(), VarRef) || VN_IS(nodep->lhsp(), ArraySel)))
|
||||
&& !AstVar::scVarRecurse(nodep->lhsp()) // Need special function for SC
|
||||
&& !AstVar::scVarRecurse(nodep->rhsp())) {
|
||||
if (AstConst* rhsp = VN_CAST(nodep->rhsp(), Const)) {
|
||||
|
|
@ -897,7 +924,11 @@ private:
|
|||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit ExpandVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~ExpandVisitor() override = default;
|
||||
virtual ~ExpandVisitor() override {
|
||||
V3Stats::addStat("Optimizations, expand wides", m_statWides);
|
||||
V3Stats::addStat("Optimizations, expand wide words", m_statWideWords);
|
||||
V3Stats::addStat("Optimizations, expand limited", m_statWideLimited);
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -692,7 +692,7 @@ int V3OutFormatter::endLevels(const char* strg) {
|
|||
}
|
||||
|
||||
void V3OutFormatter::puts(const char* strg) {
|
||||
if (m_prependIndent) {
|
||||
if (m_prependIndent && strg[0] != '\n') {
|
||||
putsNoTracking(indentSpaces(endLevels(strg)));
|
||||
m_prependIndent = false;
|
||||
}
|
||||
|
|
@ -715,9 +715,13 @@ void V3OutFormatter::puts(const char* strg) {
|
|||
break;
|
||||
case ' ': wordstart = true; break;
|
||||
case '\t': wordstart = true; break;
|
||||
case '"':
|
||||
wordstart = false;
|
||||
m_inStringLiteral = !m_inStringLiteral;
|
||||
break;
|
||||
case '/':
|
||||
if (m_lang == LA_C || m_lang == LA_VERILOG) {
|
||||
if (cp > strg && cp[-1] == '/') {
|
||||
if (cp > strg && cp[-1] == '/' && !m_inStringLiteral) {
|
||||
// Output ignoring contents to EOL
|
||||
cp++;
|
||||
while (*cp && cp[1] && cp[1] != '\n') putcNoTracking(*cp++);
|
||||
|
|
@ -1012,7 +1016,8 @@ public:
|
|||
}
|
||||
}
|
||||
string protectWordsIf(const string& old, bool doIt) {
|
||||
// Split at " " (for traces), "." (for scopes), or "->" (for scopes)
|
||||
// Split at " " (for traces), "." (for scopes), "->" (for scopes), "::" (for superclass
|
||||
// reference)
|
||||
if (!(doIt && v3Global.opt.protectIds())) return old;
|
||||
string out;
|
||||
string::size_type start = 0;
|
||||
|
|
@ -1024,6 +1029,7 @@ 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*/);
|
||||
if (pos == string::npos) break;
|
||||
out += protectIf(old.substr(start, pos - start), true) + separator;
|
||||
start = pos + separator.length();
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ private:
|
|||
int m_column = 0;
|
||||
int m_nobreak = false; // Basic operator or begin paren, don't break next
|
||||
bool m_prependIndent = true;
|
||||
bool m_inStringLiteral = false;
|
||||
int m_indentLevel = 0; // Current {} indentation
|
||||
std::stack<int> m_parenVec; // Stack of columns where last ( was
|
||||
int m_bracketLevel = 0; // Intenting = { block, indicates number of {'s seen.
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
#include "V3Graph.h"
|
||||
#include "V3Const.h"
|
||||
#include "V3Stats.h"
|
||||
#include "V3Hashed.h"
|
||||
#include "V3DupFinder.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
|
|
@ -900,7 +900,7 @@ void GateVisitor::optimizeElimVar(AstVarScope* varscp, AstNode* substp, AstNode*
|
|||
//######################################################################
|
||||
// Auxiliary hash class for GateDedupeVarVisitor
|
||||
|
||||
class GateDedupeHash final : public V3HashedUserSame {
|
||||
class GateDedupeHash final : public V3DupFinderUserSame {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Ast*::user2p -> parent AstNodeAssign* for this rhsp
|
||||
|
|
@ -911,24 +911,36 @@ private:
|
|||
// AstUser1InUse m_inuser1; (Allocated for use in GateVisitor)
|
||||
// AstUser2InUse m_inuser2; (Allocated for use in GateVisitor)
|
||||
AstUser3InUse m_inuser3;
|
||||
// AstUser4InUse m_inuser4; (Allocated for use in V3Hashed)
|
||||
// AstUser4InUse m_inuser4; (Allocated for use in V3Hasher via V3DupFinder)
|
||||
AstUser5InUse m_inuser5;
|
||||
|
||||
V3Hashed m_hashed; // Hash, contains rhs of assigns
|
||||
V3DupFinder m_dupFinder; // Duplicate finder for rhs of assigns
|
||||
std::unordered_set<AstNode*> m_nodeDeleteds; // Any node in this hash was deleted
|
||||
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void hash(AstNode* nodep) {
|
||||
// !nullptr && the object is hashable
|
||||
if (nodep && !nodep->sameHash().isIllegal()) m_hashed.hash(nodep);
|
||||
}
|
||||
bool sameHash(AstNode* node1p, AstNode* node2p) {
|
||||
return (node1p && node2p && !node1p->sameHash().isIllegal()
|
||||
&& !node2p->sameHash().isIllegal() && m_hashed.sameNodes(node1p, node2p));
|
||||
}
|
||||
bool same(AstNode* node1p, AstNode* node2p) {
|
||||
return node1p == node2p || sameHash(node1p, node2p);
|
||||
// Regarding the complexity of this funcition 'same':
|
||||
// Applying this comparison function to a a set of n trees pairwise is O(n^2) in the
|
||||
// number of comparisons (number of pairs). AstNode::sameTree itself, is O(sizeOfTree) in
|
||||
// the worst case, which happens if the operands of sameTree are indeed identical copies,
|
||||
// which means this line is O(n^2*sizeOfTree), iff you are comparing identical copies of
|
||||
// the same tree. In practice the identity comparison over the pointers, and the short
|
||||
// circuiting in sameTree means that for comparing the same tree instance to itself, or
|
||||
// trees of different types/shapes is a lot closer to O(1), so this 'same' function is
|
||||
// Omega(n^2) and O(n^2*sizeOfTree), and in practice as we are mostly comparing the same
|
||||
// instance to itself or different trees, the complexity should be closer to the lower
|
||||
// bound.
|
||||
//
|
||||
// Also if you see where this 'same' function is used within isSame, it's only ever
|
||||
// comparing AstActive nodes, which are very likely not to compare equals (and for the
|
||||
// purposes of V3Gate, we probably only care about them either being identical instances,
|
||||
// or having the same sensitivities anyway, so if this becomes a problem, it can be
|
||||
// improved which should also speed things up), and AstNodeMath for if conditions, which
|
||||
// are hopefully small, and to be safe they should probably be only considered same when
|
||||
// identical instances (otherwise if writing the condition between 2 ifs don't really
|
||||
// merge).
|
||||
return node1p == node2p || (node1p && node1p->sameTree(node2p));
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -958,7 +970,7 @@ public:
|
|||
|| (extra2p && m_nodeDeleteds.find(extra2p) != m_nodeDeleteds.end()));
|
||||
}
|
||||
|
||||
// Callback from V3Hashed::findDuplicate
|
||||
// Callback from V3DupFinder::findDuplicate
|
||||
virtual bool isSame(AstNode* node1p, AstNode* node2p) override {
|
||||
// Assignment may have been hashReplaced, if so consider non-match (effectively removed)
|
||||
if (isReplaced(node1p) || isReplaced(node2p)) {
|
||||
|
|
@ -977,25 +989,21 @@ public:
|
|||
rhsp->user3p(extra1p);
|
||||
rhsp->user5p(extra2p);
|
||||
|
||||
hash(extra1p);
|
||||
hash(extra2p);
|
||||
|
||||
const auto inserted = m_hashed.hashAndInsert(rhsp);
|
||||
const auto dupit = m_hashed.findDuplicate(rhsp, this);
|
||||
// Even though rhsp was just inserted, V3Hashed::findDuplicate doesn't
|
||||
// return anything in the hash that has the same pointer (V3Hashed.cpp::findDuplicate)
|
||||
const auto inserted = m_dupFinder.insert(rhsp);
|
||||
const auto dupit = m_dupFinder.findDuplicate(rhsp, this);
|
||||
// Even though rhsp was just inserted, V3DupFinder::findDuplicate doesn't
|
||||
// return anything in the hash that has the same pointer (V3DupFinder::findDuplicate)
|
||||
// So dupit is either a different, duplicate rhsp, or the end of the hash.
|
||||
if (dupit != m_hashed.end()) {
|
||||
m_hashed.erase(inserted);
|
||||
return VN_CAST(m_hashed.iteratorNodep(dupit)->user2p(), NodeAssign);
|
||||
if (dupit != m_dupFinder.end()) {
|
||||
m_dupFinder.erase(inserted);
|
||||
return VN_CAST(dupit->second->user2p(), NodeAssign);
|
||||
}
|
||||
// Retain new inserted information
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void check() {
|
||||
m_hashed.check();
|
||||
for (const auto& itr : m_hashed) {
|
||||
for (const auto& itr : m_dupFinder) {
|
||||
AstNode* nodep = itr.second;
|
||||
AstNode* activep = nodep->user3p();
|
||||
AstNode* condVarp = nodep->user5p();
|
||||
|
|
@ -1003,9 +1011,9 @@ public:
|
|||
// This class won't break if activep isn't an active, or
|
||||
// ifVar isn't a var, but this is checking the caller's construction.
|
||||
UASSERT_OBJ(!activep || (!VN_DELETED(activep) && VN_IS(activep, Active)), nodep,
|
||||
"V3Hashed check failed, lost active pointer");
|
||||
"V3DupFinder check failed, lost active pointer");
|
||||
UASSERT_OBJ(!condVarp || !VN_DELETED(condVarp), nodep,
|
||||
"V3Hashed check failed, lost if pointer");
|
||||
"V3DupFinder check failed, lost if pointer");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Hash calculation
|
||||
//
|
||||
// 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 "V3Hash.h"
|
||||
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
|
||||
V3Hash::V3Hash(const std::string& val)
|
||||
: m_value{static_cast<uint32_t>(std::hash<std::string>{}(val))} {}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const V3Hash& rhs) {
|
||||
return os << std::hex << std::setw(8) << std::setfill('0') << rhs.value();
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Hash calculation
|
||||
//
|
||||
// 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_V3HASH_H_
|
||||
#define VERILATOR_V3HASH_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
//######################################################################
|
||||
// V3Hash -- Generic hashing
|
||||
|
||||
class V3Hash final {
|
||||
uint32_t m_value; // The 32-bit hash value.
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
V3Hash()
|
||||
: m_value{0} {}
|
||||
explicit V3Hash(uint32_t val)
|
||||
: m_value{val + 0x9e3779b9} {} // This is the same as 'V3Hash() + 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(const std::string& val);
|
||||
|
||||
// METHODS
|
||||
uint32_t value() const { return m_value; }
|
||||
|
||||
// OPERATORS
|
||||
// Comparisons
|
||||
bool operator==(const V3Hash& rh) const { return m_value == rh.m_value; }
|
||||
bool operator!=(const V3Hash& rh) const { return m_value != rh.m_value; }
|
||||
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)));
|
||||
}
|
||||
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); }
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const V3Hash& rhs);
|
||||
|
||||
#endif // Guard
|
||||
207
src/V3Hashed.cpp
207
src/V3Hashed.cpp
|
|
@ -1,207 +0,0 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Hashed common code into functions
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//*************************************************************************
|
||||
// V3Hashed's Transformations:
|
||||
//
|
||||
// Hash each node depth first
|
||||
// Hash includes varp name and operator type, and constants
|
||||
// Form lookup table based on hash of each statement w/ nodep and next nodep
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Hashed.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3File.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
//######################################################################
|
||||
// Hashed state, as a visitor of each AstNode
|
||||
|
||||
class HashedVisitor final : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstNodeStmt::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal)
|
||||
// AstUser4InUse in V3Hashed.h
|
||||
|
||||
// STATE
|
||||
V3Hash m_lowerHash; // Hash of the statement we're building
|
||||
bool m_cacheInUser4; // Use user4 to cache each V3Hash?
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void nodeHashIterate(AstNode* nodep) {
|
||||
V3Hash thisHash;
|
||||
if (!m_cacheInUser4 || !nodep->user4()) {
|
||||
UASSERT_OBJ(
|
||||
!(VN_IS(nodep->backp(), CFunc)
|
||||
&& !(VN_IS(nodep, NodeStmt) || VN_IS(nodep, CFunc))),
|
||||
nodep,
|
||||
"Node " << nodep->prettyTypeName()
|
||||
<< " in statement position but not marked stmt (node under function)");
|
||||
VL_RESTORER(m_lowerHash);
|
||||
{
|
||||
m_lowerHash = nodep->sameHash();
|
||||
UASSERT_OBJ(!m_lowerHash.isIllegal(), nodep,
|
||||
"sameHash function undefined (returns 0) for node under CFunc.");
|
||||
// For identical nodes, the type should be the same thus
|
||||
// dtypep should be the same too
|
||||
m_lowerHash
|
||||
= V3Hash(m_lowerHash, V3Hash(nodep->type() << 6, V3Hash(nodep->dtypep())));
|
||||
// Now update m_lowerHash for our children's (and next children) contributions
|
||||
iterateChildren(nodep);
|
||||
// Store the hash value
|
||||
nodep->user4(m_lowerHash.fullValue());
|
||||
// UINFO(9, " hashnode "<<m_lowerHash<<" "<<nodep<<endl);
|
||||
thisHash = m_lowerHash;
|
||||
}
|
||||
}
|
||||
// Update what will become the above node's hash
|
||||
m_lowerHash += m_cacheInUser4 ? V3Hashed::nodeHash(nodep) : thisHash;
|
||||
}
|
||||
|
||||
//--------------------
|
||||
virtual void visit(AstVar*) override {}
|
||||
virtual void visit(AstTypedef*) override {}
|
||||
virtual void visit(AstParamTypeDType*) override {}
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
// Hash not just iterate
|
||||
nodeHashIterate(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit HashedVisitor(AstNode* nodep)
|
||||
: m_cacheInUser4{true} {
|
||||
nodeHashIterate(nodep);
|
||||
// UINFO(9," stmthash "<<hex<<V3Hashed::nodeHash(nodep)<<" "<<nodep<<endl);
|
||||
}
|
||||
explicit HashedVisitor(const AstNode* nodep)
|
||||
: m_cacheInUser4{false} {
|
||||
nodeHashIterate(const_cast<AstNode*>(nodep));
|
||||
}
|
||||
V3Hash finalHash() const { return m_lowerHash; }
|
||||
virtual ~HashedVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Hashed class functions
|
||||
|
||||
V3Hash V3Hashed::uncachedHash(const AstNode* nodep) {
|
||||
HashedVisitor visitor(nodep);
|
||||
return visitor.finalHash();
|
||||
}
|
||||
|
||||
V3Hashed::iterator V3Hashed::hashAndInsert(AstNode* nodep) {
|
||||
hash(nodep);
|
||||
return m_hashMmap.emplace(nodeHash(nodep), nodep);
|
||||
}
|
||||
|
||||
void V3Hashed::hash(AstNode* nodep) {
|
||||
UINFO(8, " hashI " << nodep << endl);
|
||||
if (!nodep->user4p()) { HashedVisitor visitor(nodep); }
|
||||
}
|
||||
|
||||
bool V3Hashed::sameNodes(AstNode* node1p, AstNode* node2p) {
|
||||
UASSERT_OBJ(node1p->user4p(), node1p, "Called isIdentical on non-hashed nodes");
|
||||
UASSERT_OBJ(node2p->user4p(), node2p, "Called isIdentical on non-hashed nodes");
|
||||
return (node1p->user4p() == node2p->user4p() // Same hash
|
||||
&& node1p->sameTree(node2p));
|
||||
}
|
||||
|
||||
void V3Hashed::erase(iterator it) {
|
||||
AstNode* nodep = iteratorNodep(it);
|
||||
UINFO(8, " erase " << nodep << endl);
|
||||
UASSERT_OBJ(nodep->user4p(), nodep, "Called removeNode on non-hashed node");
|
||||
m_hashMmap.erase(it);
|
||||
nodep->user4p(nullptr); // So we don't allow removeNode again
|
||||
}
|
||||
|
||||
void V3Hashed::check() {
|
||||
for (const auto& itr : *this) {
|
||||
AstNode* nodep = itr.second;
|
||||
UASSERT_OBJ(nodep->user4p(), nodep, "V3Hashed check failed, non-hashed node");
|
||||
}
|
||||
}
|
||||
|
||||
void V3Hashed::dumpFilePrefixed(const string& nameComment, bool tree) {
|
||||
if (v3Global.opt.dumpTree()) dumpFile(v3Global.debugFilename(nameComment) + ".hash", tree);
|
||||
}
|
||||
|
||||
void V3Hashed::dumpFile(const string& filename, bool tree) {
|
||||
const std::unique_ptr<std::ofstream> logp(V3File::new_ofstream(filename));
|
||||
if (logp->fail()) v3fatal("Can't write " << filename);
|
||||
|
||||
std::unordered_map<int, int> dist;
|
||||
|
||||
V3Hash lasthash;
|
||||
int num_in_bucket = 0;
|
||||
for (HashMmap::iterator it = begin(); true; ++it) {
|
||||
if (it == end() || lasthash != it->first) {
|
||||
if (it != end()) lasthash = it->first;
|
||||
if (num_in_bucket) {
|
||||
if (dist.find(num_in_bucket) == dist.end()) {
|
||||
dist.emplace(num_in_bucket, 1);
|
||||
} else {
|
||||
++dist[num_in_bucket];
|
||||
}
|
||||
}
|
||||
num_in_bucket = 0;
|
||||
}
|
||||
if (it == end()) break;
|
||||
num_in_bucket++;
|
||||
}
|
||||
*logp << "\n*** STATS:\n\n";
|
||||
*logp << " #InBucket Occurrences\n";
|
||||
for (const auto& i : dist) {
|
||||
*logp << " " << std::setw(9) << i.first << " " << std::setw(12) << i.second << '\n';
|
||||
}
|
||||
|
||||
*logp << "\n*** Dump:\n\n";
|
||||
for (const auto& itr : *this) {
|
||||
if (lasthash != itr.first) {
|
||||
lasthash = itr.first;
|
||||
*logp << " " << itr.first << '\n';
|
||||
}
|
||||
*logp << "\t" << itr.second << '\n';
|
||||
// Dumping the entire tree may make nearly N^2 sized dumps,
|
||||
// because the nodes under this one may also be in the hash table!
|
||||
if (tree) itr.second->dumpTree(*logp, " ");
|
||||
}
|
||||
}
|
||||
|
||||
V3Hashed::iterator V3Hashed::findDuplicate(AstNode* nodep, V3HashedUserSame* checkp) {
|
||||
UINFO(8, " findD " << nodep << endl);
|
||||
UASSERT_OBJ(nodep->user4p(), nodep, "Called findDuplicate on non-hashed node");
|
||||
std::pair<HashMmap::iterator, HashMmap::iterator> eqrange
|
||||
= mmap().equal_range(nodeHash(nodep));
|
||||
for (HashMmap::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) {
|
||||
AstNode* node2p = eqit->second;
|
||||
if (nodep != node2p && (!checkp || checkp->isSame(nodep, node2p))
|
||||
&& sameNodes(nodep, node2p)) {
|
||||
return eqit;
|
||||
}
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Hash AST trees to find duplicates
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2005-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_V3HASHED_H_
|
||||
#define VERILATOR_V3HASHED_H_
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
class VHashedBase VL_NOT_FINAL {
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VHashedBase() = default;
|
||||
~VHashedBase() = default;
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
|
||||
struct V3HashedUserSame {
|
||||
// Functor for V3Hashed::findDuplicate
|
||||
virtual bool isSame(AstNode*, AstNode*) = 0;
|
||||
V3HashedUserSame() = default;
|
||||
virtual ~V3HashedUserSame() = default;
|
||||
};
|
||||
|
||||
class V3Hashed final : public VHashedBase {
|
||||
// NODE STATE
|
||||
// AstNode::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal)
|
||||
AstUser4InUse m_inuser4;
|
||||
|
||||
// TYPES
|
||||
public:
|
||||
using HashMmap = std::multimap<V3Hash, AstNode*>;
|
||||
using iterator = HashMmap::iterator;
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
HashMmap m_hashMmap; // hashvalue -> nodes with that hash
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
V3Hashed() { clear(); }
|
||||
~V3Hashed() = default;
|
||||
|
||||
// ACCESSORS
|
||||
HashMmap& mmap() { return m_hashMmap; } // Return map for iteration
|
||||
iterator begin() { return m_hashMmap.begin(); }
|
||||
iterator end() { return m_hashMmap.end(); }
|
||||
|
||||
// METHODS
|
||||
void clear() {
|
||||
m_hashMmap.clear();
|
||||
AstNode::user4ClearTree();
|
||||
}
|
||||
void check(); // Check assertions on structure
|
||||
// Hash the node, and insert into map. Return iterator to inserted
|
||||
iterator hashAndInsert(AstNode* nodep);
|
||||
static void hash(AstNode* nodep); // Only hash the node
|
||||
// After hashing, and tell if identical
|
||||
static bool sameNodes(AstNode* node1p, AstNode* node2p);
|
||||
void erase(iterator it); // Remove node from structures
|
||||
// Return duplicate in hash, if any, with optional user check for sameness
|
||||
iterator findDuplicate(AstNode* nodep, V3HashedUserSame* checkp = nullptr);
|
||||
AstNode* iteratorNodep(iterator it) { return it->second; }
|
||||
void dumpFile(const string& filename, bool tree);
|
||||
void dumpFilePrefixed(const string& nameComment, bool tree = false);
|
||||
static V3Hash nodeHash(AstNode* nodep) { return V3Hash(nodep->user4p()); }
|
||||
// Hash of the nodep tree, without caching in user4.
|
||||
static V3Hash uncachedHash(const AstNode* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
@ -0,0 +1,481 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: AstNode hash computation
|
||||
//
|
||||
// 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 "V3Hasher.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
//######################################################################
|
||||
// Visitor that computes node hashes
|
||||
|
||||
class HasherVisitor final : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// AstNode::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal)
|
||||
// AstUser4InUse in V3Hasher.h
|
||||
|
||||
// STATE
|
||||
V3Hash m_hash; // Hash value accumulator
|
||||
const bool m_cacheInUser4; // Use user4 to cache each V3Hash?
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
V3Hash hashNodeAndIterate(AstNode* nodep, bool hashDType, bool hashChildren,
|
||||
std::function<void()>&& f) {
|
||||
if (m_cacheInUser4 && nodep->user4()) {
|
||||
return V3Hash(nodep->user4());
|
||||
} else {
|
||||
VL_RESTORER(m_hash);
|
||||
// Reset accumulator
|
||||
m_hash = V3Hash(nodep->type()); // Node type
|
||||
f(); // Node specific hash
|
||||
if (hashDType && nodep != nodep->dtypep()) iterateNull(nodep->dtypep()); // Node dtype
|
||||
if (hashChildren) iterateChildrenConst(nodep); // Children
|
||||
if (m_cacheInUser4) nodep->user4(m_hash.value());
|
||||
return m_hash;
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
|
||||
constexpr static bool HASH_DTYPE = true;
|
||||
constexpr static bool HASH_CHILDREN = true;
|
||||
|
||||
// Each visitor below contributes to the hash any node specific content
|
||||
// that is not dependent on either of the following, as these are
|
||||
// included by default by hashNode:
|
||||
// - Node type (as given by AstNode::type())
|
||||
// - Node dtype (unless !hashDType)
|
||||
// - child nodes (unless !hashChildren)
|
||||
//
|
||||
// The hash must be stable, which means in particular it cannot rely on
|
||||
// pointer values, or any other value that might differ between separate
|
||||
// invocations of Verilator over the same design.
|
||||
//
|
||||
// Note there is a circularity problem where some child nodes can back
|
||||
// to their ancestral nodes via member pointers, which can lead to an
|
||||
// infinite traversal. To break this, nodes that are subject to such
|
||||
// referencing and represent code which can reasonably be assumed not to
|
||||
// be equivalent to any other code, are hashed either by name (e.g.:
|
||||
// AstNodeModule), or by unique identifier (e.g.: AstNodeUOrStructDType).
|
||||
|
||||
//------------------------------------------------------------
|
||||
// AstNode - Warns to help find missing cases
|
||||
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
#if VL_DEBUG
|
||||
UINFO(0, "%Warning: Hashing node as AstNode: " << nodep);
|
||||
#endif
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
// AstNodeDType
|
||||
virtual void visit(AstNodeArrayDType* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
|
||||
iterateNull(nodep->virtRefDTypep());
|
||||
m_hash += nodep->left();
|
||||
m_hash += nodep->right();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstNodeUOrStructDType* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, false, [=]() { //
|
||||
m_hash += nodep->uniqueNum();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstParamTypeDType* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
||||
m_hash += nodep->name();
|
||||
m_hash += nodep->varType();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstMemberDType* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->name();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstDefImplicitDType* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->uniqueNum();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstAssocArrayDType* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
|
||||
iterateNull(nodep->virtRefDTypep());
|
||||
iterateNull(nodep->virtRefDType2p());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstDynArrayDType* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
||||
iterateNull(nodep->virtRefDTypep());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstUnsizedArrayDType* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
||||
iterateNull(nodep->virtRefDTypep());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstBasicDType* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
|
||||
m_hash += nodep->keyword();
|
||||
m_hash += nodep->nrange().left();
|
||||
m_hash += nodep->nrange().right();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstConstDType* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
iterateNull(nodep->virtRefDTypep());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstClassRefDType* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
||||
iterateNull(nodep->classp());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstIfaceRefDType* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
||||
iterateNull(nodep->cellp());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstQueueDType* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
||||
iterateNull(nodep->virtRefDTypep());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstRefDType* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
|
||||
m_hash += nodep->name();
|
||||
iterateNull(nodep->typedefp());
|
||||
iterateNull(nodep->refDTypep());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstVoidDType* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {});
|
||||
}
|
||||
virtual void visit(AstEnumDType* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, false, [=]() { //
|
||||
m_hash += nodep->uniqueNum();
|
||||
});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
// AstNodeMath
|
||||
virtual void visit(AstNodeMath* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
||||
}
|
||||
virtual void visit(AstConst* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->num().toHash();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstNullCheck* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
||||
}
|
||||
virtual void visit(AstCCast* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->size();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
||||
if (nodep->varScopep()) {
|
||||
iterateNull(nodep->varScopep());
|
||||
} else {
|
||||
iterateNull(nodep->varp());
|
||||
m_hash += nodep->hiernameToProt();
|
||||
}
|
||||
});
|
||||
}
|
||||
virtual void visit(AstVarXRef* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
||||
iterateNull(nodep->varp());
|
||||
m_hash += nodep->dotted();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstMemberSel* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->name();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstFScanF* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->text();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstSScanF* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->text();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstTestPlusArgs* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->text();
|
||||
});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
// AstNodeStmt
|
||||
virtual void visit(AstNodeStmt* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {});
|
||||
}
|
||||
virtual void visit(AstNodeText* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->text();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstNodeCCall* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
||||
iterateNull(nodep->funcp());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstNodeFTaskRef* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
|
||||
iterateNull(nodep->taskp());
|
||||
iterateNull(nodep->classOrPackagep());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstCMethodHard* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->name();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstCoverInc* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
||||
iterateNull(nodep->declp());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstDisplay* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->displayType();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstMonitorOff* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->off();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstJumpGo* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
||||
iterateNull(nodep->labelp());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstTraceInc* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
||||
iterateNull(nodep->declp());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstNodeCoverOrAssert* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->name();
|
||||
});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
// AstNode direct descendents
|
||||
virtual void visit(AstNodeRange* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, false, [=]() {
|
||||
m_hash += nodep->origName();
|
||||
m_hash += nodep->hierName();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstNodePreSel* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
||||
}
|
||||
virtual void visit(AstClassExtends* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
||||
}
|
||||
virtual void visit(AstSelLoopVars* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
||||
}
|
||||
virtual void visit(AstDefParam* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
||||
}
|
||||
virtual void visit(AstArg* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
||||
}
|
||||
virtual void visit(AstParseRef* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
||||
m_hash += nodep->expect();
|
||||
m_hash += nodep->name();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstClassOrPackageRef* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
iterateNull(nodep->classOrPackageNodep());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstSenItem* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->edgeType();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstSenTree* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
||||
}
|
||||
virtual void visit(AstSFormatF* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->text();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstElabDisplay* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->displayType();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstInitItem* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
||||
}
|
||||
virtual void visit(AstInitArray* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
||||
}
|
||||
virtual void visit(AstPragma* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->pragType();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstAttrOf* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->attrType();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstNodeFile* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->name();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
||||
}
|
||||
virtual void visit(AstVar* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
||||
m_hash += nodep->name();
|
||||
m_hash += nodep->varType();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstScope* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, false, [=]() {
|
||||
m_hash += nodep->name();
|
||||
iterateNull(nodep->aboveScopep());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstVarScope* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
||||
iterateNull(nodep->varp());
|
||||
iterateNull(nodep->scopep());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstEnumItem* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->name();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstTypedef* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->name();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstTypedefFwd* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->name();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstActive* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
iterateNull(nodep->sensesp());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstCell* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
||||
m_hash += nodep->name();
|
||||
iterateNull(nodep->modp());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstCellInline* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
||||
m_hash += nodep->name();
|
||||
iterateNull(nodep->scopep());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstNodeFTask* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->name();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstModport* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->name();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstModportVarRef* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
||||
m_hash += nodep->name();
|
||||
iterateNull(nodep->varp());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstModportFTaskRef* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
||||
m_hash += nodep->name();
|
||||
iterateNull(nodep->ftaskp());
|
||||
});
|
||||
}
|
||||
virtual void visit(AstNodeProcedure* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
||||
}
|
||||
virtual void visit(AstNodeBlock* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
||||
m_hash += nodep->name();
|
||||
});
|
||||
}
|
||||
virtual void visit(AstPin* nodep) override {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
||||
m_hash += nodep->name();
|
||||
m_hash += nodep->pinNum();
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit HasherVisitor(AstNode* nodep)
|
||||
: m_cacheInUser4{true} {
|
||||
iterate(nodep);
|
||||
}
|
||||
explicit HasherVisitor(const AstNode* nodep)
|
||||
: m_cacheInUser4{false} {
|
||||
iterate(const_cast<AstNode*>(nodep));
|
||||
}
|
||||
V3Hash finalHash() const { return m_hash; }
|
||||
virtual ~HasherVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// V3Hasher methods
|
||||
|
||||
V3Hash V3Hasher::operator()(AstNode* nodep) const {
|
||||
if (!nodep->user4()) { HasherVisitor visitor(nodep); }
|
||||
return V3Hash(nodep->user4());
|
||||
}
|
||||
|
||||
V3Hash V3Hasher::uncachedHash(const AstNode* nodep) {
|
||||
HasherVisitor visitor(nodep);
|
||||
return visitor.finalHash();
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Hash AST trees to find duplicates
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2005-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
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// V3Hasher handles computation of AstNode hashes
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_V3HASHER_H_
|
||||
#define VERILATOR_V3HASHER_H_
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3Hash.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3Hasher final {
|
||||
// NODE STATE
|
||||
// AstNode::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal)
|
||||
AstUser4InUse m_inuser4;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
V3Hasher() = default;
|
||||
~V3Hasher() = default;
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// Compute hash of node. This method caches the hash in the node's user4().
|
||||
V3Hash operator()(AstNode* nodep) const;
|
||||
|
||||
// Compute hash of node, without caching in user4.
|
||||
static V3Hash uncachedHash(const AstNode* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
|
||||
constexpr int MAX_SPRINTF_DOUBLE_SIZE
|
||||
= 1100; // Maximum characters with a sprintf %e/%f/%g (really 1079)
|
||||
|
|
@ -883,7 +884,11 @@ string V3Number::toString() const {
|
|||
return str;
|
||||
}
|
||||
|
||||
uint32_t V3Number::toHash() const { return m_value[0]; }
|
||||
V3Hash V3Number::toHash() const {
|
||||
V3Hash hash(m_width);
|
||||
for (int i = 0; i < words(); ++i) { hash += m_value[i]; }
|
||||
return hash;
|
||||
}
|
||||
|
||||
uint32_t V3Number::edataWord(int eword) const {
|
||||
UASSERT(!isFourState(), "edataWord with 4-state " << *this);
|
||||
|
|
@ -1526,11 +1531,8 @@ bool V3Number::isCaseEq(const V3Number& rhs) const {
|
|||
if (isString()) return toString() == rhs.toString();
|
||||
if (isDouble()) return toDouble() == rhs.toDouble();
|
||||
if (this->width() != rhs.width()) return false;
|
||||
|
||||
for (int bit = 0; bit < std::max(this->width(), rhs.width()); bit++) {
|
||||
if (this->bitIs(bit) != rhs.bitIs(bit)) return false;
|
||||
}
|
||||
return true;
|
||||
if (m_value != rhs.m_value) return false;
|
||||
return m_valueX == rhs.m_valueX;
|
||||
}
|
||||
|
||||
V3Number& V3Number::opCaseEq(const V3Number& lhs, const V3Number& rhs) {
|
||||
|
|
@ -1759,13 +1761,17 @@ V3Number& V3Number::opMul(const V3Number& lhs, const V3Number& rhs) {
|
|||
opCleanThis(); // Mult produces extra bits in result
|
||||
} else {
|
||||
for (int lword = 0; lword < lhs.words(); lword++) {
|
||||
const vluint64_t lwordval = static_cast<vluint64_t>(lhs.m_value[lword]);
|
||||
if (lwordval == 0) continue;
|
||||
for (int rword = 0; rword < rhs.words(); rword++) {
|
||||
vluint64_t mul = static_cast<vluint64_t>(lhs.m_value[lword])
|
||||
* static_cast<vluint64_t>(rhs.m_value[rword]);
|
||||
const vluint64_t rwordval = static_cast<vluint64_t>(rhs.m_value[rword]);
|
||||
if (rwordval == 0) continue;
|
||||
vluint64_t mul = lwordval * rwordval;
|
||||
for (int qword = lword + rword; qword < this->words(); qword++) {
|
||||
mul += static_cast<vluint64_t>(m_value[qword]);
|
||||
m_value[qword] = (mul & 0xffffffffULL);
|
||||
mul = (mul >> 32ULL) & 0xffffffffULL;
|
||||
if (mul == 0) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Hash.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
|
@ -298,7 +299,7 @@ public:
|
|||
string toDecimalS() const; // return ASCII signed decimal number
|
||||
string toDecimalU() const; // return ASCII unsigned decimal number
|
||||
double toDouble() const;
|
||||
uint32_t toHash() const;
|
||||
V3Hash toHash() const;
|
||||
uint32_t edataWord(int eword) const;
|
||||
uint8_t dataByte(int byte) const;
|
||||
uint32_t countBits(const V3Number& ctrl) const;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,232 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Command line option parser
|
||||
//
|
||||
// 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 V3OPTION_PARSER_NO_VOPTION_BOOL
|
||||
#include "V3Global.h"
|
||||
#include "V3Options.h"
|
||||
#endif
|
||||
#include "V3Error.h"
|
||||
#include "V3OptionParser.h"
|
||||
#include "V3String.h"
|
||||
|
||||
//######################################################################
|
||||
// V3OptionParser::Impl
|
||||
struct V3OptionParser::Impl {
|
||||
// TYPES
|
||||
|
||||
// Setting for isOnOffAllowed() and isPartialMatchAllowed()
|
||||
enum class en : uint8_t {
|
||||
NONE, // "-opt"
|
||||
ONOFF, // "-opt" and "-no-opt"
|
||||
VALUE // "-opt val"
|
||||
};
|
||||
// Base class of actual action classes
|
||||
template <en MODE, bool ALLOW_PARTIAL_MATCH = false>
|
||||
class ActionBase VL_NOT_FINAL : public ActionIfs {
|
||||
bool m_undocumented = false; // This option is not documented
|
||||
public:
|
||||
virtual bool isValueNeeded() const override final { return MODE == en::VALUE; }
|
||||
virtual bool isOnOffAllowed() const override final { return MODE == en::ONOFF; }
|
||||
virtual bool isPartialMatchAllowed() const override final { return ALLOW_PARTIAL_MATCH; }
|
||||
virtual bool isUndocumented() const override { return m_undocumented; }
|
||||
virtual void undocumented() override { m_undocumented = true; }
|
||||
};
|
||||
|
||||
// Actual action classes
|
||||
template <typename T> class ActionSet; // "-opt" for bool-ish, "-opt val" for int and string
|
||||
template <typename BOOL> class ActionOnOff; // "-opt" and "-no-opt" for bool-ish
|
||||
class ActionCbCall; // Callback without argument for "-opt"
|
||||
class ActionCbOnOff; // Callback for "-opt" and "-no-opt"
|
||||
template <class T> class ActionCbVal; // Callback for "-opt val"
|
||||
class ActionCbPartialMatch; // Callback "-O3" for "-O"
|
||||
class ActionCbPartialMatchVal; // Callback "-debugi-V3Options 3" for "-debugi-"
|
||||
|
||||
// MEMBERS
|
||||
std::map<const string, std::unique_ptr<ActionIfs>> m_options; // All actions for option
|
||||
bool m_isFinalized{false}; // Becomes after finalize() is called
|
||||
VSpellCheck m_spellCheck; // Suggests closest option when not found
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Action classes in V3OptionParser::Impl
|
||||
|
||||
#define V3OPTION_PARSER_DEF_ACT_CLASS(className, type, body, enType) \
|
||||
template <> class V3OptionParser::Impl::className<type> final : public ActionBase<enType> { \
|
||||
type* m_valp; /* Pointer to a option variable*/ \
|
||||
\
|
||||
public: \
|
||||
explicit className(type* valp) \
|
||||
: m_valp(valp) {} \
|
||||
virtual void exec(const char* optp, const char* argp) override { body; } \
|
||||
}
|
||||
|
||||
V3OPTION_PARSER_DEF_ACT_CLASS(ActionSet, bool, *m_valp = true, en::NONE);
|
||||
#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL
|
||||
V3OPTION_PARSER_DEF_ACT_CLASS(ActionSet, VOptionBool, m_valp->setTrueOrFalse(true), en::NONE);
|
||||
#endif
|
||||
V3OPTION_PARSER_DEF_ACT_CLASS(ActionSet, int, *m_valp = std::atoi(argp), en::VALUE);
|
||||
V3OPTION_PARSER_DEF_ACT_CLASS(ActionSet, string, *m_valp = argp, en::VALUE);
|
||||
|
||||
V3OPTION_PARSER_DEF_ACT_CLASS(ActionOnOff, bool, *m_valp = !hasPrefixNo(optp), en::ONOFF);
|
||||
#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL
|
||||
V3OPTION_PARSER_DEF_ACT_CLASS(ActionOnOff, VOptionBool, m_valp->setTrueOrFalse(!hasPrefixNo(optp)),
|
||||
en::ONOFF);
|
||||
#endif
|
||||
#undef V3OPTION_PARSER_DEF_ACT_CLASS
|
||||
|
||||
#define V3OPTION_PARSER_DEF_ACT_CB_CLASS(className, funcType, body, ...) \
|
||||
class V3OptionParser::Impl::className final : public ActionBase<__VA_ARGS__> { \
|
||||
std::function<funcType> m_cb; /* Callback function */ \
|
||||
\
|
||||
public: \
|
||||
using CbType = std::function<funcType>; \
|
||||
explicit className(CbType cb) \
|
||||
: m_cb(std::move(cb)) {} \
|
||||
virtual void exec(const char* optp, const char* argp) override { body; } \
|
||||
}
|
||||
|
||||
V3OPTION_PARSER_DEF_ACT_CB_CLASS(ActionCbCall, void(void), m_cb(), en::NONE);
|
||||
V3OPTION_PARSER_DEF_ACT_CB_CLASS(ActionCbOnOff, void(bool), m_cb(!hasPrefixNo(optp)), en::ONOFF);
|
||||
template <>
|
||||
V3OPTION_PARSER_DEF_ACT_CB_CLASS(ActionCbVal<int>, void(int), m_cb(std::atoi(argp)), en::VALUE);
|
||||
template <>
|
||||
V3OPTION_PARSER_DEF_ACT_CB_CLASS(ActionCbVal<const char*>, void(const char*), m_cb(argp),
|
||||
en::VALUE);
|
||||
V3OPTION_PARSER_DEF_ACT_CB_CLASS(ActionCbPartialMatch, void(const char*), m_cb(optp), en::NONE,
|
||||
true);
|
||||
V3OPTION_PARSER_DEF_ACT_CB_CLASS(ActionCbPartialMatchVal, void(const char*, const char*),
|
||||
m_cb(optp, argp), en::VALUE, true);
|
||||
|
||||
#undef V3OPTION_PARSER_DEF_ACT_CB_CLASS
|
||||
|
||||
//######################################################################
|
||||
// Member functions of V3OptionParser
|
||||
|
||||
V3OptionParser::ActionIfs* V3OptionParser::find(const char* optp) {
|
||||
auto it = m_pimpl->m_options.find(optp);
|
||||
if (it != m_pimpl->m_options.end()) return it->second.get();
|
||||
for (auto&& act : m_pimpl->m_options) {
|
||||
if (act.second->isOnOffAllowed()) { // Find starts with "-no"
|
||||
const char* const nop = std::strncmp(optp, "-no", 3) ? nullptr : (optp + 3);
|
||||
if (nop && (act.first == nop || act.first == (string{"-"} + nop))) {
|
||||
return act.second.get();
|
||||
}
|
||||
} else if (act.second->isPartialMatchAllowed()) {
|
||||
if (!std::strncmp(optp, act.first.c_str(), act.first.length())) {
|
||||
return act.second.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <class ACT, class ARG>
|
||||
V3OptionParser::ActionIfs& V3OptionParser::add(const std::string& opt, ARG arg) {
|
||||
UASSERT(!m_pimpl->m_isFinalized, "Cannot add after finalize() is called");
|
||||
std::unique_ptr<ACT> act{new ACT{std::move(arg)}};
|
||||
UASSERT(opt.size() >= 2, opt << " is too short");
|
||||
UASSERT(opt[0] == '-' || opt[0] == '+', opt << " does not start with either '-' or '+'");
|
||||
UASSERT(!(opt[0] == '-' && opt[1] == '-'), "Option must have single '-', but " << opt);
|
||||
const auto insertedResult = m_pimpl->m_options.emplace(opt, std::move(act));
|
||||
UASSERT(insertedResult.second, opt << " is already registered");
|
||||
return *insertedResult.first->second;
|
||||
}
|
||||
|
||||
bool V3OptionParser::hasPrefixNo(const char* strp) {
|
||||
UASSERT(strp[0] == '-', strp << " does not start with '-'");
|
||||
if (strp[1] == '-') ++strp;
|
||||
return std::strncmp(strp, "-no", 3) == 0;
|
||||
}
|
||||
|
||||
int V3OptionParser::parse(int idx, int argc, char* argv[]) {
|
||||
UASSERT(m_pimpl->m_isFinalized, "finalize() must be called before parse()");
|
||||
const char* optp = argv[idx];
|
||||
if (optp[0] == '-' && optp[1] == '-') ++optp;
|
||||
ActionIfs* actp = find(optp);
|
||||
if (!actp) return 0;
|
||||
if (!actp->isValueNeeded()) {
|
||||
actp->exec(optp, nullptr);
|
||||
return 1;
|
||||
} else if (idx + 1 < argc) {
|
||||
actp->exec(optp, argv[idx + 1]);
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
string V3OptionParser::getSuggestion(const char* str) const {
|
||||
return m_pimpl->m_spellCheck.bestCandidateMsg(str);
|
||||
}
|
||||
|
||||
void V3OptionParser::addSuggestionCandidate(const string& s) {
|
||||
m_pimpl->m_spellCheck.pushCandidate(s);
|
||||
}
|
||||
|
||||
void V3OptionParser::finalize() {
|
||||
UASSERT(!m_pimpl->m_isFinalized, "finalize() must not be called twice");
|
||||
for (auto&& opt : m_pimpl->m_options) {
|
||||
if (opt.second->isUndocumented()) continue;
|
||||
m_pimpl->m_spellCheck.pushCandidate(opt.first);
|
||||
if (opt.second->isOnOffAllowed()) m_pimpl->m_spellCheck.pushCandidate("-no" + opt.first);
|
||||
}
|
||||
m_pimpl->m_isFinalized = true;
|
||||
}
|
||||
|
||||
V3OptionParser::V3OptionParser()
|
||||
: m_pimpl{new Impl{}} {}
|
||||
|
||||
V3OptionParser::~V3OptionParser() = default;
|
||||
|
||||
//######################################################################
|
||||
// Member functions of V3OptionParser::AppendHelper
|
||||
|
||||
#define V3OPTION_PARSER_DEF_OP(actKind, argType, actType) \
|
||||
V3OptionParser::ActionIfs& V3OptionParser::AppendHelper::operator()( \
|
||||
const char* optp, actKind, argType arg) const { \
|
||||
return m_parser.add<Impl::actType>(optp, arg); \
|
||||
}
|
||||
V3OPTION_PARSER_DEF_OP(Set, bool*, ActionSet<bool>)
|
||||
#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL
|
||||
V3OPTION_PARSER_DEF_OP(Set, VOptionBool*, ActionSet<VOptionBool>)
|
||||
#endif
|
||||
V3OPTION_PARSER_DEF_OP(Set, int*, ActionSet<int>)
|
||||
V3OPTION_PARSER_DEF_OP(Set, string*, ActionSet<string>)
|
||||
V3OPTION_PARSER_DEF_OP(OnOff, bool*, ActionOnOff<bool>)
|
||||
#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL
|
||||
V3OPTION_PARSER_DEF_OP(OnOff, VOptionBool*, ActionOnOff<VOptionBool>)
|
||||
#endif
|
||||
V3OPTION_PARSER_DEF_OP(CbCall, Impl::ActionCbCall::CbType, ActionCbCall)
|
||||
V3OPTION_PARSER_DEF_OP(CbOnOff, Impl::ActionCbOnOff::CbType, ActionCbOnOff)
|
||||
V3OPTION_PARSER_DEF_OP(CbVal, Impl::ActionCbVal<int>::CbType, ActionCbVal<int>)
|
||||
V3OPTION_PARSER_DEF_OP(CbVal, Impl::ActionCbVal<const char*>::CbType, ActionCbVal<const char*>)
|
||||
#undef V3OPTION_PARSER_DEF_OP
|
||||
|
||||
V3OptionParser::ActionIfs&
|
||||
V3OptionParser::AppendHelper::operator()(const char* optp, CbPartialMatch,
|
||||
Impl::ActionCbPartialMatch::CbType cb) const {
|
||||
const size_t prefixLen = std::strlen(optp);
|
||||
auto wrap = [prefixLen, cb](const char* optp) { cb(optp + prefixLen); };
|
||||
return m_parser.add<Impl::ActionCbPartialMatch>(optp, std::move(wrap));
|
||||
}
|
||||
|
||||
V3OptionParser::ActionIfs&
|
||||
V3OptionParser::AppendHelper::operator()(const char* optp, CbPartialMatchVal,
|
||||
Impl::ActionCbPartialMatchVal::CbType cb) const {
|
||||
const size_t prefixLen = std::strlen(optp);
|
||||
auto wrap
|
||||
= [prefixLen, cb](const char* optp, const char* argp) { cb(optp + prefixLen, argp); };
|
||||
return m_parser.add<Impl::ActionCbPartialMatchVal>(optp, std::move(wrap));
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Command line option parser
|
||||
//
|
||||
// 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_V3OPTION_PARSER_H_
|
||||
#define VERILATOR_V3OPTION_PARSER_H_
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
// Not to include V3Option.h here so that VlcMain or other executables can use this file easily.
|
||||
#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL
|
||||
class VOptionBool;
|
||||
#endif
|
||||
|
||||
// Typycal usage would look as below.
|
||||
// See also V3Options::parseOptsList() in V3Optoins.cpp for more detailed usage.
|
||||
//
|
||||
// V3OptionParser parser;
|
||||
// V3OptionParser::AppendHelper DECL_OPTION{parser};
|
||||
// V3OPTION_PARSER_DECL_TAGS;
|
||||
//
|
||||
// DECL_OPPTION("-help", CbCall, []() { showHelp(); });
|
||||
// DECL_OPPTION("-some_opt", Set, &m_intMember});
|
||||
// parser.finalize();
|
||||
// for (int i = 1; i < argc;) {
|
||||
// if (int consumed = parser.parse(i, argc, argv)) {
|
||||
// i += consumed;
|
||||
// } else { // error
|
||||
// cerr << parser.getSuggestion(argv[i]) << endl;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
|
||||
//######################################################################
|
||||
// V3 OptionParser
|
||||
|
||||
class V3OptionParser final {
|
||||
public:
|
||||
// TYPES
|
||||
class ActionIfs;
|
||||
// Functor to register options to V3OptionParser
|
||||
class AppendHelper;
|
||||
struct Impl;
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
std::unique_ptr<Impl> m_pimpl;
|
||||
|
||||
// METHODS
|
||||
ActionIfs* find(const char* optp);
|
||||
template <class ACT, class ARG> ActionIfs& add(const string& opt, ARG arg);
|
||||
static bool hasPrefixNo(const char* strp); // Returns true if strp starts with "-no"
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
// Returns how many args are consumed. 0 means not match
|
||||
int parse(int idx, int argc, char* argv[]);
|
||||
// Find the most similar option
|
||||
string getSuggestion(const char* str) const;
|
||||
void addSuggestionCandidate(const string& s);
|
||||
// Call this function after all options are registered.
|
||||
void finalize();
|
||||
|
||||
// CONSTRUCTORS
|
||||
V3OptionParser();
|
||||
~V3OptionParser();
|
||||
};
|
||||
|
||||
class V3OptionParser::ActionIfs VL_NOT_FINAL {
|
||||
public:
|
||||
virtual ~ActionIfs() = default;
|
||||
virtual bool isValueNeeded() const = 0; // Need val of "-opt val"
|
||||
virtual bool isOnOffAllowed() const = 0; // true if "-no-opt" is allowd
|
||||
virtual bool isPartialMatchAllowed() const = 0; // true if "-Wno-" matches "-Wno-fatal"
|
||||
virtual bool isUndocumented() const = 0; // Will not be suggested in typo
|
||||
// Set a value or run callback
|
||||
virtual void exec(const char* optp, const char* valp) = 0;
|
||||
// Mark this option undocumented. (Exclude this option from suggestion list).
|
||||
virtual void undocumented() = 0;
|
||||
};
|
||||
|
||||
// A helper class to register options
|
||||
class V3OptionParser::AppendHelper final {
|
||||
public:
|
||||
// TYPES
|
||||
// Tag to specify which operator() to call
|
||||
struct Set {}; // For ActionSet
|
||||
struct OnOff {}; // For ActionOnOff
|
||||
struct CbCall {}; // For ActionCbCall
|
||||
struct CbOnOff {}; // For ActionOnOff
|
||||
struct CbVal {}; // For ActionCbVal
|
||||
struct CbPartialMatch {}; // For ActionCbPartialMatch
|
||||
struct CbPartialMatchVal {}; // For ActionCbPartialMatchVal
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
V3OptionParser& m_parser; // The actual option registory
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
ActionIfs& operator()(const char* optp, Set, bool*) const;
|
||||
#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL
|
||||
ActionIfs& operator()(const char* optp, Set, VOptionBool*) const;
|
||||
#endif
|
||||
ActionIfs& operator()(const char* optp, Set, int*) const;
|
||||
ActionIfs& operator()(const char* optp, Set, string*) const;
|
||||
|
||||
ActionIfs& operator()(const char* optp, OnOff, bool*) const;
|
||||
#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL
|
||||
ActionIfs& operator()(const char* optp, OnOff, VOptionBool*) const;
|
||||
#endif
|
||||
|
||||
ActionIfs& operator()(const char* optp, CbCall, std::function<void(void)>) const;
|
||||
ActionIfs& operator()(const char* optp, CbOnOff, std::function<void(bool)>) const;
|
||||
ActionIfs& operator()(const char* optp, CbVal, std::function<void(int)>) const;
|
||||
ActionIfs& operator()(const char* optp, CbVal, std::function<void(const char*)>) const;
|
||||
|
||||
ActionIfs& operator()(const char* optp, CbPartialMatch,
|
||||
std::function<void(const char*)>) const;
|
||||
ActionIfs& operator()(const char* optp, CbPartialMatchVal,
|
||||
std::function<void(const char*, const char*)>) const;
|
||||
|
||||
// CONSTRUCTORS
|
||||
explicit AppendHelper(V3OptionParser& parser)
|
||||
: m_parser(parser) {}
|
||||
};
|
||||
|
||||
#define V3OPTION_PARSER_DECL_TAGS \
|
||||
const auto Set VL_ATTR_UNUSED = V3OptionParser::AppendHelper::Set{}; \
|
||||
const auto OnOff VL_ATTR_UNUSED = V3OptionParser::AppendHelper::OnOff{}; \
|
||||
const auto CbCall VL_ATTR_UNUSED = V3OptionParser::AppendHelper::CbCall{}; \
|
||||
const auto CbOnOff VL_ATTR_UNUSED = V3OptionParser::AppendHelper::CbOnOff{}; \
|
||||
const auto CbVal VL_ATTR_UNUSED = V3OptionParser::AppendHelper::CbVal{}; \
|
||||
const auto CbPartialMatch VL_ATTR_UNUSED = V3OptionParser::AppendHelper::CbPartialMatch{}; \
|
||||
const auto CbPartialMatchVal VL_ATTR_UNUSED \
|
||||
= V3OptionParser::AppendHelper::CbPartialMatchVal {}
|
||||
|
||||
//######################################################################
|
||||
|
||||
#endif // guard
|
||||
|
|
@ -19,9 +19,9 @@
|
|||
|
||||
#include "V3Global.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3String.h"
|
||||
#include "V3Os.h"
|
||||
#include "V3Options.h"
|
||||
#include "V3OptionParser.h"
|
||||
#include "V3Error.h"
|
||||
#include "V3File.h"
|
||||
#include "V3PreShell.h"
|
||||
|
|
@ -37,7 +37,6 @@
|
|||
#include <cctype>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
|
@ -106,226 +105,6 @@ public:
|
|||
~V3OptionsImp() = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// V3 OptionParser
|
||||
|
||||
class V3OptionsParser final {
|
||||
public:
|
||||
// TYPES
|
||||
class ActionIfs VL_NOT_FINAL {
|
||||
public:
|
||||
virtual ~ActionIfs() = default;
|
||||
virtual bool isValueNeeded() const = 0; // Need val of "-opt val"
|
||||
virtual bool isOnOffAllowed() const = 0; // true if "-no-opt" is allowd
|
||||
virtual bool isPartialMatchAllowed() const = 0; // true if "-Wno-" matches "-Wno-fatal"
|
||||
virtual bool isUndocumented() const = 0; // Will not be suggested in typo
|
||||
// Set a value or run callback
|
||||
virtual void exec(const char* optp, const char* valp) = 0;
|
||||
virtual void undocumented() = 0;
|
||||
};
|
||||
enum class en { // Setting for isOnOffAllowed() and isPartialMatchAllowed()
|
||||
NONE, // "-opt"
|
||||
ONOFF, // "-opt" and "-no-opt"
|
||||
VALUE // "-opt val"
|
||||
};
|
||||
// Base class of actual action classes
|
||||
template <en MODE, bool ALLOW_PARTIAL_MATCH = false>
|
||||
class ActionBase VL_NOT_FINAL : public ActionIfs {
|
||||
bool m_undocumented = false; // This option is not documented
|
||||
public:
|
||||
virtual bool isValueNeeded() const override final { return MODE == en::VALUE; }
|
||||
virtual bool isOnOffAllowed() const override final { return MODE == en::ONOFF; }
|
||||
virtual bool isPartialMatchAllowed() const override final { return ALLOW_PARTIAL_MATCH; }
|
||||
virtual bool isUndocumented() const override { return m_undocumented; }
|
||||
virtual void undocumented() override { m_undocumented = true; }
|
||||
};
|
||||
// Actual action classes
|
||||
template <typename T> class ActionSet; // "-opt" for bool-ish, "-opt val" for int and string
|
||||
template <typename BOOL> class ActionOnOff; // "-opt" and "-no-opt" for bool-ish
|
||||
class ActionCbCall; // Callback without argument for "-opt"
|
||||
class ActionCbOnOff; // Callback for "-opt" and "-no-opt"
|
||||
template <class T> class ActionCbVal; // Callback for "-opt val"
|
||||
class ActionCbPartialMatch; // Callback "-O3" for "-O"
|
||||
class ActionCbPartialMatchVal; // Callback "-debugi-V3Options 3" for "-debugi-"
|
||||
|
||||
// Functor to register options to V3OptionsParser
|
||||
struct AppendHelper;
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
std::map<const string, std::unique_ptr<ActionIfs>> m_options; // All actions for option
|
||||
bool m_isFinalized{false}; // Becomes after finalize() is called
|
||||
VSpellCheck m_spellCheck; // Suggests closest option when not found
|
||||
|
||||
// METHODS
|
||||
ActionIfs* find(const char* optp) {
|
||||
auto it = m_options.find(optp);
|
||||
if (it != m_options.end()) return it->second.get();
|
||||
for (auto&& act : m_options) {
|
||||
if (act.second->isOnOffAllowed()) { // Find starts with "-no"
|
||||
const char* const nop = std::strncmp(optp, "-no", 3) ? nullptr : (optp + 3);
|
||||
if (nop && (act.first == nop || act.first == (string{"-"} + nop))) {
|
||||
return act.second.get();
|
||||
}
|
||||
} else if (act.second->isPartialMatchAllowed()) {
|
||||
if (!std::strncmp(optp, act.first.c_str(), act.first.length())) {
|
||||
return act.second.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
template <class ACT, class ARG> ActionIfs& add(const std::string& opt, ARG arg) {
|
||||
UASSERT(!m_isFinalized, "Cannot add after finalize() is called");
|
||||
std::unique_ptr<ACT> act{new ACT{std::move(arg)}};
|
||||
UASSERT(opt.size() >= 2, opt << " is too short");
|
||||
UASSERT(opt[0] == '-' || opt[0] == '+', opt << " does not start with either '-' or '+'");
|
||||
UASSERT(!(opt[0] == '-' && opt[1] == '-'), "Option must have single '-', but " << opt);
|
||||
const auto insertedResult = m_options.emplace(opt, std::move(act));
|
||||
UASSERT(insertedResult.second, opt << " is already registered");
|
||||
return *insertedResult.first->second;
|
||||
}
|
||||
static bool hasPrefixNo(const char* strp) { // Returns true if strp starts with "-no"
|
||||
UASSERT(strp[0] == '-', strp << " does not start with '-'");
|
||||
if (strp[1] == '-') ++strp;
|
||||
return std::strncmp(strp, "-no", 3) == 0;
|
||||
}
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
// Returns how many args are consumed. 0 means not match
|
||||
int parse(int idx, int argc, char* argv[]) {
|
||||
UASSERT(m_isFinalized, "finalize() must be called before parse()");
|
||||
const char* optp = argv[idx];
|
||||
if (optp[0] == '-' && optp[1] == '-') ++optp;
|
||||
ActionIfs* actp = find(optp);
|
||||
if (!actp) return 0;
|
||||
if (!actp->isValueNeeded()) {
|
||||
actp->exec(optp, nullptr);
|
||||
return 1;
|
||||
} else if (idx + 1 < argc) {
|
||||
actp->exec(optp, argv[idx + 1]);
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
// Find the most similar option
|
||||
string getSuggestion(const char* str) const { return m_spellCheck.bestCandidateMsg(str); }
|
||||
void addSuggestionCandidate(const string& s) { m_spellCheck.pushCandidate(s); }
|
||||
void finalize() {
|
||||
UASSERT(!m_isFinalized, "finalize() must not be called twice");
|
||||
for (auto&& opt : m_options) {
|
||||
if (opt.second->isUndocumented()) continue;
|
||||
m_spellCheck.pushCandidate(opt.first);
|
||||
if (opt.second->isOnOffAllowed()) m_spellCheck.pushCandidate("-no" + opt.first);
|
||||
}
|
||||
m_isFinalized = true;
|
||||
}
|
||||
};
|
||||
|
||||
#define V3OPTIONS_DEF_ACT_CLASS(className, type, body, enType) \
|
||||
template <> class V3OptionsParser::className<type> final : public ActionBase<enType> { \
|
||||
type* m_valp; /* Pointer to a option variable*/ \
|
||||
\
|
||||
public: \
|
||||
explicit className(type* valp) \
|
||||
: m_valp(valp) {} \
|
||||
virtual void exec(const char* optp, const char* argp) override { body; } \
|
||||
}
|
||||
|
||||
V3OPTIONS_DEF_ACT_CLASS(ActionSet, bool, *m_valp = true, en::NONE);
|
||||
V3OPTIONS_DEF_ACT_CLASS(ActionSet, VOptionBool, m_valp->setTrueOrFalse(true), en::NONE);
|
||||
V3OPTIONS_DEF_ACT_CLASS(ActionSet, int, *m_valp = std::atoi(argp), en::VALUE);
|
||||
V3OPTIONS_DEF_ACT_CLASS(ActionSet, string, *m_valp = argp, en::VALUE);
|
||||
|
||||
V3OPTIONS_DEF_ACT_CLASS(ActionOnOff, bool, *m_valp = !hasPrefixNo(optp), en::ONOFF);
|
||||
V3OPTIONS_DEF_ACT_CLASS(ActionOnOff, VOptionBool, m_valp->setTrueOrFalse(!hasPrefixNo(optp)),
|
||||
en::ONOFF);
|
||||
|
||||
#undef V3OPTIONS_DEF_ACT_CLASS
|
||||
|
||||
#define V3OPTIONS_DEF_ACT_CB_CLASS(className, funcType, body, ...) \
|
||||
class V3OptionsParser::className final : public ActionBase<__VA_ARGS__> { \
|
||||
std::function<funcType> m_cb; /* Callback function */ \
|
||||
\
|
||||
public: \
|
||||
using CbType = std::function<funcType>; \
|
||||
explicit className(CbType cb) \
|
||||
: m_cb(std::move(cb)) {} \
|
||||
virtual void exec(const char* optp, const char* argp) override { body; } \
|
||||
}
|
||||
|
||||
V3OPTIONS_DEF_ACT_CB_CLASS(ActionCbCall, void(void), m_cb(), en::NONE);
|
||||
V3OPTIONS_DEF_ACT_CB_CLASS(ActionCbOnOff, void(bool), m_cb(!hasPrefixNo(optp)), en::ONOFF);
|
||||
template <>
|
||||
V3OPTIONS_DEF_ACT_CB_CLASS(ActionCbVal<int>, void(int), m_cb(std::atoi(argp)), en::VALUE);
|
||||
template <>
|
||||
V3OPTIONS_DEF_ACT_CB_CLASS(ActionCbVal<const char*>, void(const char*), m_cb(argp), en::VALUE);
|
||||
V3OPTIONS_DEF_ACT_CB_CLASS(ActionCbPartialMatch, void(const char*), m_cb(optp), en::NONE, true);
|
||||
V3OPTIONS_DEF_ACT_CB_CLASS(ActionCbPartialMatchVal, void(const char*, const char*),
|
||||
m_cb(optp, argp), en::VALUE, true);
|
||||
|
||||
#undef V3OPTIONS_DEF_ACT_CB_CLASS
|
||||
|
||||
// A helper class to register options
|
||||
struct V3OptionsParser::AppendHelper final {
|
||||
V3OptionsParser& m_parser; // The actual option registory
|
||||
|
||||
// TYPES
|
||||
// Tag to specify which operator() to call
|
||||
struct Set {}; // For ActionSet
|
||||
struct OnOff {}; // For ActionOnOff
|
||||
struct CbCall {}; // For ActionCbCall
|
||||
struct CbOnOff {}; // For ActionOnOff
|
||||
struct CbVal {}; // For ActionCbVal
|
||||
struct CbPartialMatch {}; // For ActionCbPartialMatch
|
||||
struct CbPartialMatchVal {}; // For ActionCbPartialMatchVal
|
||||
|
||||
// METHODS
|
||||
|
||||
#define V3OPTIONS_DEF_OP(actKind, argType, actType) \
|
||||
ActionIfs& operator()(const char* optp, actKind, argType arg) const { \
|
||||
return m_parser.add<actType>(optp, arg); \
|
||||
}
|
||||
V3OPTIONS_DEF_OP(Set, bool*, ActionSet<bool>)
|
||||
V3OPTIONS_DEF_OP(Set, VOptionBool*, ActionSet<VOptionBool>)
|
||||
V3OPTIONS_DEF_OP(Set, int*, ActionSet<int>)
|
||||
V3OPTIONS_DEF_OP(Set, string*, ActionSet<string>)
|
||||
V3OPTIONS_DEF_OP(OnOff, bool*, ActionOnOff<bool>)
|
||||
V3OPTIONS_DEF_OP(OnOff, VOptionBool*, ActionOnOff<VOptionBool>)
|
||||
V3OPTIONS_DEF_OP(CbCall, ActionCbCall::CbType, ActionCbCall)
|
||||
V3OPTIONS_DEF_OP(CbOnOff, ActionCbOnOff::CbType, ActionCbOnOff)
|
||||
V3OPTIONS_DEF_OP(CbVal, ActionCbVal<int>::CbType, ActionCbVal<int>)
|
||||
V3OPTIONS_DEF_OP(CbVal, ActionCbVal<const char*>::CbType, ActionCbVal<const char*>)
|
||||
#undef V3OPTIONS_DEF_OP
|
||||
|
||||
// Syntax sugar to register a member function of V3Options directry
|
||||
ActionIfs& operator()(const char* optp, CbVal,
|
||||
std::pair<V3Options*, void (V3Options::*)(int)> arg) const {
|
||||
auto cb = [arg](int v) { (arg.first->*arg.second)(v); };
|
||||
return m_parser.add<ActionCbVal<int>>(optp, cb);
|
||||
}
|
||||
ActionIfs& operator()(const char* optp, CbVal,
|
||||
std::pair<V3Options*, void (V3Options::*)(const string&)> arg) const {
|
||||
auto cb = [arg](const string& v) { (arg.first->*arg.second)(v); };
|
||||
return m_parser.add<ActionCbVal<const char*>>(optp, cb);
|
||||
}
|
||||
// Callback of partial match expects prefix to be removed, so wrap the given callback
|
||||
ActionIfs& operator()(const char* optp, CbPartialMatch,
|
||||
ActionCbPartialMatch::CbType cb) const {
|
||||
const size_t prefixLen = std::strlen(optp);
|
||||
auto wrap = [prefixLen, cb](const char* optp) { cb(optp + prefixLen); };
|
||||
return m_parser.add<ActionCbPartialMatch>(optp, std::move(wrap));
|
||||
}
|
||||
ActionIfs& operator()(const char* optp, CbPartialMatchVal,
|
||||
ActionCbPartialMatchVal::CbType cb) const {
|
||||
const size_t prefixLen = std::strlen(optp);
|
||||
auto wrap
|
||||
= [prefixLen, cb](const char* optp, const char* argp) { cb(optp + prefixLen, argp); };
|
||||
return m_parser.add<ActionCbPartialMatchVal>(optp, std::move(wrap));
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// V3LangCode class functions
|
||||
|
||||
|
|
@ -1008,6 +787,10 @@ void V3Options::notify() {
|
|||
cmdfl->v3warn(E_UNSUPPORTED,
|
||||
"--main not usable with SystemC. Suggest see examples for sc_main().");
|
||||
}
|
||||
|
||||
if (coverage() && savable()) {
|
||||
cmdfl->v3error("--coverage and --savable not supported together");
|
||||
}
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
|
|
@ -1112,20 +895,18 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
addArg(argv[i]); // -f's really should be inserted in the middle, but this is for debug
|
||||
}
|
||||
|
||||
V3OptionsParser parser;
|
||||
const auto Set = V3OptionsParser::AppendHelper::Set{};
|
||||
const auto OnOff = V3OptionsParser::AppendHelper::OnOff{};
|
||||
const auto CbCall = V3OptionsParser::AppendHelper::CbCall{};
|
||||
const auto CbOnOff = V3OptionsParser::AppendHelper::CbOnOff{};
|
||||
const auto CbVal = V3OptionsParser::AppendHelper::CbVal{};
|
||||
const auto CbPartialMatch = V3OptionsParser::AppendHelper::CbPartialMatch{};
|
||||
const auto CbPartialMatchVal = V3OptionsParser::AppendHelper::CbPartialMatchVal{};
|
||||
V3OptionsParser::AppendHelper DECL_OPTION{parser};
|
||||
V3OptionParser parser;
|
||||
V3OptionParser::AppendHelper DECL_OPTION{parser};
|
||||
V3OPTION_PARSER_DECL_TAGS;
|
||||
|
||||
auto callStrSetter = [this](void (V3Options::*cbStr)(const string&)) {
|
||||
return [this, cbStr](const string& v) { (this->*cbStr)(v); };
|
||||
};
|
||||
// Usage
|
||||
// DECL_OPTION("-option", action, pointer_or_lambda);
|
||||
// action: one of Set, OnOff, CbCall, CbOnOff, CbVal, CbPartialMatch, and CbPartialMatchVal
|
||||
// Set : Set value to a variable, pointer_or_lambda must be a pointer to the
|
||||
// variable.
|
||||
// variable.
|
||||
// true is set to bool-ish variable when '-opt' is passed to verilator.
|
||||
// val is set to int and string variable when '-opt val' is passed.
|
||||
// OnOff : Set value to a bool-ish variable, pointer_or_lambda must be a pointer
|
||||
|
|
@ -1138,7 +919,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
// CbVal : Call lambda or function that takes int or const char*.
|
||||
// "-opt val" is passed to verilator, val is passed to the lambda.
|
||||
// If a function to be called is a member of V3Options that only takes
|
||||
// bool/const string&, {this, &V3Options::memberFunc} can be passed
|
||||
// const string&, callStrSetter(&V3Options::memberFunc) can be passed
|
||||
// instead of lambda as a syntax sugar.
|
||||
// CbPartialMatch : Call lambda or function that takes remaining string.
|
||||
// e.g. DECL_OPTION("-opt-", CbPartialMatch, [](const char*optp) { cout <<
|
||||
|
|
@ -1201,14 +982,14 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
DECL_OPTION("-bin", Set, &m_bin);
|
||||
DECL_OPTION("-build", Set, &m_build);
|
||||
|
||||
DECL_OPTION("-CFLAGS", CbVal, {this, &V3Options::addCFlags});
|
||||
DECL_OPTION("-CFLAGS", CbVal, callStrSetter(&V3Options::addCFlags));
|
||||
DECL_OPTION("-cc", CbCall, [this]() {
|
||||
m_outFormatOk = true;
|
||||
m_systemC = false;
|
||||
});
|
||||
DECL_OPTION("-cdc", OnOff, &m_cdc);
|
||||
DECL_OPTION("-clk", CbVal, {this, &V3Options::addClocker});
|
||||
DECL_OPTION("-no-clk", CbVal, {this, &V3Options::addNoClocker});
|
||||
DECL_OPTION("-clk", CbVal, callStrSetter(&V3Options::addClocker));
|
||||
DECL_OPTION("-no-clk", CbVal, callStrSetter(&V3Options::addNoClocker));
|
||||
DECL_OPTION("-comp-limit-blocks", Set, &m_compLimitBlocks).undocumented();
|
||||
DECL_OPTION("-comp-limit-members", Set,
|
||||
&m_compLimitMembers)
|
||||
|
|
@ -1244,7 +1025,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
|
||||
DECL_OPTION("-D", CbPartialMatch, [this](const char* valp) { addDefine(valp, false); });
|
||||
DECL_OPTION("-debug", CbCall, [this]() { setDebugMode(3); });
|
||||
DECL_OPTION("-debugi", CbVal, {this, &V3Options::setDebugMode});
|
||||
DECL_OPTION("-debugi", CbVal, [this](int v) { setDebugMode(v); });
|
||||
DECL_OPTION("-debugi-", CbPartialMatchVal, [this](const char* optp, const char* valp) {
|
||||
setDebugSrcLevel(optp, std::atoi(valp));
|
||||
});
|
||||
|
|
@ -1279,6 +1060,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
DECL_OPTION("-E", Set, &m_preprocOnly);
|
||||
DECL_OPTION("-error-limit", CbVal, static_cast<void (*)(int)>(&V3Error::errorLimit));
|
||||
DECL_OPTION("-exe", OnOff, &m_exe);
|
||||
DECL_OPTION("-expand-limit", CbVal,
|
||||
[this](const char* valp) { m_expandLimit = std::atoi(valp); });
|
||||
|
||||
DECL_OPTION("-F", CbVal, [this, fl, &optdir](const char* valp) {
|
||||
parseOptsFile(fl, parseFileArg(optdir, valp), true);
|
||||
|
|
@ -1320,7 +1103,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
});
|
||||
DECL_OPTION("-inline-mult", Set, &m_inlineMult);
|
||||
|
||||
DECL_OPTION("-LDFLAGS", CbVal, {this, &V3Options::addLdLibs});
|
||||
DECL_OPTION("-LDFLAGS", CbVal, callStrSetter(&V3Options::addLdLibs));
|
||||
auto setLang = [this, fl](const char* valp) {
|
||||
V3LangCode optval = V3LangCode(valp);
|
||||
if (optval.legal()) {
|
||||
|
|
@ -1340,7 +1123,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
DECL_OPTION("-no-l2name", CbCall, [this]() { m_l2Name = ""; }).undocumented(); // Historical
|
||||
DECL_OPTION("-l2name", CbCall, [this]() { m_l2Name = "v"; }).undocumented(); // Historical
|
||||
|
||||
DECL_OPTION("-MAKEFLAGS", CbVal, {this, &V3Options::addMakeFlags});
|
||||
DECL_OPTION("-MAKEFLAGS", CbVal, callStrSetter(&V3Options::addMakeFlags));
|
||||
DECL_OPTION("-MMD", OnOff, &m_makeDepend);
|
||||
DECL_OPTION("-MP", OnOff, &m_makePhony);
|
||||
DECL_OPTION("-Mdir", CbVal, [this](const char* valp) {
|
||||
|
|
@ -1465,6 +1248,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
fl->v3warn(DEPRECATED, "Deprecated --no-relative-cfuncs, unnecessary with C++11.");
|
||||
});
|
||||
DECL_OPTION("-relative-includes", OnOff, &m_relativeIncludes);
|
||||
DECL_OPTION("-reloop-limit", CbVal, [this, fl](const char* valp) {
|
||||
m_reloopLimit = std::atoi(valp);
|
||||
if (m_reloopLimit < 2) { fl->v3error("--reloop-limit must be >= 2: " << valp); }
|
||||
});
|
||||
DECL_OPTION("-report-unoptflat", OnOff, &m_reportUnoptflat);
|
||||
DECL_OPTION("-rr", CbCall, []() {}); // Processed only in bin/verilator shell
|
||||
|
||||
|
|
|
|||
|
|
@ -285,6 +285,7 @@ private:
|
|||
int m_convergeLimit = 100; // main switch: --converge-limit
|
||||
int m_coverageMaxWidth = 256; // main switch: --coverage-max-width
|
||||
int m_dumpTree = 0; // main switch: --dump-tree
|
||||
int m_expandLimit = 64; // main switch: --expand-limit
|
||||
int m_gateStmts = 100; // main switch: --gate-stmts
|
||||
int m_ifDepth = 0; // main switch: --if-depth
|
||||
int m_inlineMult = 2000; // main switch: --inline-mult
|
||||
|
|
@ -295,6 +296,7 @@ private:
|
|||
int m_outputSplitCFuncs = -1; // main switch: --output-split-cfuncs
|
||||
int m_outputSplitCTrace = -1; // main switch: --output-split-ctrace
|
||||
int m_pinsBv = 65; // main switch: --pins-bv
|
||||
int m_reloopLimit = 40; // main switch: --reloop-limit
|
||||
VOptionBool m_skipIdentical; // main switch: --skip-identical
|
||||
int m_threads = 0; // main switch: --threads (0 == --no-threads)
|
||||
int m_threadsMaxMTasks = 0; // main switch: --threads-max-mtasks
|
||||
|
|
@ -481,6 +483,7 @@ public:
|
|||
int coverageMaxWidth() const { return m_coverageMaxWidth; }
|
||||
int dumpTree() const { return m_dumpTree; }
|
||||
bool dumpTreeAddrids() const { return m_dumpTreeAddrids; }
|
||||
int expandLimit() const { return m_expandLimit; }
|
||||
int gateStmts() const { return m_gateStmts; }
|
||||
int ifDepth() const { return m_ifDepth; }
|
||||
int inlineMult() const { return m_inlineMult; }
|
||||
|
|
@ -491,6 +494,7 @@ public:
|
|||
int outputSplitCFuncs() const { return m_outputSplitCFuncs; }
|
||||
int outputSplitCTrace() const { return m_outputSplitCTrace; }
|
||||
int pinsBv() const { return m_pinsBv; }
|
||||
int reloopLimit() const { return m_reloopLimit; }
|
||||
VOptionBool skipIdentical() const { return m_skipIdentical; }
|
||||
int threads() const { return m_threads; }
|
||||
int threadsMaxMTasks() const { return m_threadsMaxMTasks; }
|
||||
|
|
|
|||
|
|
@ -1723,50 +1723,73 @@ void OrderVisitor::processMoveOne(OrderMoveVertex* vertexp, OrderMoveDomScope* d
|
|||
AstActive* OrderVisitor::processMoveOneLogic(const OrderLogicVertex* lvertexp,
|
||||
AstCFunc*& newFuncpr, int& newStmtsr) {
|
||||
AstActive* activep = nullptr;
|
||||
AstScope* scopep = lvertexp->scopep();
|
||||
AstSenTree* domainp = lvertexp->domainp();
|
||||
AstScope* const scopep = lvertexp->scopep();
|
||||
AstSenTree* const domainp = lvertexp->domainp();
|
||||
AstNode* nodep = lvertexp->nodep();
|
||||
AstNodeModule* modp = VN_CAST(scopep->user1p(), NodeModule); // Stashed by visitor func
|
||||
AstNodeModule* const modp = VN_CAST(scopep->user1p(), NodeModule); // Stashed by visitor func
|
||||
UASSERT(modp, "nullptr");
|
||||
if (VN_IS(nodep, SenTree)) {
|
||||
// Just ignore sensitivities, we'll deal with them when we move statements that need them
|
||||
} else { // Normal logic
|
||||
// Make or borrow a CFunc to contain the new statements
|
||||
if (v3Global.opt.profCFuncs()
|
||||
|| (v3Global.opt.outputSplitCFuncs()
|
||||
&& v3Global.opt.outputSplitCFuncs() < newStmtsr)) {
|
||||
// Put every statement into a unique function to ease profiling or reduce function size
|
||||
newFuncpr = nullptr;
|
||||
}
|
||||
if (!newFuncpr && domainp != m_deleteDomainp) {
|
||||
string name = cfuncName(modp, domainp, scopep, nodep);
|
||||
newFuncpr = new AstCFunc(nodep->fileline(), name, scopep);
|
||||
newFuncpr->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
newFuncpr->symProlog(true);
|
||||
newStmtsr = 0;
|
||||
if (domainp->hasInitial() || domainp->hasSettle()) newFuncpr->slow(true);
|
||||
scopep->addActivep(newFuncpr);
|
||||
// Where will we be adding the call?
|
||||
activep = new AstActive(nodep->fileline(), name, domainp);
|
||||
// Add a top call to it
|
||||
AstCCall* callp = new AstCCall(nodep->fileline(), newFuncpr);
|
||||
callp->argTypes("vlSymsp");
|
||||
activep->addStmtsp(callp);
|
||||
UINFO(6, " New " << newFuncpr << endl);
|
||||
// Move the logic into a CFunc
|
||||
nodep->unlinkFrBack();
|
||||
|
||||
// Process procedures per statement (unless profCFuncs), so we can split CFuncs within
|
||||
// procedures. Everything else is handled in one go
|
||||
AstNodeProcedure* const procp = VN_CAST(nodep, NodeProcedure);
|
||||
if (procp && !v3Global.opt.profCFuncs()) {
|
||||
nodep = procp->bodysp();
|
||||
pushDeletep(procp);
|
||||
}
|
||||
|
||||
// Move the logic to the function we're creating
|
||||
nodep->unlinkFrBack();
|
||||
if (domainp == m_deleteDomainp) {
|
||||
UINFO(4, " Ordering deleting pre-settled " << nodep << endl);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
} else {
|
||||
newFuncpr->addStmtsp(nodep);
|
||||
if (v3Global.opt.outputSplitCFuncs()) {
|
||||
// Add in the number of nodes we're adding
|
||||
EmitCBaseCounterVisitor visitor(nodep);
|
||||
newStmtsr += visitor.count();
|
||||
while (nodep) {
|
||||
// Make or borrow a CFunc to contain the new statements
|
||||
if (v3Global.opt.profCFuncs()
|
||||
|| (v3Global.opt.outputSplitCFuncs()
|
||||
&& v3Global.opt.outputSplitCFuncs() < newStmtsr)) {
|
||||
// Put every statement into a unique function to ease profiling or reduce function
|
||||
// size
|
||||
newFuncpr = nullptr;
|
||||
}
|
||||
if (!newFuncpr && domainp != m_deleteDomainp) {
|
||||
const string name = cfuncName(modp, domainp, scopep, nodep);
|
||||
newFuncpr = new AstCFunc(nodep->fileline(), name, scopep);
|
||||
newFuncpr->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
newFuncpr->symProlog(true);
|
||||
newStmtsr = 0;
|
||||
if (domainp->hasInitial() || domainp->hasSettle()) newFuncpr->slow(true);
|
||||
scopep->addActivep(newFuncpr);
|
||||
// Create top call to it
|
||||
AstCCall* const callp = new AstCCall(nodep->fileline(), newFuncpr);
|
||||
callp->argTypes("vlSymsp");
|
||||
// Where will we be adding the call?
|
||||
AstActive* const newActivep = new AstActive(nodep->fileline(), name, domainp);
|
||||
newActivep->addStmtsp(callp);
|
||||
if (!activep) {
|
||||
activep = newActivep;
|
||||
} else {
|
||||
activep->addNext(newActivep);
|
||||
}
|
||||
UINFO(6, " New " << newFuncpr << endl);
|
||||
}
|
||||
|
||||
AstNode* const nextp = nodep->nextp();
|
||||
// When processing statements in a procedure, unlink the current statement
|
||||
if (nodep->backp()) nodep->unlinkFrBack();
|
||||
|
||||
if (domainp == m_deleteDomainp) {
|
||||
UINFO(4, " Ordering deleting pre-settled " << nodep << endl);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
} else {
|
||||
newFuncpr->addStmtsp(nodep);
|
||||
if (v3Global.opt.outputSplitCFuncs()) {
|
||||
// Add in the number of nodes we're adding
|
||||
EmitCBaseCounterVisitor visitor(nodep);
|
||||
newStmtsr += visitor.count();
|
||||
}
|
||||
}
|
||||
|
||||
nodep = nextp;
|
||||
}
|
||||
}
|
||||
return activep;
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@
|
|||
#include "V3Parse.h"
|
||||
#include "V3Width.h"
|
||||
#include "V3Unroll.h"
|
||||
#include "V3Hashed.h"
|
||||
#include "V3Hasher.h"
|
||||
|
||||
#include <deque>
|
||||
#include <map>
|
||||
|
|
@ -314,7 +314,7 @@ class ParamProcessor final {
|
|||
key += "[" + cvtToStr(bdtp->left()) + ":" + cvtToStr(bdtp->right()) + "]";
|
||||
}
|
||||
}
|
||||
V3Hash hash = V3Hashed::uncachedHash(nodep);
|
||||
V3Hash hash = V3Hasher::uncachedHash(nodep);
|
||||
// Force hash collisions -- for testing only
|
||||
if (VL_UNLIKELY(v3Global.opt.debugCollision())) hash = V3Hash();
|
||||
int num;
|
||||
|
|
|
|||
|
|
@ -146,6 +146,10 @@ private:
|
|||
void lexStreamDepthAdd(int delta);
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
// Enum Class for `pragma protect encoding types
|
||||
enum class Enctype : uint8_t { UUENCODE, BASE64, QUOTE_PRINTABLE, RAW, ERR };
|
||||
|
||||
//======================================================================
|
||||
// Class entry for each per-lexer state
|
||||
|
||||
|
|
@ -170,6 +174,9 @@ public: // Used only by V3PreLex.cpp and V3PreProc.cpp
|
|||
bool m_defQuote = false; // Definition value inside quote
|
||||
string m_defValue; // Definition value being built.
|
||||
int m_enterExit = 0; // For VL_LINE, the enter/exit level
|
||||
int m_protLength = 0; // unencoded length for BASE64
|
||||
int m_protBytes = 0; // decoded length for BASE64
|
||||
Enctype m_encType; // encoding type for `pragma protect
|
||||
|
||||
// CONSTRUCTORS
|
||||
V3PreLex(V3PreProcImp* preimpp, FileLine* filelinep)
|
||||
|
|
|
|||
122
src/V3PreLex.l
122
src/V3PreLex.l
|
|
@ -77,6 +77,11 @@ static void appendDefValue(const char* t, size_t l) { LEXP->appendDefValue(t, l)
|
|||
%x ARGMODE
|
||||
%x INCMODE
|
||||
%x PRTMODE
|
||||
%x PRAGMA
|
||||
%x PRAGMAERR
|
||||
%x PRAGMAPRT
|
||||
%x PRAGMAPRTERR
|
||||
%x ENCBASE64
|
||||
|
||||
/* drop: Drop Ctrl-Z - can't pass thru or may EOF the output too soon */
|
||||
|
||||
|
|
@ -112,12 +117,119 @@ bom [\357\273\277]
|
|||
<INITIAL>"`undef" { FL_FWDC; return VP_UNDEF; }
|
||||
<INITIAL>"`undefineall" { FL_FWDC; return VP_UNDEFINEALL; }
|
||||
<INITIAL>"`error" { FL_FWDC; if (!pedantic()) return VP_ERROR; else return VP_DEFREF; }
|
||||
<INITIAL>"`pragma"{wsn}*[^\n\r]* { FL_FWDC;
|
||||
const char* pointp = yytext + strlen("`pragma");
|
||||
while (isspace(*pointp)) ++pointp;
|
||||
if (!*pointp && v3Global.opt.pedantic()) {
|
||||
yyerrorf("`pragma is missing a pragma_expression.");
|
||||
/* We wanted this to be next to `protect But it must be before `pramga */
|
||||
/* we win only because we both match to the end of the line so the length */
|
||||
/* is equal and we are first*/
|
||||
<PRAGMAPRT>"encoding"{wsn}*[^\n\r]* { FL_FWDC;
|
||||
int res;
|
||||
char enctype[16]; // long enough to hold "quote-printable"
|
||||
if (LEXP->m_protBytes > 0) {
|
||||
LEXP->curFilelinep()->v3warn(BADSTDPRAGMA, "multiple `pragma protected encoding sections");
|
||||
}
|
||||
res = sscanf(yytext + strlen("encoding"), " = (enctype = \"%15[A-Za-z0-9]\", line_length = %d, bytes = %d)", &enctype[0], &LEXP->m_protLength, &LEXP->m_protBytes);
|
||||
if (res == 0)
|
||||
LEXP->curFilelinep()->v3warn(BADSTDPRAGMA, "`pragma protected encoding must have an \"enctype\" field");
|
||||
LEXP->m_encType = !VL_STRCASECMP(enctype, "uuencode") ? Enctype::UUENCODE :
|
||||
!VL_STRCASECMP(enctype, "base64") ? Enctype::BASE64 :
|
||||
!VL_STRCASECMP(enctype, "quoted-printable") ? Enctype::QUOTE_PRINTABLE :
|
||||
!VL_STRCASECMP(enctype, "raw") ? Enctype::RAW : Enctype::ERR;
|
||||
if (LEXP->m_encType == Enctype::ERR)
|
||||
LEXP->curFilelinep()->v3warn(BADSTDPRAGMA, "Illegal encoding type for `pragma protected encoding");
|
||||
if (LEXP->m_encType != Enctype::BASE64)
|
||||
LEXP->curFilelinep()->v3warn(E_UNSUPPORTED, "Unsupported: only BASE64 is recognized for `pragma protected encoding");
|
||||
if (res == 3) {
|
||||
if ((LEXP->m_encType == Enctype::BASE64) && (LEXP->m_protLength & 3))
|
||||
LEXP->curFilelinep()->v3warn(BADSTDPRAGMA, "line_length must be multiple of 4 for BASE64");
|
||||
} else {
|
||||
// default values
|
||||
LEXP->m_protBytes = 0;
|
||||
LEXP->m_protLength = 76; // ?? default value not mentioned in IEEE spec
|
||||
}
|
||||
BEGIN(INITIAL);
|
||||
return VP_TEXT;
|
||||
}
|
||||
<PRAGMAPRT>"key_block"{wsn}*[\n\r] {
|
||||
FL_FWDC;
|
||||
linenoInc();
|
||||
BEGIN(ENCBASE64);
|
||||
return VP_TEXT; }
|
||||
<PRAGMAPRT>"data_block"{wsn}*[\n\r] {
|
||||
FL_FWDC;
|
||||
linenoInc();
|
||||
LEXP->curFilelinep()->v3warn(PROTECTED, "A '`pragma protected data_block' encrypted section was detected and will be skipped.");
|
||||
BEGIN(ENCBASE64);
|
||||
return VP_TEXT; }
|
||||
<PRAGMAPRT>("begin_protected"|"end_protected")[\n\r] { FL_FWDC; linenoInc(); BEGIN(INITIAL); return VP_TEXT; }
|
||||
<PRAGMAPRT>"version="[^\n\r]*[\n\r] {
|
||||
FL_FWDC;
|
||||
linenoInc();
|
||||
BEGIN(INITIAL);
|
||||
return VP_TEXT; }
|
||||
<PRAGMAPRT>("encrypt_agent"|"encrypt_agent_info"|"key_keyowner"|"key_keyname"){wsn}*={wsn}*[\"][^\n\r]*[\"][\n\r] {
|
||||
FL_FWDC;
|
||||
linenoInc();
|
||||
BEGIN(INITIAL);
|
||||
return VP_TEXT; }
|
||||
<PRAGMAPRT>("data_method"|"key_method"){wsn}*={wsn}*[\"][^\n\r]*[\"][\n\r] {
|
||||
FL_FWDC;
|
||||
linenoInc();
|
||||
BEGIN(INITIAL);
|
||||
return VP_TEXT; }
|
||||
/* catch-all for unknown '`pragma protect' rules */
|
||||
<PRAGMAPRT>. { yyless(0);
|
||||
BEGIN(PRAGMAPRTERR);
|
||||
return VP_TEXT; }
|
||||
<ENCBASE64>[A-Za-z0-9+/]+[=]{0,2}[\n\r] { FL_FWDC; linenoInc(); FL_BRK;
|
||||
if ((yyourleng()-1) <= size_t(LEXP->m_protLength) && ((yyleng & 3) == 1)) {
|
||||
LEXP->m_protBytes -= (yyleng-1)/4*3;
|
||||
} else {
|
||||
LEXP->curFilelinep()->v3warn(BADSTDPRAGMA, "BASE64 line too long in `pragma protect key_bloock/data_block");
|
||||
}
|
||||
if (yytext[yyleng-3] == '=')
|
||||
LEXP->m_protBytes++;
|
||||
if (yytext[yyleng-2] == '=')
|
||||
LEXP->m_protBytes++;
|
||||
if (LEXP->m_protBytes == 0) {
|
||||
BEGIN(INITIAL);
|
||||
} else if (LEXP->m_protBytes < 0)
|
||||
LEXP->curFilelinep()->v3warn(BADSTDPRAGMA, "BASE64 encoding (too short) in `pragma protect key_bloock/data_block");
|
||||
/*return VP_TEXT;*/ }
|
||||
<ENCBASE64>{wsn}*[\n\r] { FL_FWDC;
|
||||
if (LEXP->m_protBytes != 0)
|
||||
LEXP->curFilelinep()->v3warn(BADSTDPRAGMA, "BASE64 encoding length mismatch in `pragma protect key_bloock/data_block");
|
||||
linenoInc(); BEGIN(INITIAL);
|
||||
return VP_TEXT; }
|
||||
|
||||
/* Catch only empty `pragma lines */
|
||||
<INITIAL>"`pragma"{wsn}*[\n\r] {
|
||||
yyless(yyleng-1); FL_FWDC;
|
||||
if (v3Global.opt.pedantic()) {
|
||||
LEXP->curFilelinep()->v3warn(BADSTDPRAGMA, "`pragma is missing a pragma_expression.");
|
||||
}
|
||||
return VP_TEXT; }
|
||||
|
||||
/* catch all other nonempty `pragma */
|
||||
<INITIAL>"`pragma"{wsn}*[^\n\r] {
|
||||
yyless(yyleng-1); FL_FWDC;
|
||||
if (!v3Global.opt.preprocOnly())
|
||||
BEGIN(PRAGMA);
|
||||
return VP_TEXT; }
|
||||
<PRAGMA>"protect"{wsn}* { FL_FWDC; BEGIN(PRAGMAPRT); return VP_TEXT;}
|
||||
|
||||
/* catch-all for unknown `pragma rules */
|
||||
<PRAGMA>. { yyless(0);
|
||||
BEGIN(PRAGMAERR);
|
||||
return VP_TEXT; }
|
||||
|
||||
/* the catch-all rule only got 1 char, lets get all line */
|
||||
<PRAGMAERR>[^\n\r]* { FL_FWDC;
|
||||
/* Add a warning here for unknown pragmas if desired, at the moment , we don't */
|
||||
/* LEXP->curFilelinep()->v3warn(BADPRAGMA, "Unknown `pragma"); */
|
||||
BEGIN(INITIAL);
|
||||
return VP_TEXT; }
|
||||
<PRAGMAPRTERR>[^\n\r]* { FL_FWDC;
|
||||
LEXP->curFilelinep()->v3warn(BADSTDPRAGMA, "Unknown '`pragma protect' error");
|
||||
BEGIN(INITIAL);
|
||||
return VP_TEXT; }
|
||||
<INITIAL,STRIFY>"`__FILE__" { FL_FWDC;
|
||||
static string rtnfile;
|
||||
|
|
|
|||
|
|
@ -30,17 +30,21 @@
|
|||
#include "V3Global.h"
|
||||
#include "V3Premit.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3DupFinder.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
constexpr int STATIC_CONST_MIN_WIDTH = 256; // Minimum size to extract to static constant
|
||||
|
||||
//######################################################################
|
||||
// Structure for global state
|
||||
|
||||
class PremitAssignVisitor final : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// AstVar::user4() // bool; occurs on LHS of current assignment
|
||||
AstUser4InUse m_inuser4;
|
||||
// AstVar::user3() // bool; occurs on LHS of current assignment
|
||||
AstUser3InUse m_inuser3;
|
||||
|
||||
// STATE
|
||||
bool m_noopt = false; // Disable optimization of variables in this block
|
||||
|
|
@ -50,7 +54,7 @@ private:
|
|||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeAssign* nodep) override {
|
||||
// AstNode::user4ClearTree(); // Implied by AstUser4InUse
|
||||
// AstNode::user3ClearTree(); // Implied by AstUser3InUse
|
||||
// LHS first as fewer varrefs
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
// Now find vars marked as lhs
|
||||
|
|
@ -59,9 +63,9 @@ private:
|
|||
virtual void visit(AstVarRef* nodep) override {
|
||||
// it's LHS var is used so need a deep temporary
|
||||
if (nodep->access().isWriteOrRW()) {
|
||||
nodep->varp()->user4(true);
|
||||
nodep->varp()->user3(true);
|
||||
} else {
|
||||
if (nodep->varp()->user4()) {
|
||||
if (nodep->varp()->user3()) {
|
||||
if (!m_noopt) UINFO(4, "Block has LHS+RHS var: " << nodep << endl);
|
||||
m_noopt = true;
|
||||
}
|
||||
|
|
@ -88,9 +92,11 @@ private:
|
|||
// AstNodeMath::user() -> bool. True if iterated already
|
||||
// AstShiftL::user2() -> bool. True if converted to conditional
|
||||
// AstShiftR::user2() -> bool. True if converted to conditional
|
||||
// AstConst::user2p() -> Replacement static variable pointer
|
||||
// *::user4() -> See PremitAssignVisitor
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
// AstUser4InUse part of V3Hasher via V3DupFinder
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
|
|
@ -100,6 +106,11 @@ private:
|
|||
AstTraceInc* m_inTracep = nullptr; // Inside while loop, special statement additions
|
||||
bool m_assignLhs = false; // Inside assignment lhs, don't breakup extracts
|
||||
|
||||
V3DupFinder m_dupFinder; // Duplicate finder for static constants that can be reused
|
||||
|
||||
VDouble0 m_staticConstantsExtracted; // Statistic tracking
|
||||
VDouble0 m_staticConstantsReused; // Statistic tracking
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
|
|
@ -139,14 +150,6 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
AstVar* getBlockTemp(AstNode* nodep) {
|
||||
string newvarname = (string("__Vtemp") + cvtToStr(m_modp->varNumGetInc()));
|
||||
AstVar* varp
|
||||
= new AstVar(nodep->fileline(), AstVarType::STMTTEMP, newvarname, nodep->dtypep());
|
||||
m_cfuncp->addInitsp(varp);
|
||||
return varp;
|
||||
}
|
||||
|
||||
void insertBeforeStmt(AstNode* newp) {
|
||||
// Insert newp before m_stmtp
|
||||
if (m_inWhilep) {
|
||||
|
|
@ -173,28 +176,65 @@ private:
|
|||
AstNRelinker linker;
|
||||
nodep->unlinkFrBack(&linker);
|
||||
|
||||
AstVar* varp = getBlockTemp(nodep);
|
||||
AstVar* varp = nullptr;
|
||||
|
||||
AstConst* const constp = VN_CAST(nodep, Const);
|
||||
|
||||
const bool useStatic = constp && (constp->width() >= STATIC_CONST_MIN_WIDTH)
|
||||
&& !constp->num().isFourState();
|
||||
if (useStatic) {
|
||||
// Extract as static constant
|
||||
const auto& it = m_dupFinder.findDuplicate(constp);
|
||||
if (it == m_dupFinder.end()) {
|
||||
const string newvarname = string("__Vconst") + cvtToStr(m_modp->varNumGetInc());
|
||||
varp = new AstVar(nodep->fileline(), AstVarType::MODULETEMP, newvarname,
|
||||
nodep->dtypep());
|
||||
varp->isConst(true);
|
||||
varp->isStatic(true);
|
||||
varp->valuep(constp);
|
||||
m_modp->addStmtp(varp);
|
||||
m_dupFinder.insert(constp);
|
||||
nodep->user2p(varp);
|
||||
++m_staticConstantsExtracted;
|
||||
} else {
|
||||
varp = VN_CAST(it->second->user2p(), Var);
|
||||
++m_staticConstantsReused;
|
||||
}
|
||||
} else {
|
||||
// Keep as local temporary
|
||||
const string newvarname = string("__Vtemp") + cvtToStr(m_modp->varNumGetInc());
|
||||
varp
|
||||
= new AstVar(nodep->fileline(), AstVarType::STMTTEMP, newvarname, nodep->dtypep());
|
||||
m_cfuncp->addInitsp(varp);
|
||||
}
|
||||
|
||||
if (noSubst) varp->noSubst(true); // Do not remove varrefs to this in V3Const
|
||||
|
||||
// Replace node tree with reference to var
|
||||
AstVarRef* newp = new AstVarRef(nodep->fileline(), varp, VAccess::READ);
|
||||
linker.relink(newp);
|
||||
// Put assignment before the referencing statement
|
||||
AstAssign* assp = new AstAssign(
|
||||
nodep->fileline(), new AstVarRef(nodep->fileline(), varp, VAccess::WRITE), nodep);
|
||||
insertBeforeStmt(assp);
|
||||
if (debug() > 8) assp->dumpTree(cout, "deepou:");
|
||||
|
||||
if (!useStatic) {
|
||||
// Put assignment before the referencing statement
|
||||
AstAssign* assp = new AstAssign(
|
||||
nodep->fileline(), new AstVarRef(nodep->fileline(), varp, VAccess::WRITE), nodep);
|
||||
insertBeforeStmt(assp);
|
||||
if (debug() > 8) assp->dumpTree(cout, "deepou:");
|
||||
}
|
||||
|
||||
nodep->user1(true); // Don't add another assignment
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
UINFO(4, " MOD " << nodep << endl);
|
||||
VL_RESTORER(m_modp);
|
||||
{
|
||||
m_modp = nodep;
|
||||
m_cfuncp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
UASSERT_OBJ(m_modp == nullptr, nodep, "Nested modules ?");
|
||||
UASSERT_OBJ(m_dupFinder.empty(), nodep, "Statements outside module ?");
|
||||
m_modp = nodep;
|
||||
m_cfuncp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
m_modp = nullptr;
|
||||
m_dupFinder.clear();
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
VL_RESTORER(m_cfuncp);
|
||||
|
|
@ -401,7 +441,11 @@ private:
|
|||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit PremitVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~PremitVisitor() override = default;
|
||||
virtual ~PremitVisitor() {
|
||||
V3Stats::addStat("Optimizations, Prelim static constants extracted",
|
||||
m_staticConstantsExtracted);
|
||||
V3Stats::addStat("Optimizations, Prelim static constants reused", m_staticConstantsReused);
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
#include "V3Global.h"
|
||||
#include "V3String.h"
|
||||
#include "V3ProtectLib.h"
|
||||
#include "V3Hashed.h"
|
||||
#include "V3Hasher.h"
|
||||
#include "V3Task.h"
|
||||
|
||||
#include <list>
|
||||
|
|
@ -87,9 +87,9 @@ private:
|
|||
|
||||
iterateChildren(nodep);
|
||||
|
||||
V3Hash hash = V3Hashed::uncachedHash(m_cfilep);
|
||||
m_hashValuep->addText(fl, cvtToStr(hash.fullValue()) + ";\n");
|
||||
m_cHashValuep->addText(fl, cvtToStr(hash.fullValue()) + "U;\n");
|
||||
V3Hash hash = V3Hasher::uncachedHash(m_cfilep);
|
||||
m_hashValuep->addText(fl, cvtToStr(hash.value()) + ";\n");
|
||||
m_cHashValuep->addText(fl, cvtToStr(hash.value()) + "U;\n");
|
||||
m_foundTop = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
117
src/V3Reloop.cpp
117
src/V3Reloop.cpp
|
|
@ -18,12 +18,12 @@
|
|||
// Each CFunc:
|
||||
// Look for a series of assignments that would look better in a loop:
|
||||
//
|
||||
// ASSIGN(ARRAYREF(var, #), ARRAYREF(var, #))
|
||||
// ASSIGN(ARRAYREF(var, #+1), ARRAYREF(var, #+1))
|
||||
// ASSIGN(ARRAYREF(var, #), ARRAYREF(var, #+C))
|
||||
// ASSIGN(ARRAYREF(var, #+1), ARRAYREF(var, #+1+C))
|
||||
// ->
|
||||
// Create __Vilp local variable
|
||||
// FOR(__Vilp = low; __Vilp <= high; ++__Vlip)
|
||||
// ASSIGN(ARRAYREF(var, __Vilp), ARRAYREF(var, __Vilp))
|
||||
// ASSIGN(ARRAYREF(var, __Vilp), ARRAYREF(var, __Vilp + C))
|
||||
//
|
||||
// Likewise vector assign to the same constant converted to a loop.
|
||||
//
|
||||
|
|
@ -39,8 +39,6 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
constexpr unsigned RELOOP_MIN_ITERS = 40; // Need at least this many loops to do this optimization
|
||||
|
||||
//######################################################################
|
||||
|
||||
class ReloopVisitor final : public AstNVisitor {
|
||||
|
|
@ -61,6 +59,7 @@ private:
|
|||
AstNodeSel* m_mgSelRp = nullptr; // Parent select, nullptr = constant
|
||||
AstNodeVarRef* m_mgVarrefLp = nullptr; // Parent varref
|
||||
AstNodeVarRef* m_mgVarrefRp = nullptr; // Parent varref, nullptr = constant
|
||||
int64_t m_mgOffset = 0; // Index offset
|
||||
AstConst* m_mgConstRp = nullptr; // Parent RHS constant, nullptr = sel
|
||||
uint32_t m_mgIndexLo = 0; // Merge range
|
||||
uint32_t m_mgIndexHi = 0; // Merge range
|
||||
|
|
@ -83,38 +82,50 @@ private:
|
|||
if (!m_mgAssignps.empty()) {
|
||||
uint32_t items = m_mgIndexHi - m_mgIndexLo + 1;
|
||||
UINFO(9, "End merge iter=" << items << " " << m_mgIndexHi << ":" << m_mgIndexLo << " "
|
||||
<< m_mgAssignps[0] << endl);
|
||||
if (items >= RELOOP_MIN_ITERS) {
|
||||
<< m_mgOffset << " " << m_mgAssignps[0] << endl);
|
||||
if (items >= static_cast<uint32_t>(v3Global.opt.reloopLimit())) {
|
||||
UINFO(6, "Reloop merging items=" << items << " " << m_mgIndexHi << ":"
|
||||
<< m_mgIndexLo << " " << m_mgAssignps[0] << endl);
|
||||
<< m_mgIndexLo << " " << m_mgOffset << " "
|
||||
<< m_mgAssignps[0] << endl);
|
||||
++m_statReloops;
|
||||
m_statReItems += items;
|
||||
|
||||
// Transform first assign into for loop body
|
||||
AstNodeAssign* bodyp = m_mgAssignps.front();
|
||||
AstNodeAssign* const bodyp = m_mgAssignps.front();
|
||||
UASSERT_OBJ(bodyp->lhsp() == m_mgSelLp, bodyp, "Corrupt queue/state");
|
||||
FileLine* fl = bodyp->fileline();
|
||||
AstVar* itp = findCreateVarTemp(fl, m_mgCfuncp);
|
||||
FileLine* const fl = bodyp->fileline();
|
||||
AstVar* const itp = findCreateVarTemp(fl, m_mgCfuncp);
|
||||
|
||||
AstNode* initp = new AstAssign(fl, new AstVarRef(fl, itp, VAccess::WRITE),
|
||||
new AstConst(fl, m_mgIndexLo));
|
||||
AstNode* condp = new AstLte(fl, new AstVarRef(fl, itp, VAccess::READ),
|
||||
new AstConst(fl, m_mgIndexHi));
|
||||
AstNode* incp = new AstAssign(
|
||||
if (m_mgOffset > 0) {
|
||||
UASSERT_OBJ(m_mgIndexLo >= m_mgOffset, bodyp,
|
||||
"Reloop iteration starts at negative index");
|
||||
m_mgIndexLo -= m_mgOffset;
|
||||
m_mgIndexHi -= m_mgOffset;
|
||||
}
|
||||
|
||||
AstNode* const initp = new AstAssign(fl, new AstVarRef(fl, itp, VAccess::WRITE),
|
||||
new AstConst(fl, m_mgIndexLo));
|
||||
AstNode* const condp = new AstLte(fl, new AstVarRef(fl, itp, VAccess::READ),
|
||||
new AstConst(fl, m_mgIndexHi));
|
||||
AstNode* const incp = new AstAssign(
|
||||
fl, new AstVarRef(fl, itp, VAccess::WRITE),
|
||||
new AstAdd(fl, new AstConst(fl, 1), new AstVarRef(fl, itp, VAccess::READ)));
|
||||
AstWhile* whilep = new AstWhile(fl, condp, nullptr, incp);
|
||||
AstWhile* const whilep = new AstWhile(fl, condp, nullptr, incp);
|
||||
initp->addNext(whilep);
|
||||
bodyp->replaceWith(initp);
|
||||
whilep->addBodysp(bodyp);
|
||||
|
||||
// Replace constant index with new loop index
|
||||
AstNode* lbitp = m_mgSelLp->bitp();
|
||||
lbitp->replaceWith(new AstVarRef(fl, itp, VAccess::READ));
|
||||
AstNode* const offsetp
|
||||
= m_mgOffset == 0 ? nullptr : new AstConst(fl, std::abs(m_mgOffset));
|
||||
AstNode* const lbitp = m_mgSelLp->bitp();
|
||||
AstNode* const lvrefp = new AstVarRef(fl, itp, VAccess::READ);
|
||||
lbitp->replaceWith(m_mgOffset > 0 ? new AstAdd(fl, lvrefp, offsetp) : lvrefp);
|
||||
VL_DO_DANGLING(lbitp->deleteTree(), lbitp);
|
||||
if (m_mgSelRp) { // else constant and no replace
|
||||
AstNode* rbitp = m_mgSelRp->bitp();
|
||||
rbitp->replaceWith(new AstVarRef(fl, itp, VAccess::READ));
|
||||
AstNode* const rbitp = m_mgSelRp->bitp();
|
||||
AstNode* const rvrefp = new AstVarRef(fl, itp, VAccess::READ);
|
||||
rbitp->replaceWith(m_mgOffset < 0 ? new AstAdd(fl, rvrefp, offsetp) : rvrefp);
|
||||
VL_DO_DANGLING(rbitp->deleteTree(), lbitp);
|
||||
}
|
||||
if (debug() >= 9) initp->dumpTree(cout, "-new: ");
|
||||
|
|
@ -133,6 +144,7 @@ private:
|
|||
m_mgSelRp = nullptr;
|
||||
m_mgVarrefLp = nullptr;
|
||||
m_mgVarrefRp = nullptr;
|
||||
m_mgOffset = 0;
|
||||
m_mgConstRp = nullptr;
|
||||
}
|
||||
}
|
||||
|
|
@ -143,6 +155,7 @@ private:
|
|||
{
|
||||
m_cfuncp = nodep;
|
||||
iterateChildren(nodep);
|
||||
mergeEnd(); // Finish last pending merge, if any
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep) override {
|
||||
|
|
@ -155,7 +168,7 @@ private:
|
|||
return;
|
||||
}
|
||||
// Of a constant index
|
||||
AstConst* lbitp = VN_CAST(lselp->bitp(), Const);
|
||||
AstConst* const lbitp = VN_CAST(lselp->bitp(), Const);
|
||||
if (!lbitp) {
|
||||
mergeEnd();
|
||||
return;
|
||||
|
|
@ -164,53 +177,58 @@ private:
|
|||
mergeEnd();
|
||||
return;
|
||||
}
|
||||
uint32_t index = lbitp->toUInt();
|
||||
const uint32_t lindex = lbitp->toUInt();
|
||||
// Of variable
|
||||
AstNodeVarRef* lvarrefp = VN_CAST(lselp->fromp(), NodeVarRef);
|
||||
AstNodeVarRef* const lvarrefp = VN_CAST(lselp->fromp(), NodeVarRef);
|
||||
if (!lvarrefp) {
|
||||
mergeEnd();
|
||||
return;
|
||||
}
|
||||
|
||||
// RHS is a constant or a select
|
||||
AstConst* rconstp = VN_CAST(nodep->rhsp(), Const);
|
||||
AstNodeSel* rselp = VN_CAST(nodep->rhsp(), NodeSel);
|
||||
AstConst* const rconstp = VN_CAST(nodep->rhsp(), Const);
|
||||
AstNodeSel* const rselp = VN_CAST(nodep->rhsp(), NodeSel);
|
||||
AstNodeVarRef* rvarrefp = nullptr;
|
||||
uint32_t rindex = lindex;
|
||||
if (rconstp) { // Ok
|
||||
} else {
|
||||
if (!rselp) {
|
||||
mergeEnd();
|
||||
return;
|
||||
}
|
||||
AstConst* rbitp = VN_CAST(rselp->bitp(), Const);
|
||||
} else if (rselp) {
|
||||
AstConst* const rbitp = VN_CAST(rselp->bitp(), Const);
|
||||
rvarrefp = VN_CAST(rselp->fromp(), NodeVarRef);
|
||||
if (!rbitp || rbitp->toUInt() != index || !rvarrefp
|
||||
|| lvarrefp->varp() == rvarrefp->varp()) {
|
||||
if (!rbitp || !rvarrefp || lvarrefp->varp() == rvarrefp->varp()) {
|
||||
mergeEnd();
|
||||
return;
|
||||
}
|
||||
rindex = rbitp->toUInt();
|
||||
} else {
|
||||
mergeEnd();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_mgSelLp) { // Old merge
|
||||
if (m_mgCfuncp == m_cfuncp && m_mgNextp == nodep && m_mgSelLp->same(lselp)
|
||||
&& m_mgVarrefLp->same(lvarrefp)
|
||||
&& (m_mgConstRp
|
||||
? (rconstp && m_mgConstRp->same(rconstp))
|
||||
: (rselp && m_mgSelRp->same(rselp) && m_mgVarrefRp->same(rvarrefp)))
|
||||
&& (index == m_mgIndexLo - 1 || index == m_mgIndexHi + 1)) {
|
||||
if (m_mgCfuncp == m_cfuncp // In same function
|
||||
&& m_mgNextp == nodep // Consecutive node
|
||||
&& m_mgVarrefLp->same(lvarrefp) // Same array on left hand side
|
||||
&& (m_mgConstRp // On the right hand side either ...
|
||||
? (rconstp && m_mgConstRp->same(rconstp)) // ... same constant
|
||||
: (rselp && m_mgVarrefRp->same(rvarrefp))) // ... or same array
|
||||
&& (lindex == m_mgIndexLo - 1 || lindex == m_mgIndexHi + 1) // Left index +/- 1
|
||||
&& (m_mgConstRp || lindex == rindex + m_mgOffset) // Same right index offset
|
||||
) {
|
||||
// Sequentially next to last assign; continue merge
|
||||
if (index == m_mgIndexLo - 1) {
|
||||
m_mgIndexLo = index;
|
||||
} else if (index == m_mgIndexHi + 1) {
|
||||
m_mgIndexHi = index;
|
||||
if (lindex == m_mgIndexLo - 1) {
|
||||
m_mgIndexLo = lindex;
|
||||
} else if (lindex == m_mgIndexHi + 1) {
|
||||
m_mgIndexHi = lindex;
|
||||
}
|
||||
UINFO(9, "Continue merge i=" << index << " " << m_mgIndexHi << ":" << m_mgIndexLo
|
||||
UINFO(9, "Continue merge i=" << lindex << " " << m_mgIndexHi << ":" << m_mgIndexLo
|
||||
<< " " << nodep << endl);
|
||||
m_mgAssignps.push_back(nodep);
|
||||
m_mgNextp = nodep->nextp();
|
||||
return;
|
||||
} else {
|
||||
// This assign doesn't merge with previous assign,
|
||||
UINFO(9, "End merge i="
|
||||
<< lindex << " " << m_mgIndexHi << ":" << m_mgIndexLo << " " << nodep
|
||||
<< endl); // This assign doesn't merge with previous assign,
|
||||
// but should start a new merge
|
||||
mergeEnd();
|
||||
}
|
||||
|
|
@ -224,10 +242,11 @@ private:
|
|||
m_mgSelRp = rselp;
|
||||
m_mgVarrefLp = lvarrefp;
|
||||
m_mgVarrefRp = rvarrefp;
|
||||
m_mgOffset = static_cast<int64_t>(lindex) - static_cast<int64_t>(rindex);
|
||||
m_mgConstRp = rconstp;
|
||||
m_mgIndexLo = index;
|
||||
m_mgIndexHi = index;
|
||||
UINFO(9, "Start merge i=" << index << " " << nodep << endl);
|
||||
m_mgIndexLo = lindex;
|
||||
m_mgIndexHi = lindex;
|
||||
UINFO(9, "Start merge i=" << lindex << " o=" << m_mgOffset << nodep << endl);
|
||||
}
|
||||
//--------------------
|
||||
virtual void visit(AstVar*) override {} // Accelerate
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Hashed.h"
|
||||
#include "V3Hasher.h"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
|
|
@ -37,7 +37,7 @@ private:
|
|||
// TYPES
|
||||
struct HashSenTree {
|
||||
size_t operator()(const AstSenTree* kp) const {
|
||||
return V3Hashed::uncachedHash(kp).fullValue();
|
||||
return V3Hasher::uncachedHash(kp).value();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -979,6 +979,17 @@ private:
|
|||
}
|
||||
SimStackNode stackNode(nodep, &tconnects);
|
||||
m_callStack.push_front(&stackNode);
|
||||
// Clear output variable
|
||||
if (auto* const basicp = VN_CAST(funcp->fvarp(), Var)->basicp()) {
|
||||
AstConst cnst(funcp->fvarp()->fileline(), AstConst::WidthedValue(), basicp->widthMin(),
|
||||
0);
|
||||
if (basicp->isZeroInit()) {
|
||||
cnst.num().setAllBits0();
|
||||
} else {
|
||||
cnst.num().setAllBitsX();
|
||||
}
|
||||
newValue(funcp->fvarp(), &cnst);
|
||||
}
|
||||
// Evaluate the function
|
||||
iterate(funcp);
|
||||
m_callStack.pop_front();
|
||||
|
|
|
|||
|
|
@ -342,7 +342,7 @@ private:
|
|||
|
||||
AstVarScope* findDuplicateTable(AstVarScope* vsc1p) {
|
||||
// See if another table we've created is identical, if so use it for both.
|
||||
// (A more 'modern' way would be to instead use V3Hashed::findDuplicate)
|
||||
// (A more 'modern' way would be to instead use V3DupFinder::findDuplicate)
|
||||
AstVar* var1p = vsc1p->varp();
|
||||
for (AstVarScope* vsc2p : m_modTableVscs) {
|
||||
AstVar* var2p = vsc2p->varp();
|
||||
|
|
|
|||
|
|
@ -1157,7 +1157,8 @@ private:
|
|||
cfuncp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
if (cfuncp->name() == "new") {
|
||||
cfuncp->addInitsp(
|
||||
new AstCStmt(nodep->fileline(), "_ctor_var_reset(vlSymsp);\n"));
|
||||
new AstCStmt(nodep->fileline(),
|
||||
VIdProtect::protect("_ctor_var_reset") + "(vlSymsp);\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@
|
|||
#include "V3Trace.h"
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3Graph.h"
|
||||
#include "V3Hashed.h"
|
||||
#include "V3DupFinder.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include <map>
|
||||
|
|
@ -154,8 +154,8 @@ public:
|
|||
class TraceVisitor final : public EmitCBaseVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// V3Hashed
|
||||
// Ast*::user4() // V3Hashed calculation
|
||||
// V3Hasher in V3DupFinder
|
||||
// Ast*::user4() // V3Hasher calculation
|
||||
// Cleared entire netlist
|
||||
// AstCFunc::user1() // V3GraphVertex* for this node
|
||||
// AstTraceDecl::user1() // V3GraphVertex* for this node
|
||||
|
|
@ -165,7 +165,7 @@ private:
|
|||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
AstUser3InUse m_inuser3;
|
||||
// AstUser4InUse In V3Hashed
|
||||
// AstUser4InUse In V3Hasher via V3DupFinder
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_topModp = nullptr; // Module to add variables to
|
||||
|
|
@ -194,7 +194,7 @@ private:
|
|||
void detectDuplicates() {
|
||||
UINFO(9, "Finding duplicates\n");
|
||||
// Note uses user4
|
||||
V3Hashed hashed; // Duplicate code detection
|
||||
V3DupFinder dupFinder; // Duplicate code detection
|
||||
// Hash all of the values the traceIncs need
|
||||
for (const V3GraphVertex* itp = m_graph.verticesBeginp(); itp;
|
||||
itp = itp->verticesNextp()) {
|
||||
|
|
@ -205,13 +205,9 @@ private:
|
|||
UASSERT_OBJ(nodep->valuep()->backp() == nodep, nodep,
|
||||
"Trace duplicate back needs consistency,"
|
||||
" so we can map duplicates back to TRACEINCs");
|
||||
hashed.hash(nodep->valuep());
|
||||
UINFO(8, " Hashed " << std::hex << hashed.nodeHash(nodep->valuep()) << " "
|
||||
<< nodep << endl);
|
||||
|
||||
// Just keep one node in the map and point all duplicates to this node
|
||||
if (hashed.findDuplicate(nodep->valuep()) == hashed.end()) {
|
||||
hashed.hashAndInsert(nodep->valuep());
|
||||
if (dupFinder.findDuplicate(nodep->valuep()) == dupFinder.end()) {
|
||||
dupFinder.insert(nodep->valuep());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -221,10 +217,10 @@ private:
|
|||
if (TraceTraceVertex* const vvertexp = dynamic_cast<TraceTraceVertex*>(itp)) {
|
||||
AstTraceDecl* const nodep = vvertexp->nodep();
|
||||
if (nodep->valuep() && !vvertexp->duplicatep()) {
|
||||
const auto dupit = hashed.findDuplicate(nodep->valuep());
|
||||
if (dupit != hashed.end()) {
|
||||
const auto dupit = dupFinder.findDuplicate(nodep->valuep());
|
||||
if (dupit != dupFinder.end()) {
|
||||
const AstTraceDecl* const dupDeclp
|
||||
= VN_CAST_CONST(hashed.iteratorNodep(dupit)->backp(), TraceDecl);
|
||||
= VN_CAST_CONST(dupit->second->backp(), TraceDecl);
|
||||
UASSERT_OBJ(dupDeclp, nodep, "Trace duplicate of wrong type");
|
||||
TraceTraceVertex* const dupvertexp
|
||||
= dynamic_cast<TraceTraceVertex*>(dupDeclp->user1u().toGraphVertex());
|
||||
|
|
@ -237,7 +233,6 @@ private:
|
|||
}
|
||||
}
|
||||
}
|
||||
hashed.clear();
|
||||
}
|
||||
|
||||
void graphSimplify(bool initial) {
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ private:
|
|||
new AstAssign(fl, prep, new AstVarRef(fl, varp, VAccess::READ)))),
|
||||
nullptr);
|
||||
newp->branchPred(VBranchPred::BP_LIKELY);
|
||||
newp->isBoundsCheck(true);
|
||||
if (debug() >= 9) newp->dumpTree(cout, " _new: ");
|
||||
abovep->addNextStmt(newp, abovep);
|
||||
prep->user2p(newp); // Save so we may LogAnd it next time
|
||||
|
|
|
|||
|
|
@ -492,7 +492,7 @@ private:
|
|||
// signed: Unsigned (11.8.1)
|
||||
// width: LHS + RHS
|
||||
AstNodeDType* vdtypep = m_vup->dtypeNullSkipRefp();
|
||||
userIterateAndNext(vdtypep, WidthVP(SELF, BOTH).p());
|
||||
userIterate(vdtypep, WidthVP(SELF, BOTH).p());
|
||||
if (VN_IS(vdtypep, QueueDType)) {
|
||||
// Queue "element 0" is lhsp, so we need to swap arguments
|
||||
auto* newp = new AstConsQueue(nodep->fileline(), nodep->rhsp()->unlinkFrBack(),
|
||||
|
|
@ -2457,7 +2457,7 @@ private:
|
|||
AstNodeDType* fromDtp = nodep->fromp()->dtypep()->skipRefToEnump();
|
||||
AstBasicDType* basicp = fromDtp ? fromDtp->basicp() : nullptr;
|
||||
UINFO(9, " from dt " << fromDtp << endl);
|
||||
userIterateAndNext(fromDtp, WidthVP(SELF, BOTH).p());
|
||||
userIterate(fromDtp, WidthVP(SELF, BOTH).p());
|
||||
if (AstEnumDType* adtypep = VN_CAST(fromDtp, EnumDType)) {
|
||||
methodCallEnum(nodep, adtypep);
|
||||
} else if (AstAssocArrayDType* adtypep = VN_CAST(fromDtp, AssocArrayDType)) {
|
||||
|
|
@ -3310,7 +3310,9 @@ private:
|
|||
while (const AstConstDType* vdtypep = VN_CAST(dtypep, ConstDType)) {
|
||||
dtypep = vdtypep->subDTypep()->skipRefp();
|
||||
}
|
||||
userIterateAndNext(dtypep, WidthVP(SELF, BOTH).p());
|
||||
|
||||
userIterate(dtypep, WidthVP(SELF, BOTH).p());
|
||||
|
||||
if (auto* vdtypep = VN_CAST(dtypep, NodeUOrStructDType)) {
|
||||
VL_DO_DANGLING(patternUOrStruct(nodep, vdtypep, defaultp), nodep);
|
||||
} else if (auto* vdtypep = VN_CAST(dtypep, NodeArrayDType)) {
|
||||
|
|
@ -3864,27 +3866,6 @@ private:
|
|||
if (nodep->timeunit().isNone()) {
|
||||
nodep->v3fatalSrc("display %t has no time units");
|
||||
}
|
||||
double scale = nodep->timeunit().multiplier()
|
||||
/ v3Global.rootp()->timeprecision().multiplier();
|
||||
if (scale != 1.0) {
|
||||
AstNode* newp;
|
||||
AstNRelinker relinkHandle;
|
||||
argp->unlinkFrBack(&relinkHandle);
|
||||
if (argp->isDouble()) { // Convert it
|
||||
ch = '^';
|
||||
newp = new AstMulD(
|
||||
argp->fileline(),
|
||||
new AstConst(argp->fileline(), AstConst::RealDouble(), scale),
|
||||
argp);
|
||||
} else {
|
||||
newp = new AstMul(argp->fileline(),
|
||||
new AstConst(argp->fileline(),
|
||||
AstConst::Unsized64(),
|
||||
std::llround(scale)),
|
||||
argp);
|
||||
}
|
||||
relinkHandle.relink(newp);
|
||||
}
|
||||
argp = nextp;
|
||||
}
|
||||
break;
|
||||
|
|
@ -6125,6 +6106,7 @@ private:
|
|||
}
|
||||
void userIterateAndNext(AstNode* nodep, WidthVP* vup) {
|
||||
if (!nodep) return;
|
||||
if (nodep->didWidth()) return; // Avoid iterating list we have already iterated
|
||||
{
|
||||
VL_RESTORER(m_vup);
|
||||
m_vup = vup;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@
|
|||
#define V3ERROR_NO_GLOBAL_
|
||||
#include "V3Error.cpp"
|
||||
#include "V3String.cpp"
|
||||
#define V3OPTION_PARSER_NO_VOPTION_BOOL
|
||||
#include "V3OptionParser.cpp"
|
||||
#include "V3Os.cpp"
|
||||
#include "VlcTop.cpp"
|
||||
|
||||
|
|
@ -47,74 +49,43 @@ string VlcOptions::version() {
|
|||
return ver;
|
||||
}
|
||||
|
||||
bool VlcOptions::onoff(const char* sw, const char* arg, bool& flag) {
|
||||
// if sw==arg, then return true (found it), and flag=true
|
||||
// if sw=="-no-arg", then return true (found it), and flag=false
|
||||
// if sw=="-noarg", then return true (found it), and flag=false
|
||||
// else return false
|
||||
if (arg[0] != '-') v3fatalSrc("OnOff switches must have leading dash.");
|
||||
if (0 == strcmp(sw, arg)) {
|
||||
flag = true;
|
||||
return true;
|
||||
} else if (0 == strncmp(sw, "-no", 3) && (0 == strcmp(sw + 3, arg + 1))) {
|
||||
flag = false;
|
||||
return true;
|
||||
} else if (0 == strncmp(sw, "-no-", 4) && (0 == strcmp(sw + 4, arg + 1))) {
|
||||
flag = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void VlcOptions::parseOptsList(int argc, char** argv) {
|
||||
V3OptionParser parser;
|
||||
V3OptionParser::AppendHelper DECL_OPTION{parser};
|
||||
V3OPTION_PARSER_DECL_TAGS;
|
||||
|
||||
DECL_OPTION("-annotate-all", OnOff, &m_annotateAll);
|
||||
DECL_OPTION("-rank", OnOff, &m_rank);
|
||||
DECL_OPTION("-unlink", OnOff, &m_unlink);
|
||||
DECL_OPTION("-annotate-min", Set, &m_annotateMin);
|
||||
DECL_OPTION("-annotate", Set, &m_annotateOut);
|
||||
DECL_OPTION("-debug", CbCall, []() { V3Error::debugDefault(3); });
|
||||
DECL_OPTION("-debugi", CbVal, [](int v) { V3Error::debugDefault(v); });
|
||||
DECL_OPTION("-V", CbCall, []() {
|
||||
showVersion(true);
|
||||
std::exit(0);
|
||||
});
|
||||
DECL_OPTION("-version", CbCall, []() {
|
||||
showVersion(false);
|
||||
std::exit(0);
|
||||
});
|
||||
DECL_OPTION("-write", Set, &m_writeFile);
|
||||
DECL_OPTION("-write-info", Set, &m_writeInfoFile);
|
||||
parser.finalize();
|
||||
|
||||
// Parse parameters
|
||||
// Note argc and argv DO NOT INCLUDE the filename in [0]!!!
|
||||
// May be called recursively when there are -f files.
|
||||
for (int i = 0; i < argc;) {
|
||||
UINFO(9, " Option: " << argv[i] << endl);
|
||||
if (argv[i][0] == '-') {
|
||||
const char* sw = argv[i];
|
||||
bool flag = true;
|
||||
// Allow gnu -- switches
|
||||
if (sw[0] == '-' && sw[1] == '-') ++sw;
|
||||
// Single switches
|
||||
if (onoff(sw, "-annotate-all", flag /*ref*/)) {
|
||||
m_annotateAll = flag;
|
||||
} else if (onoff(sw, "-rank", flag /*ref*/)) {
|
||||
m_rank = flag;
|
||||
} else if (onoff(sw, "-unlink", flag /*ref*/)) {
|
||||
m_unlink = flag;
|
||||
}
|
||||
// Parameterized switches
|
||||
else if (!strcmp(sw, "-annotate-min") && (i + 1) < argc) {
|
||||
++i;
|
||||
m_annotateMin = atoi(argv[i]);
|
||||
} else if (!strcmp(sw, "-annotate") && (i + 1) < argc) {
|
||||
++i;
|
||||
m_annotateOut = argv[i];
|
||||
} else if (!strcmp(sw, "-debug")) {
|
||||
V3Error::debugDefault(3);
|
||||
} else if (!strcmp(sw, "-debugi") && (i + 1) < argc) {
|
||||
++i;
|
||||
V3Error::debugDefault(atoi(argv[i]));
|
||||
} else if (!strcmp(sw, "-V")) {
|
||||
showVersion(true);
|
||||
std::exit(0);
|
||||
} else if (!strcmp(sw, "-version")) {
|
||||
showVersion(false);
|
||||
std::exit(0);
|
||||
} else if (!strcmp(sw, "-write") && (i + 1) < argc) {
|
||||
++i;
|
||||
m_writeFile = argv[i];
|
||||
} else if (!strcmp(sw, "-write-info") && (i + 1) < argc) {
|
||||
++i;
|
||||
m_writeInfoFile = argv[i];
|
||||
if (int consumed = parser.parse(i, argc, argv)) {
|
||||
i += consumed;
|
||||
} else {
|
||||
v3fatal("Invalid option: " << argv[i]);
|
||||
v3fatal("Invalid option: " << argv[i] << parser.getSuggestion(argv[i]));
|
||||
++i;
|
||||
}
|
||||
++i;
|
||||
} // - options
|
||||
else {
|
||||
} else {
|
||||
addReadFile(argv[i]);
|
||||
++i;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ class VlcOptions final {
|
|||
private:
|
||||
// METHODS
|
||||
static void showVersion(bool verbose);
|
||||
static bool onoff(const char* sw, const char* arg, bool& flag);
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
|
|
|
|||
28
src/astgen
28
src/astgen
|
|
@ -621,7 +621,7 @@ def write_types(filename):
|
|||
fh.write(" }\n")
|
||||
|
||||
|
||||
def write_header(filename):
|
||||
def write_macros(filename):
|
||||
with open_file(filename) as fh:
|
||||
typen = "None"
|
||||
base = "None"
|
||||
|
|
@ -629,10 +629,7 @@ def write_header(filename):
|
|||
in_filename = "V3AstNodes.h"
|
||||
ifile = Args.I + "/" + in_filename
|
||||
with open(ifile) as ifh:
|
||||
|
||||
fh.write("#line 1 \"../" + in_filename + "\"\n")
|
||||
|
||||
for line in ifh:
|
||||
for (lineno, line) in enumerate(ifh, 1):
|
||||
# Drop expanded macro definitions - but keep empty line so compiler
|
||||
# message locations are accurate
|
||||
line = re.sub(r'^\s*#(define|undef)\s+ASTGEN_.*$', '', line)
|
||||
|
|
@ -644,13 +641,20 @@ def write_header(filename):
|
|||
if match:
|
||||
typen = match.group(1)
|
||||
base = match.group(4)
|
||||
if not typen.startswith("Node"):
|
||||
macro = "#define ASTGEN_SUPER_{t}(...) {b}(AstType::at{t}, __VA_ARGS__)\n" \
|
||||
.format(b=base, t=typen)
|
||||
fh.write(macro)
|
||||
|
||||
# Substitute macros
|
||||
line = re.sub(r'\bASTGEN_SUPER\s*\(',
|
||||
base + "(AstType::at" + typen + ", ", line)
|
||||
|
||||
# Emit the line
|
||||
fh.write(line)
|
||||
match = re.search(r"ASTGEN_SUPER_(\w+)", line)
|
||||
if match:
|
||||
if typen != match.group(1):
|
||||
print((
|
||||
"V3AstNodes.h:{l} ERROR: class Ast{t} calls wrong superclass "
|
||||
+
|
||||
"constructor macro (should call ASTGEN_SUPER_{t})"
|
||||
).format(l=lineno, t=typen))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
######################################################################
|
||||
|
|
@ -708,7 +712,7 @@ if Args.classes:
|
|||
write_visitor("V3Ast__gen_visitor.h")
|
||||
write_impl("V3Ast__gen_impl.h")
|
||||
write_types("V3Ast__gen_types.h")
|
||||
write_header("V3AstNodes__gen.h")
|
||||
write_macros("V3AstNodes__gen_macros.h")
|
||||
|
||||
for cpt in Args.infiles:
|
||||
if not re.search(r'.cpp$', cpt):
|
||||
|
|
|
|||
|
|
@ -52,9 +52,10 @@ string(REGEX REPLACE "(^|;)--" "\\1-"
|
|||
|
||||
getarg(TEST_VERILATOR_ARGS_NORM "-prefix" TEST_PREFIX)
|
||||
getarg(TEST_VERILATOR_ARGS_NORM "-threads" TEST_THREADS)
|
||||
getarg(TEST_VERILATOR_ARGS_NORM "-trace-threads" TEST_TRACE_THREADS)
|
||||
|
||||
# Strip unwanted args with 1 parameter
|
||||
string(REGEX REPLACE "(^|;)--?(Mdir|make|prefix|threads);[^;]*" ""
|
||||
string(REGEX REPLACE "(^|;)--?(Mdir|make|prefix|threads|trace-threads);[^;]*" ""
|
||||
TEST_VERILATOR_ARGS
|
||||
"${TEST_VERILATOR_ARGS}")
|
||||
# Strip unwanted args
|
||||
|
|
@ -81,6 +82,9 @@ endif()
|
|||
if(TEST_THREADS)
|
||||
list(APPEND verilate_ARGS THREADS ${TEST_THREADS})
|
||||
endif()
|
||||
if(TEST_TRACE_THREADS)
|
||||
list(APPEND verilate_ARGS TRACE_THREADS ${TEST_TRACE_THREADS})
|
||||
endif()
|
||||
if(TEST_SYSTEMC)
|
||||
list(APPEND verilate_ARGS SYSTEMC)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -908,7 +908,7 @@ sub compile_vlt_flags {
|
|||
unshift @verilator_flags, "--trace-threads 2" if $param{vltmt} && $checkflags =~ /-trace-fst /;
|
||||
unshift @verilator_flags, "--debug-partition" if $param{vltmt};
|
||||
unshift @verilator_flags, "-CFLAGS -ggdb -LDFLAGS -ggdb" if $opt_gdbsim;
|
||||
unshift @verilator_flags, "-CFLAGS -fsanitize=address -LDFLAGS -fsanitize=address" if $param{sanitize};
|
||||
unshift @verilator_flags, "-CFLAGS -fsanitize=address,undefined -LDFLAGS -fsanitize=address,undefined" if $param{sanitize};
|
||||
unshift @verilator_flags, "--make gmake" if $param{verilator_make_gmake};
|
||||
unshift @verilator_flags, "--make cmake" if $param{verilator_make_cmake};
|
||||
unshift @verilator_flags, "--exe" if
|
||||
|
|
@ -2304,6 +2304,10 @@ sub cfg_with_threaded {
|
|||
return 1; # C++11 now always required
|
||||
}
|
||||
|
||||
sub cfg_with_ccache {
|
||||
return `grep "OBJCACHE \?= ccache" "$ENV{VERILATOR_ROOT}/include/verilated.mk"` ne "";
|
||||
}
|
||||
|
||||
sub tries {
|
||||
# Number of retries when reading logfiles, generally only need many
|
||||
# retries when system is busy running a lot of tests
|
||||
|
|
@ -2449,10 +2453,12 @@ sub _lineno_match {
|
|||
my $lineno = shift;
|
||||
my $lines = shift;
|
||||
return 1 if !defined $lines;
|
||||
if ($lines =~ /^(\d+)$/) {
|
||||
return $1 == $lineno;
|
||||
} elsif ($lines =~ /^(\d+)-(\d+)$/) {
|
||||
return $1 <= $lineno && $2 >= $lineno;
|
||||
foreach my $lc (split /,/, $lines) {
|
||||
if ($lc =~ /^(\d+)$/) {
|
||||
return 1 if $1 == $lineno;
|
||||
} elsif ($lc =~ /^(\d+)-(\d+)$/) {
|
||||
return 1 if $1 <= $lineno && $2 >= $lineno;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,15 @@ static const bool verbose = false;
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
#define TEST_CHECK_Z(got) \
|
||||
do { \
|
||||
if ((got)) { \
|
||||
std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << std::hex \
|
||||
<< ": GOT!= NULL EXP=NULL" << std::endl; \
|
||||
++errors; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TEST_CHECK_NZ(got) \
|
||||
do { \
|
||||
if (!(got)) { \
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2019 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
|
||||
|
||||
scenarios(simulator => 1);
|
||||
|
||||
compile(
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2019 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
||||
`define checks(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
||||
`define checkg(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%g' exp='%g'\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
|
||||
integer cyc=0;
|
||||
|
||||
// associative array of an associative array
|
||||
logic [31:0] a [logic [31:0]][logic [63:0]];
|
||||
|
||||
always @ (posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
if (cyc == 1) begin
|
||||
a[5][8] = 8;
|
||||
a[5][9] = 9;
|
||||
end
|
||||
else if (cyc == 2) begin
|
||||
`checkh(a[5][8], 8);
|
||||
`checkh(a[5][9], 9);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 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
|
||||
|
||||
scenarios(vlt => 1);
|
||||
|
||||
if (!$Self->cfg_with_ccache) {
|
||||
skip("Requires configuring with ccache");
|
||||
}
|
||||
|
||||
top_filename("t_a1_first_cc.v");
|
||||
|
||||
# This test requires rebuilding the object files to check the ccache log
|
||||
foreach my $filename (glob ("$Self->{obj_dir}/*.o")) {
|
||||
print "rm $filename\n" if $Self->{verbose};
|
||||
unlink $filename;
|
||||
}
|
||||
|
||||
compile(
|
||||
verilator_flags2 => ['--trace'],
|
||||
make_flags => "ccache-report"
|
||||
);
|
||||
|
||||
my $report = "$Self->{obj_dir}/$Self->{VM_PREFIX}__ccache_report.txt";
|
||||
|
||||
# We do not actually want to make this test depend on whether the file was
|
||||
# cached or not, so trim the report to ignore actual caching behaviour
|
||||
run(cmd => ["sed", "-i", "-e", "'s/ : .*/ : IGNORED/; /|/s/.*/IGNORED/;'", $report]);
|
||||
files_identical($report, "t/$Self->{name}__ccache_report_initial.out");
|
||||
|
||||
# Now rebuild again (should be all up to date)
|
||||
run(
|
||||
logfile => "$Self->{obj_dir}/rebuild.log",
|
||||
cmd => ["make", "-C", $Self->{obj_dir},
|
||||
"-f", "$Self->{VM_PREFIX}.mk",
|
||||
$Self->{VM_PREFIX}, "ccache-report"]
|
||||
);
|
||||
|
||||
files_identical($report, "t/$Self->{name}__ccache_report_rebuild.out");
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
################################################################################
|
||||
ccache report (from verilator_ccache_report) :
|
||||
|
||||
Compiled object files:
|
||||
Vt_ccache_report__ALL.o : IGNORED
|
||||
|
||||
Summary:
|
||||
IGNORED
|
||||
|
||||
Longest:
|
||||
IGNORED
|
||||
################################################################################
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
################################################################################
|
||||
ccache report (from verilator_ccache_report) :
|
||||
|
||||
All object files up to date
|
||||
################################################################################
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 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
|
||||
|
||||
scenarios(vlt_all => 1);
|
||||
|
||||
# This test makes randomly named .cpp/.h files, which tend to collect, so remove them first
|
||||
foreach my $filename (glob ("$Self->{obj_dir}/*_PS*.cpp"
|
||||
." $Self->{obj_dir}/*_PS*.h"
|
||||
." $Self->{obj_dir}/*.d" )) {
|
||||
print "rm $filename\n" if $Self->{verbose};
|
||||
unlink $filename;
|
||||
}
|
||||
|
||||
top_filename("t/t_class_extends.v");
|
||||
|
||||
compile(
|
||||
make_flags => 'VM_PARALLEL_BUILDS=1', # bug2775
|
||||
verilator_flags2 => ["--protect-ids",
|
||||
"--protect-key SECRET_KEY"]
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 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
|
||||
|
||||
scenarios(vlt_all => 1);
|
||||
|
||||
# This test makes randomly named .cpp/.h files, which tend to collect, so remove them first
|
||||
foreach my $filename (glob ("$Self->{obj_dir}/*_PS*.cpp"
|
||||
." $Self->{obj_dir}/*_PS*.h"
|
||||
." $Self->{obj_dir}/*.d" )) {
|
||||
print "rm $filename\n" if $Self->{verbose};
|
||||
unlink $filename;
|
||||
}
|
||||
|
||||
top_filename("t/t_class_local.v");
|
||||
|
||||
compile(
|
||||
verilator_flags2 => ["--protect-ids",
|
||||
"--protect-key SECRET_KEY"]
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 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
|
||||
|
||||
scenarios(vlt_all => 1);
|
||||
|
||||
# This test makes randomly named .cpp/.h files, which tend to collect, so remove them first
|
||||
foreach my $filename (glob ("$Self->{obj_dir}/*_PS*.cpp"
|
||||
." $Self->{obj_dir}/*_PS*.h"
|
||||
." $Self->{obj_dir}/*.d" )) {
|
||||
print "rm $filename\n" if $Self->{verbose};
|
||||
unlink $filename;
|
||||
}
|
||||
|
||||
top_filename("t/t_class_static_method.v");
|
||||
|
||||
compile(
|
||||
verilator_flags2 => ["--protect-ids",
|
||||
"--protect-key SECRET_KEY"]
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 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
|
||||
|
||||
scenarios(vlt_all => 1);
|
||||
|
||||
# This test makes randomly named .cpp/.h files, which tend to collect, so remove them first
|
||||
foreach my $filename (glob ("$Self->{obj_dir}/*_PS*.cpp"
|
||||
." $Self->{obj_dir}/*_PS*.h"
|
||||
." $Self->{obj_dir}/*.d" )) {
|
||||
print "rm $filename\n" if $Self->{verbose};
|
||||
unlink $filename;
|
||||
}
|
||||
|
||||
top_filename("t/t_class_virtual.v");
|
||||
|
||||
compile(
|
||||
verilator_flags2 => ["--protect-ids",
|
||||
"--protect-key SECRET_KEY"]
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue