mirror of
https://github.com/verilator/verilator.git
synced 2026-10-04 17:13:40 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35fe205fd6 | ||
|
|
70cf2582d6 | ||
|
|
ec620387af | ||
|
|
e1e4bde125 | ||
|
|
b947391662 | ||
|
|
edc0f442e4 | ||
|
|
562b9551bf | ||
|
|
f74af949da | ||
|
|
81e806e895 | ||
|
|
7d4958264a | ||
|
|
88b87429dc | ||
|
|
429f463032 | ||
|
|
af4a39603c | ||
|
|
a13d230d8c | ||
|
|
b7bd5956ea | ||
|
|
1a36e05cc6 | ||
|
|
71265a8ec9 | ||
|
|
3a720204c2 | ||
|
|
a95f58749f | ||
|
|
9e58ede480 | ||
|
|
cf2ac30f66 | ||
|
|
68b22a4a3d | ||
|
|
875dc9a27e | ||
|
|
1332446f2a | ||
|
|
6b798830c9 | ||
|
|
ffa23b59b8 | ||
|
|
7d27461054 | ||
|
|
b3f318d8ab | ||
|
|
4a14788c9b | ||
|
|
5560da2934 | ||
|
|
43ce048f9e | ||
|
|
b045111a67 | ||
|
|
654571bd1c | ||
|
|
a3fd105acb | ||
|
|
97561bf064 | ||
|
|
a4820fc700 | ||
|
|
ee469eedaf | ||
|
|
a49d819437 | ||
|
|
cbb9288cb1 | ||
|
|
173efbc829 | ||
|
|
0fef3b02ec | ||
|
|
d1ee6689c4 | ||
|
|
b3eab9d78a | ||
|
|
defd05bdd7 | ||
|
|
a7e5cccf33 | ||
|
|
0f857ce423 | ||
|
|
83cf1882de | ||
|
|
e17543d6d8 | ||
|
|
dacf45fea9 | ||
|
|
13a20c5ee9 | ||
|
|
58dfe9d071 | ||
|
|
6c5cc885a6 | ||
|
|
2c44ab61ac | ||
|
|
0847ea57ae | ||
|
|
9af84c71f5 | ||
|
|
4ce77d3e68 | ||
|
|
3c67791624 | ||
|
|
a281c00752 | ||
|
|
2ca418288d | ||
|
|
bea34d0ced | ||
|
|
a5f2ce0dad | ||
|
|
07ae7146e3 | ||
|
|
8548ecfdac | ||
|
|
7e54ff1b37 | ||
|
|
a4b9745e6e | ||
|
|
8612ba2b8d | ||
|
|
fea6e047e2 | ||
|
|
01ef7122e9 | ||
|
|
ac43e7322b | ||
|
|
02a3f2089f | ||
|
|
33ad834106 | ||
|
|
efe739c52f | ||
|
|
5cd531a5e0 | ||
|
|
0e8dfdbb71 | ||
|
|
ec32a9b976 | ||
|
|
1aae6df669 | ||
|
|
f7641d2ecc | ||
|
|
5f27c41ee3 | ||
|
|
51df77099a | ||
|
|
b8b7e5c978 | ||
|
|
dddcfeb482 | ||
|
|
8d63f83177 | ||
|
|
d43e9aa2c5 | ||
|
|
a2c1b7f827 | ||
|
|
2fff92fe78 | ||
|
|
ba9af4aabf | ||
|
|
ef02678305 | ||
|
|
6990db70fa | ||
|
|
af5616c077 | ||
|
|
009f053d6e | ||
|
|
6903c52ef7 | ||
|
|
aa65dfb1bc | ||
|
|
84a2cd0ec7 | ||
|
|
8e7559c6c9 | ||
|
|
951521320a | ||
|
|
abc0eb92a0 | ||
|
|
c514bb26e8 | ||
|
|
5cb6474cc6 | ||
|
|
b8eb6368e9 | ||
|
|
6239deb1df | ||
|
|
89c2a6070a |
+1
-1
@@ -2,7 +2,7 @@ Checks: 'clang-diagnostic-*,clang-analyzer-*,*,-modernize*,-hicpp*,-and
|
||||
WarningsAsErrors: ''
|
||||
HeaderFilterRegex: ''
|
||||
FormatStyle: none
|
||||
CheckOptions:
|
||||
CheckOptions:
|
||||
- key: google-readability-braces-around-statements.ShortStatementLines
|
||||
value: '2'
|
||||
- key: google-readability-namespace-comments.SpacesBeforeComments
|
||||
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
# DESCRIPTION: Travis-CI config
|
||||
#
|
||||
# Copyright 2003-2019 by Todd Strader. Verilator 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.
|
||||
|
||||
language: cpp
|
||||
|
||||
matrix:
|
||||
include:
|
||||
# Non-cron build will just run on whatever linux flavor we get
|
||||
- if: type != cron
|
||||
os: linux
|
||||
compiler: gcc
|
||||
env:
|
||||
- SCENARIOS=--dist
|
||||
- if: type != cron
|
||||
os: linux
|
||||
compiler: gcc
|
||||
env:
|
||||
- SCENARIOS=--vlt
|
||||
- if: type != cron
|
||||
os: linux
|
||||
compiler: gcc
|
||||
env:
|
||||
- SCENARIOS=--vltmt
|
||||
# Cron builds try different OS/compiler combinations
|
||||
- if: type = cron
|
||||
dist: xenial
|
||||
compiler: gcc
|
||||
env:
|
||||
- SCENARIOS=--dist
|
||||
- if: type = cron
|
||||
dist: xenial
|
||||
compiler: gcc
|
||||
env:
|
||||
- SCENARIOS=--vlt
|
||||
- if: type = cron
|
||||
dist: xenial
|
||||
compiler: gcc
|
||||
env:
|
||||
- SCENARIOS=--vltmt
|
||||
- if: type = cron
|
||||
dist: xenial
|
||||
compiler: clang
|
||||
env:
|
||||
- SCENARIOS=--dist
|
||||
- if: type = cron
|
||||
dist: xenial
|
||||
compiler: clang
|
||||
env:
|
||||
- SCENARIOS=--vlt
|
||||
- if: type = cron
|
||||
dist: xenial
|
||||
compiler: clang
|
||||
env:
|
||||
- SCENARIOS=--vltmt
|
||||
# - if: type = cron
|
||||
# os: osx
|
||||
# compiler: gcc
|
||||
# env:
|
||||
# - SCENARIOS=--dist
|
||||
# - if: type = cron
|
||||
# os: osx
|
||||
# compiler: gcc
|
||||
# env:
|
||||
# - SCENARIOS=--vlt
|
||||
# - if: type = cron
|
||||
# os: osx
|
||||
# compiler: gcc
|
||||
# env:
|
||||
# - SCENARIOS=--vltmt
|
||||
- if: type = cron
|
||||
dist: trusty
|
||||
compiler: gcc
|
||||
env:
|
||||
- SCENARIOS=--dist
|
||||
- if: type = cron
|
||||
dist: trusty
|
||||
compiler: gcc
|
||||
env:
|
||||
- SCENARIOS=--vlt
|
||||
- if: type = cron
|
||||
dist: trusty
|
||||
compiler: gcc
|
||||
env:
|
||||
- SCENARIOS=--vltmt
|
||||
|
||||
cache: ccache
|
||||
|
||||
# Some tests require gdb
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gdb
|
||||
|
||||
before_install:
|
||||
# Perl modules needed for testing
|
||||
- yes yes | sudo cpan -fi Unix::Processors Parallel::Forker Bit::Vector
|
||||
before_script:
|
||||
- export VLT_JOBS=$((`nproc` + 1))
|
||||
- export OBJCACHE=ccache
|
||||
- export VERILATOR_ROOT=`pwd`
|
||||
- autoconf
|
||||
script:
|
||||
- >
|
||||
./configure --enable-maintainer-mode --enable-longtests &&
|
||||
make -j $VLT_JOBS &&
|
||||
make test_regress DRIVER_FLAGS="-j 0 --quiet --rerun"
|
||||
after_script:
|
||||
- ccache -s
|
||||
@@ -2,7 +2,36 @@ Revision history for Verilator
|
||||
|
||||
The contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
* Verilator 4.016 2016-06-16
|
||||
* Verilator 4.018 2019-08-29
|
||||
|
||||
** When showing an error, show source code and offer suggestions of replacements.
|
||||
|
||||
** When showing an error, show the instance location, bug1305. [Todd Strader]
|
||||
|
||||
*** Add --rr, bug1481. [Todd Strader]
|
||||
|
||||
*** Change MULTITOP to warning to help linting, see manual.
|
||||
|
||||
*** Add XSim support to driver.pl, bug1493. [Todd Strader]
|
||||
|
||||
**** Show included-from filenames in warnings, bug1439. [Todd Strader]
|
||||
|
||||
**** Fix elaboration time errors, bug1429. [Udi Finkelstein]
|
||||
|
||||
**** Fix not reporting some duplicate signals/ports, bug1462. [Peter Gerst]
|
||||
|
||||
**** Fix not in array context on non-power-of-two slices, msg2946. [Yu Sheng Lin]
|
||||
|
||||
**** Fix system compile flags injection. [Gianfranco Costamagna]
|
||||
|
||||
**** Fix enum values not being sized based on parent, bug1442. [Dan Petrisko]
|
||||
|
||||
**** Fix internal error on gate optimization of assign, bug1475. [Oyvind Harboe]
|
||||
|
||||
**** Add --dpi-hdr-only, bug1491. [Todd Strader]
|
||||
|
||||
|
||||
* Verilator 4.016 2019-06-16
|
||||
|
||||
*** Add --quiet-exit, bug1436. [Todd Strader]
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
.*\.key
|
||||
.*\.vcd
|
||||
.*\.1
|
||||
\.travis\.yml
|
||||
/obj_dir/
|
||||
/obj_dbg/
|
||||
/obj_nc/
|
||||
|
||||
+10
-28
@@ -83,7 +83,7 @@ infodir = @infodir@
|
||||
pkgdatadir = @pkgdatadir@
|
||||
|
||||
# Directory in which to install pkgconfig file
|
||||
# Generall ${prefix}/share/pkgconfig
|
||||
# Generally ${prefix}/share/pkgconfig
|
||||
pkgconfigdir = @pkgconfigdir@
|
||||
|
||||
# Directory in which to install data across multiple architectures
|
||||
@@ -180,6 +180,12 @@ INST_PROJ_BIN_FILES = \
|
||||
|
||||
DISTFILES := $(DISTFILES_INC)
|
||||
|
||||
EXAMPLES_FIRST = \
|
||||
examples/hello_world_c \
|
||||
examples/hello_world_sc \
|
||||
|
||||
EXAMPLES = $(EXAMPLES_FIRST) $(filter-out $(EXAMPLES_FIRST), $(sort $(wildcard examples/*)))
|
||||
|
||||
ifeq ($(OBJCACHE_JOBS),)
|
||||
ifneq ($(OBJCACHE_HOSTS),)
|
||||
export OBJCACHE_JOBS := -j $(shell objcache --jobs "$(OBJCACHE_HOSTS)")
|
||||
@@ -226,7 +232,7 @@ test_regress: all_nomsg
|
||||
$(MAKE) -C test_regress
|
||||
|
||||
examples: all_nomsg
|
||||
for p in examples/* ; do \
|
||||
for p in $(EXAMPLES) ; do \
|
||||
$(MAKE) -C $$p VERILATOR_ROOT=`pwd` || exit 10; \
|
||||
done
|
||||
|
||||
@@ -377,34 +383,9 @@ install-here: installman ftp
|
||||
ifeq ($(VERILATOR_AUTHOR_SITE),1) # Local... Else don't burden users
|
||||
DISTNAMEREV = $(shell sed -e '/DTVERSION/!d' -e 's/.*verilator_\([^"]*\).*/\1/' -e q ${srcdir}/src/config_rev.h)
|
||||
|
||||
DIRPROJECT := $(shell project_dir --project)
|
||||
VERILATOR_CAD_DIR = $(CAD_DIR)/verilator/$(DISTNAMEREV)/$(DIRPROJECT_ARCH)
|
||||
INST_PROJ_CVS = cp_if_cvs_diff
|
||||
|
||||
install-project: dist
|
||||
@echo "Install-project to $(DIRPROJECT)"
|
||||
strip bin/verilator_bin*
|
||||
strip bin/verilator_coverage_bin*
|
||||
$(MAKE) install-project-quick
|
||||
for p in $(VL_INST_MAN_FILES) ; do \
|
||||
$(INSTALL_DATA) $$p $(DIRPROJECT_PREFIX)/man/man1/$$p; \
|
||||
done
|
||||
$(INST_PROJ_CVS) $(DISTNAME).tgz $(DIRPROJECT)/hw/utils/verilator/verilator.tgz
|
||||
rm $(DISTNAME).tgz
|
||||
|
||||
install-project-quick:
|
||||
ifeq ($(CFG_WITH_DEFENV),yes)
|
||||
@echo "%Error: Reconfigure with './configure --disable-defenv' to avoid hardcoded paths."
|
||||
false
|
||||
endif
|
||||
@echo "Install-project-quick (no strip) to $(DIRPROJECT)"
|
||||
for p in $(INST_PROJ_FILES) ; do \
|
||||
$(INST_PROJ_CVS) $$p $(DIRPROJECT)/hw/utils/verilator/$$p; \
|
||||
done
|
||||
for p in $(INST_PROJ_BIN_FILES) ; do \
|
||||
$(INST_PROJ_CVS) $$p $(DIRPROJECT)/hw/utils/verilator/$$p-$(DIRPROJECT_ARCH); \
|
||||
done
|
||||
|
||||
install-cadtools: dist
|
||||
@echo "Install-project to $(CAD_DIR)"
|
||||
strip bin/verilator_bin*
|
||||
@@ -521,8 +502,9 @@ clean mostlyclean distclean maintainer-clean::
|
||||
rm -f *.pg *.pgs *.toc *.tp *.tps *.vr *.vrs *.idx
|
||||
rm -f *.ev *.evs *.ov *.ovs *.cv *.cvs *.ma *.mas
|
||||
rm -f *.tex
|
||||
rm -rf test_*/obj_dir
|
||||
rm -rf examples/*/obj_dir examples/*/logs
|
||||
rm -rf test_*/obj_dir
|
||||
rm -rf nodist/obj_dir
|
||||
|
||||
distclean maintainer-clean::
|
||||
rm -f *.info* *.1 $(INFOS)
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
=pod
|
||||
|
||||
=for html <a href="https://travis-ci.com/verilator/verilator"><img src="https://travis-ci.com/verilator/verilator.svg?branch=master"></a>
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Welcome to Verilator. This is the Verilator package's README file.
|
||||
|
||||
+52
-20
@@ -17,13 +17,6 @@ eval 'exec perl -wS $0 ${1+"$@"}'
|
||||
|
||||
require 5.006_001;
|
||||
use warnings;
|
||||
BEGIN {
|
||||
if ($ENV{DIRPROJECT} && $ENV{DIRPROJECT_PERL_BOOT}) {
|
||||
# Magic to allow author testing of perl packages in local directory
|
||||
require $ENV{DIRPROJECT}."/".$ENV{DIRPROJECT_PERL_BOOT};
|
||||
}
|
||||
}
|
||||
|
||||
use Getopt::Long;
|
||||
use FindBin qw($RealBin $RealScript);
|
||||
use IO::File;
|
||||
@@ -41,6 +34,7 @@ autoflush STDERR 1;
|
||||
|
||||
$Debug = 0;
|
||||
my $opt_gdb;
|
||||
my $opt_rr;
|
||||
my $opt_gdbbt;
|
||||
my $opt_quiet_exit;
|
||||
|
||||
@@ -62,12 +56,13 @@ Getopt::Long::config("no_auto_abbrev","pass_through");
|
||||
if (! GetOptions(
|
||||
# Major operating modes
|
||||
"help" => \&usage,
|
||||
"debug:s" => \&debug,
|
||||
"debug" => \&debug,
|
||||
# "version!" => \&version, # Also passthru'ed
|
||||
# Switches
|
||||
"gdb!" => \$opt_gdb,
|
||||
"gdbbt!" => \$opt_gdbbt,
|
||||
"quiet-exit!" => \$opt_quiet_exit,
|
||||
"rr!" => \$opt_rr,
|
||||
# Additional parameters
|
||||
"<>" => sub {}, # Ignored
|
||||
)) {
|
||||
@@ -100,6 +95,10 @@ if ($opt_gdb) {
|
||||
." -ex \"run ".join(' ', @quoted_sw)."\""
|
||||
." -ex 'set width 0'"
|
||||
." -ex 'bt'");
|
||||
} elsif ($opt_rr) {
|
||||
# Record with rr
|
||||
run ("rr record ".verilator_bin()
|
||||
." ".join(' ', @quoted_sw));
|
||||
} elsif ($opt_gdbbt && $Debug) {
|
||||
# Run under GDB to get gdbbt
|
||||
run ("gdb"
|
||||
@@ -107,7 +106,7 @@ if ($opt_gdb) {
|
||||
." --batch --quiet --return-child-result"
|
||||
." -ex \"run ".join(' ', @quoted_sw)."\""
|
||||
." -ex 'set width 0'"
|
||||
." -ex 'bt'");
|
||||
." -ex 'bt' -ex 'quit'");
|
||||
} else {
|
||||
# Normal, non gdb
|
||||
run(verilator_bin()." ".join(' ',@quoted_sw));
|
||||
@@ -166,7 +165,8 @@ sub gdb_works {
|
||||
." --batch-silent --quiet --return-child-result"
|
||||
." -ex 'run -n'" # `echo -n`
|
||||
." -ex 'set width 0'"
|
||||
." -ex 'bt'");
|
||||
." -ex 'bt'"
|
||||
." -ex 'quit'");
|
||||
my $status = $?;
|
||||
return $status==0;
|
||||
}
|
||||
@@ -298,6 +298,7 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
|
||||
--debugi-<srcfile> <level> Enable debugging a source file at a level
|
||||
--default-language <lang> Default language to parse
|
||||
+define+<var>=<value> Set preprocessor define
|
||||
--dpi-hdr-only Only produce the DPI header file
|
||||
--dump-defines Show preprocessor defines with -E
|
||||
--dump-tree Enable dumping .tree files
|
||||
--dump-treei <level> Enable dumping .tree files at a level
|
||||
@@ -359,6 +360,7 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
|
||||
--relative-includes Resolve includes relative to current file
|
||||
--no-relative-cfuncs Disallow 'this->' in generated functions
|
||||
--report-unoptflat Extra diagnostics for UNOPTFLAT
|
||||
--rr Run Verilator and record with rr
|
||||
--savable Enable model save-restore
|
||||
--sc Create SystemC output
|
||||
--stats Create statistics file
|
||||
@@ -740,6 +742,11 @@ Defines the given preprocessor symbol, or multiple symbols if separated by
|
||||
plusses. Similar to -D; +define is fairly standard across Verilog tools
|
||||
while -D is an alias for GCC compatibility.
|
||||
|
||||
=item --dpi-hdr-only
|
||||
|
||||
Only generate the DPI header file. This option has no effect on the name
|
||||
or location of the file.
|
||||
|
||||
=item --dump-defines
|
||||
|
||||
With -E, suppress normal output, and instead print a list of all defines
|
||||
@@ -775,8 +782,8 @@ will also go to standard out.
|
||||
|
||||
=item --error-limit I<value>
|
||||
|
||||
After this number of errors or warnings are encountered, exit. Defaults to
|
||||
50.
|
||||
After this number of errors are encountered, exit. Warnings are not
|
||||
counted in this limit. Defaults to 50.
|
||||
|
||||
=item --exe
|
||||
|
||||
@@ -1198,6 +1205,10 @@ printing. For example:
|
||||
will generate a PDF Vt_unoptflat_simple_2_35_unoptflat.dot.pdf from the DOT
|
||||
file.
|
||||
|
||||
=item --rr
|
||||
|
||||
Run Verilator and record with rr. See: rr-project.org.
|
||||
|
||||
=item --savable
|
||||
|
||||
Enable including save and restore functions in the generated model.
|
||||
@@ -1287,7 +1298,7 @@ good value.
|
||||
When the input Verilog contains more than one top level module, specifies
|
||||
the name of the top level Verilog module to become the top, and sets the
|
||||
default for if --prefix is not used. This is not needed with standard
|
||||
designs with only one top.
|
||||
designs with only one top. See also the MULTITOP warning section.
|
||||
|
||||
=item --trace
|
||||
|
||||
@@ -2451,6 +2462,10 @@ Configuration files are parsed after the normal Verilog preprocessing, so
|
||||
`ifdefs, `defines, and comments may be used as if it were normal Verilog
|
||||
code.
|
||||
|
||||
Note that file or line-specific configuration only applies to files read
|
||||
after the configuration file. It is therefore recommended to pass the
|
||||
configuration file to Verilator as first file.
|
||||
|
||||
The grammar of configuration commands is as follows:
|
||||
|
||||
=over 4
|
||||
@@ -3725,14 +3740,31 @@ correctly.
|
||||
|
||||
=item MULTITOP
|
||||
|
||||
Error that there are multiple top level modules, that is modules not
|
||||
instantiated by any other module. Verilator only supports a single top
|
||||
level, if you need more, create a module that wraps all of the top modules.
|
||||
Warns that there are multiple top level modules, that is modules not
|
||||
instantiated by any other module, and both modules were put on the command
|
||||
line (not in a library). Three likely cases:
|
||||
|
||||
Often this error is because some low level cell is being read in, but is
|
||||
not really needed. The best solution is to insure that each module is in a
|
||||
unique file by the same name. Otherwise, make sure all library files are
|
||||
read in as libraries with -v, instead of automatically with -y.
|
||||
1. A single module is intended to be the top. This warning then occurs
|
||||
because some low level cell is being read in, but is not really needed as
|
||||
part of the design. The best solution for this situation is to ensure that
|
||||
only the top module is put on the command line without any flags, and all
|
||||
remaining library files are read in as libraries with -v, or are
|
||||
automatically resolved by having filenames that match the module names.
|
||||
|
||||
2. A single module is intended to be the top, the name of it is known, and
|
||||
all other modules should be ignored if not part of the design. The best
|
||||
solution is to use the --top-module option to specify the top module's
|
||||
name. All other modules that are not part of the design will be for the
|
||||
most part ignored (they must be clean in syntax and their contents will be
|
||||
removed as part of the Verilog module elaboration process.)
|
||||
|
||||
3. Multiple modules are intended to be design tops, e.g. when linting a
|
||||
library file. As multiple modules are desired, disable the MULTITOP
|
||||
warning. All input/outputs will go uniquely to each module, with any
|
||||
conflicting and identical signal names being uniquified by adding a prefix
|
||||
based on the top module name followed by __02E (a Verilator-encoded ASCII
|
||||
".'). This renaming is done even if the two modules' signals seem
|
||||
identical, e.g. multiple modules with a "clk" input.
|
||||
|
||||
=item PINCONNECTEMPTY
|
||||
|
||||
|
||||
@@ -17,13 +17,6 @@ eval 'exec perl -wS $0 ${1+"$@"}'
|
||||
|
||||
require 5.006_001;
|
||||
use warnings;
|
||||
BEGIN {
|
||||
if ($ENV{DIRPROJECT} && $ENV{DIRPROJECT_PERL_BOOT}) {
|
||||
# Magic to allow author testing of perl packages in local directory
|
||||
require $ENV{DIRPROJECT}."/".$ENV{DIRPROJECT_PERL_BOOT};
|
||||
}
|
||||
}
|
||||
|
||||
use Getopt::Long;
|
||||
use FindBin qw($RealBin $RealScript);
|
||||
use IO::File;
|
||||
|
||||
+8
-6
@@ -6,7 +6,7 @@
|
||||
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[4.016 2019-06-16],
|
||||
AC_INIT([Verilator],[4.018 2019-08-29],
|
||||
[https://www.veripool.org/verilator],
|
||||
[verilator],[https://www.veripool.org/verilator])
|
||||
# When releasing, also update header of Changes file
|
||||
@@ -91,10 +91,10 @@ AC_SUBST(CFG_WITH_PREC11)
|
||||
AC_MSG_RESULT($CFG_WITH_PREC11)
|
||||
|
||||
# Compiler flags
|
||||
CFLAGS=-I${includedir}
|
||||
CPPFLAGS=-I${includedir}
|
||||
CXXFLAGS=-I${includedir}
|
||||
LDFLAGS=-L${libdir}
|
||||
CFLAGS+=" -I${includedir}"
|
||||
CPPFLAGS+=" -I${includedir}"
|
||||
CXXFLAGS+=" -I${includedir}"
|
||||
LDFLAGS+=" -L${libdir}"
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
@@ -273,7 +273,9 @@ AC_SUBST(CFG_CXXFLAGS_PARSER)
|
||||
# without this flag, even though there's a conditional to prevent the divide.
|
||||
# We still don't add no-div-by-zero as it throws message to stdout, though doesn't die.
|
||||
#_MY_CXX_CHECK_OPT(-Wno-div-by-zero)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-faligned-new)
|
||||
# For some reason -faligned-new does not work under Travis w/ clang but the
|
||||
# configure test doesn't catch this either
|
||||
AS_IF([test "x$TRAVIS_COMPILER" != xclang], [_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-faligned-new)])
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-fbracket-depth=4096)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-Qunused-arguments)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-Wno-bool-operation)
|
||||
|
||||
+5
-1
@@ -4,10 +4,14 @@ under the Developer Certificate of Origin
|
||||
|
||||
Please see the Verilator manual for additional contributors.
|
||||
|
||||
Jeremy Bennett
|
||||
Alex Chadwick
|
||||
Gianfranco Costamagna
|
||||
Jeremy Bennett
|
||||
John Coiner
|
||||
Kanad Kanhere
|
||||
Maarten De Braekeleer
|
||||
Philipp Wagner
|
||||
Richard Myers
|
||||
Sebastien Van Cauwenberghe
|
||||
Stefan Wallentowitz
|
||||
Wilson Snyder
|
||||
|
||||
@@ -24,7 +24,7 @@ endif
|
||||
# e.g. examples/tracing_c.
|
||||
|
||||
# If $VERILATOR_ROOT isn't in the environment, we assume it is part of a
|
||||
# package inatall, and verilator is in your path. Otherwise find the
|
||||
# package install, and verilator is in your path. Otherwise find the
|
||||
# binary relative to $VERILATOR_ROOT (such as when inside the git sources).
|
||||
ifeq ($(VERILATOR_ROOT),)
|
||||
VERILATOR = verilator
|
||||
|
||||
@@ -24,7 +24,7 @@ endif
|
||||
# e.g. examples/tracing_sc.
|
||||
|
||||
# If $VERILATOR_ROOT isn't in the environment, we assume it is part of a
|
||||
# package inatall, and verilator is in your path. Otherwise find the
|
||||
# package install, and verilator is in your path. Otherwise find the
|
||||
# binary relative to $VERILATOR_ROOT (such as when inside the git sources).
|
||||
ifeq ($(VERILATOR_ROOT),)
|
||||
VERILATOR = verilator
|
||||
|
||||
@@ -21,7 +21,7 @@ endif
|
||||
# Set up variables
|
||||
|
||||
# If $VERILATOR_ROOT isn't in the environment, we assume it is part of a
|
||||
# package inatall, and verilator is in your path. Otherwise find the
|
||||
# package install, and verilator is in your path. Otherwise find the
|
||||
# binary relative to $VERILATOR_ROOT (such as when inside the git sources).
|
||||
ifeq ($(VERILATOR_ROOT),)
|
||||
VERILATOR = verilator
|
||||
|
||||
@@ -14,18 +14,18 @@ module top
|
||||
input fastclk,
|
||||
input reset_l,
|
||||
|
||||
output [1:0] out_small,
|
||||
output [39:0] out_quad,
|
||||
output [69:0] out_wide,
|
||||
output wire [1:0] out_small,
|
||||
output wire [39:0] out_quad,
|
||||
output wire [69:0] out_wide,
|
||||
input [1:0] in_small,
|
||||
input [39:0] in_quad,
|
||||
input [69:0] in_wide
|
||||
);
|
||||
|
||||
// Connect up the outputs, using some trivial logic
|
||||
wire [1:0] out_small = ~reset_l ? '0 : (in_small + 2'b1);
|
||||
wire [39:0] out_quad = ~reset_l ? '0 : (in_quad + 40'b1);
|
||||
wire [69:0] out_wide = ~reset_l ? '0 : (in_wide + 70'b1);
|
||||
assign out_small = ~reset_l ? '0 : (in_small + 2'b1);
|
||||
assign out_quad = ~reset_l ? '0 : (in_quad + 40'b1);
|
||||
assign out_wide = ~reset_l ? '0 : (in_wide + 70'b1);
|
||||
|
||||
// And an example sub module. The submodule will print stuff.
|
||||
sub sub (/*AUTOINST*/
|
||||
|
||||
@@ -21,7 +21,7 @@ endif
|
||||
# Set up variables
|
||||
|
||||
# If $VERILATOR_ROOT isn't in the environment, we assume it is part of a
|
||||
# package inatall, and verilator is in your path. Otherwise find the
|
||||
# package install, and verilator is in your path. Otherwise find the
|
||||
# binary relative to $VERILATOR_ROOT (such as when inside the git sources).
|
||||
ifeq ($(VERILATOR_ROOT),)
|
||||
VERILATOR = verilator
|
||||
|
||||
@@ -14,18 +14,18 @@ module top
|
||||
input fastclk,
|
||||
input reset_l,
|
||||
|
||||
output [1:0] out_small,
|
||||
output [39:0] out_quad,
|
||||
output [69:0] out_wide,
|
||||
output wire [1:0] out_small,
|
||||
output wire [39:0] out_quad,
|
||||
output wire [69:0] out_wide,
|
||||
input [1:0] in_small,
|
||||
input [39:0] in_quad,
|
||||
input [69:0] in_wide
|
||||
);
|
||||
|
||||
// Connect up the outputs, using some trivial logic
|
||||
wire [1:0] out_small = ~reset_l ? '0 : (in_small + 2'b1);
|
||||
wire [39:0] out_quad = ~reset_l ? '0 : (in_quad + 40'b1);
|
||||
wire [69:0] out_wide = ~reset_l ? '0 : (in_wide + 70'b1);
|
||||
assign out_small = ~reset_l ? '0 : (in_small + 2'b1);
|
||||
assign out_quad = ~reset_l ? '0 : (in_quad + 40'b1);
|
||||
assign out_wide = ~reset_l ? '0 : (in_wide + 70'b1);
|
||||
|
||||
// And an example sub module. The submodule will print stuff.
|
||||
sub sub (/*AUTOINST*/
|
||||
|
||||
@@ -285,6 +285,7 @@ IData VL_RANDOM_I(int obits) VL_MT_SAFE {
|
||||
QData VL_RANDOM_Q(int obits) VL_MT_SAFE {
|
||||
return vl_rand64() & VL_MASK_Q(obits);
|
||||
}
|
||||
// VL_RANDOM_W currently unused as $random always 32 bits
|
||||
WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||
for (int i=0; i<VL_WORDS_I(obits); ++i) {
|
||||
if (i<(VL_WORDS_I(obits)-1)) {
|
||||
@@ -1093,8 +1094,8 @@ IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE {
|
||||
IData bytes = VL_BYTES_I(obits);
|
||||
char buffer[VL_TO_STRING_MAX_WORDS*VL_WORDSIZE+1];
|
||||
// V3Emit has static check that bytes < VL_TO_STRING_MAX_WORDS, but be safe
|
||||
if (VL_UNLIKELY(bytes > VL_TO_STRING_MAX_WORDS*VL_WORDSIZE)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: fgets buffer overrun");
|
||||
if (VL_UNCOVERABLE(bytes > VL_TO_STRING_MAX_WORDS*VL_WORDSIZE)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: fgets buffer overrun"); // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
// We don't use fgets, as we must read \0s.
|
||||
@@ -1317,7 +1318,7 @@ const char* memhFormat(int nBits) {
|
||||
case 5: VL_SNPRINTF(buf, 32, "%%06x"); break;
|
||||
case 6: VL_SNPRINTF(buf, 32, "%%07x"); break;
|
||||
case 7: VL_SNPRINTF(buf, 32, "%%08x"); break;
|
||||
default: assert(false); break;
|
||||
default: assert(false); break; // LCOV_EXCL_LINE
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
@@ -1842,9 +1843,9 @@ void Verilated::flushCb(VerilatedVoidCb cb) VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
if (s_flushCb == cb) {} // Ok - don't duplicate
|
||||
else if (!s_flushCb) { s_flushCb=cb; }
|
||||
else {
|
||||
else { // LCOV_EXCL_LINE
|
||||
// Someday we may allow multiple callbacks ala atexit(), but until then
|
||||
VL_FATAL_MT("unknown", 0, "",
|
||||
VL_FATAL_MT("unknown", 0, "", // LCOV_EXCL_LINE
|
||||
"Verilated::flushCb called twice with different callbacks");
|
||||
}
|
||||
}
|
||||
@@ -2136,8 +2137,8 @@ void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_
|
||||
// Alternative is to dynamically stretch the array, which is more code, and slower.
|
||||
if (funcnum >= m_funcnumMax) { m_funcnumMax = funcnum+1; }
|
||||
} else {
|
||||
if (VL_UNLIKELY(funcnum >= m_funcnumMax)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
if (VL_UNCOVERABLE(funcnum >= m_funcnumMax)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", // LCOV_EXCL_LINE
|
||||
"Internal: Bad funcnum vs. pre-finalize maximum");
|
||||
}
|
||||
if (VL_UNLIKELY(!m_callbacksp)) { // First allocation
|
||||
|
||||
+4
-4
@@ -189,8 +189,8 @@ public:
|
||||
// METHODS
|
||||
/// Check that the current thread ID is the same as the construction thread ID
|
||||
void check() VL_MT_UNSAFE_ONE {
|
||||
if (!VL_LIKELY(m_threadid == VL_THREAD_ID())) {
|
||||
fatal_different();
|
||||
if (VL_UNCOVERABLE(m_threadid != VL_THREAD_ID())) {
|
||||
fatal_different(); // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
static void fatal_different() VL_MT_SAFE;
|
||||
@@ -318,8 +318,8 @@ public: // But internals only - called from VerilatedModule's
|
||||
if (VL_LIKELY(funcnum < scopep->m_funcnumMax)) {
|
||||
// m_callbacksp must be declared, as Max'es are > 0
|
||||
return scopep->m_callbacksp[funcnum];
|
||||
} else {
|
||||
return scopep->exportFindError(funcnum);
|
||||
} else { // LCOV_EXCL_LINE
|
||||
return scopep->exportFindError(funcnum); // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -65,7 +65,8 @@ VerilatedDeserialize& VerilatedDeserialize::readAssert(const void* __restrict da
|
||||
size_t size) VL_MT_UNSAFE_ONE {
|
||||
if (VL_UNLIKELY(readDiffers(datap, size))) {
|
||||
std::string fn = filename();
|
||||
std::string msg = "Can't deserialize save-restore file as was made from different model";
|
||||
std::string msg = "Can't deserialize save-restore file as was made from different model:"
|
||||
+filename();
|
||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||
close();
|
||||
}
|
||||
@@ -86,7 +87,8 @@ void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE {
|
||||
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
|
||||
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, strlen(VLTSAVE_HEADER_STR)))) {
|
||||
std::string fn = filename();
|
||||
std::string msg = std::string("Can't deserialize; file has wrong header signature");
|
||||
std::string msg = std::string("Can't deserialize; file has wrong header signature: ")
|
||||
+filename();
|
||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||
close();
|
||||
}
|
||||
@@ -103,7 +105,8 @@ void VerilatedDeserialize::trailer() VL_MT_UNSAFE_ONE {
|
||||
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
|
||||
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_TRAILER_STR, strlen(VLTSAVE_TRAILER_STR)))) {
|
||||
std::string fn = filename();
|
||||
std::string msg = std::string("Can't deserialize; file has wrong end-of-file signature");
|
||||
std::string msg = std::string("Can't deserialize; file has wrong end-of-file signature: ")
|
||||
+filename();
|
||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||
close();
|
||||
}
|
||||
|
||||
@@ -221,8 +221,7 @@ void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed) {
|
||||
eit->m_cpu,
|
||||
thread_id);
|
||||
break;
|
||||
default: assert(false);
|
||||
break;
|
||||
default: assert(false); break; // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
// We reserve word zero for the next pointer, as that's safer in case a
|
||||
// dangling reference to the original remains around.
|
||||
static const size_t chunk = 96;
|
||||
if (VL_UNLIKELY(size>chunk)) VL_FATAL_MT(__FILE__, __LINE__, "", "increase chunk");
|
||||
if (VL_UNCOVERABLE(size>chunk)) VL_FATAL_MT(__FILE__, __LINE__, "", "increase chunk");
|
||||
if (VL_LIKELY(t_freeHead)) {
|
||||
vluint8_t* newp = t_freeHead;
|
||||
t_freeHead = *((vluint8_t**)newp);
|
||||
@@ -348,7 +348,7 @@ public:
|
||||
varop->createPrevDatap();
|
||||
}
|
||||
}
|
||||
if (VL_UNLIKELY(vop->reason() >= CB_ENUM_MAX_VALUE)) {
|
||||
if (VL_UNCOVERABLE(vop->reason() >= CB_ENUM_MAX_VALUE)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "vpi bb reason too large");
|
||||
}
|
||||
s_s.m_cbObjLists[vop->reason()].push_back(vop);
|
||||
@@ -1255,7 +1255,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
||||
return;
|
||||
case VLVT_WDATA: {
|
||||
int words = VL_WORDS_I(vop->varp()->packed().elements());
|
||||
if (VL_UNLIKELY(words >= VL_MULS_MAX_WORDS)) {
|
||||
if (VL_UNCOVERABLE(words >= VL_MULS_MAX_WORDS)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"vpi_get_value with more than VL_MULS_MAX_WORDS; increase and recompile");
|
||||
}
|
||||
|
||||
@@ -110,6 +110,7 @@
|
||||
# define VL_LIKELY(x) (!!(x)) ///< Boolean expression more often true than false
|
||||
# define VL_UNLIKELY(x) (!!(x)) ///< Boolean expression more often false than true
|
||||
#endif
|
||||
#define VL_UNCOVERABLE(x) VL_UNLIKELY(x) ///< Boolean expression never hit by users (no coverage)
|
||||
#ifndef VL_UNREACHABLE
|
||||
# define VL_UNREACHABLE ///< Point that may never be reached
|
||||
#endif
|
||||
@@ -406,4 +407,4 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
||||
|
||||
//=========================================================================
|
||||
|
||||
#endif /*guard*/
|
||||
#endif // Guard
|
||||
|
||||
Executable
+263
@@ -0,0 +1,263 @@
|
||||
#!/usr/bin/perl -w
|
||||
# See copyright, etc in below POD section.
|
||||
######################################################################
|
||||
|
||||
use Cwd;
|
||||
use File::Copy qw(cp);
|
||||
use File::Path qw(mkpath);
|
||||
use Getopt::Long;
|
||||
use IO::File;
|
||||
use Pod::Usage;
|
||||
use strict;
|
||||
use vars qw($Debug);
|
||||
|
||||
our $Exclude_Line_Regexp;
|
||||
our @Remove_Sources;
|
||||
our @Source_Globs;
|
||||
|
||||
#======================================================================
|
||||
# main
|
||||
|
||||
our $Opt_Stage = 0;
|
||||
|
||||
autoflush STDOUT 1;
|
||||
autoflush STDERR 1;
|
||||
Getopt::Long::config("no_auto_abbrev");
|
||||
if (! GetOptions(
|
||||
"debug" => sub { $Debug = 1; },
|
||||
"<>" => sub { die "%Error: Unknown parameter: $_[0]\n"; },
|
||||
"stage=i" => \$Opt_Stage,
|
||||
)) {
|
||||
die "%Error: Bad usage, try 'install_test --help'\n";
|
||||
}
|
||||
|
||||
test();
|
||||
exit(0);
|
||||
|
||||
#######################################################################
|
||||
|
||||
sub test {
|
||||
-r "nodist/code_coverage.dat" or die "%Error: Run from the top of the verilator kit,";
|
||||
require "./nodist/code_coverage.dat";
|
||||
|
||||
if ($Opt_Stage <= 0) {
|
||||
print "Stage 0: configure (coverage on)\n";
|
||||
run("make distclean");
|
||||
run("./configure --enable-longtests CXX='g++ --coverage'");
|
||||
run("make -k");
|
||||
# The optimized versions will not collect good coverage, overwrite them
|
||||
run("cp bin/verilator_bin_dbg bin/verilator_bin");
|
||||
run("cp bin/verilator_coverage_bin_dbg bin/verilator_coverage_bin");
|
||||
}
|
||||
|
||||
if ($Opt_Stage <= 1) {
|
||||
print "Stage 1: make examples (with coverage on)\n";
|
||||
run("make examples");
|
||||
run("make test_regress");
|
||||
}
|
||||
|
||||
my $cc_dir = "nodist/obj_dir/coverage";
|
||||
if ($Opt_Stage <= 2) {
|
||||
print "Stage 2: Create info files under $cc_dir\n";
|
||||
mkpath($cc_dir);
|
||||
mkpath("$cc_dir/info");
|
||||
my $dats = `find . -print | grep .gcda`;
|
||||
my %dirs;
|
||||
foreach my $dat (split '\n', $dats) {
|
||||
$dat =~ s!/[^/]+$!!;
|
||||
$dirs{$dat} = 1;
|
||||
}
|
||||
foreach my $dir (sort keys %dirs) {
|
||||
(my $outname = $dir) =~ s![^a-zA-Z0-9]+!_!g;
|
||||
run("cd $cc_dir/info ; lcov -c -d ../../../../$dir -o app_test_${outname}.info");
|
||||
}
|
||||
}
|
||||
|
||||
if ($Opt_Stage <= 3) {
|
||||
# lcov doesn't have a control file to override single lines, so replicate the sources
|
||||
print "Stage 3: Clone sources under $cc_dir\n";
|
||||
clone_sources($cc_dir);
|
||||
}
|
||||
|
||||
if ($Opt_Stage <= 4) {
|
||||
print "Stage 4: Copy .gcno files\n";
|
||||
my $dats = `find . -print | grep .gcno`;
|
||||
foreach my $dat (split '\n', $dats) {
|
||||
next if $dat =~ /$cc_dir/;
|
||||
my $outdat = $cc_dir."/".$dat;
|
||||
#print "cp $dat, $outdat);\n";
|
||||
cp($dat, $outdat);
|
||||
}
|
||||
}
|
||||
|
||||
if ($Opt_Stage <= 5) {
|
||||
print "Stage 5: Combine data files\n";
|
||||
run("cd $cc_dir ; lcov -c -i -d src/obj_dbg -o app_base.info");
|
||||
run("cd $cc_dir ; lcov -a app_base.info -o app_total.info");
|
||||
my $infos = `cd $cc_dir ; find info -print | grep .info`;
|
||||
my $comb = "";
|
||||
my @infos = (split /\n/, $infos);
|
||||
foreach my $info (@infos) {
|
||||
$comb .= " -a $info";
|
||||
# Need to batch them to avoid overrunning shell command length limit
|
||||
if (length($comb) > 10000 || $info eq $infos[$#infos]) {
|
||||
run("cd $cc_dir ; lcov -a app_total.info $comb -o app_total.info");
|
||||
$comb = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($Opt_Stage <= 6) {
|
||||
print "Stage 6: Filter processed source files\n";
|
||||
my $cmd = '';
|
||||
foreach my $glob (@Remove_Sources) {
|
||||
$cmd .= " '$glob'";
|
||||
}
|
||||
run("cd $cc_dir ; lcov --remove app_total.info $cmd -o app_total.info");
|
||||
}
|
||||
|
||||
if ($Opt_Stage <= 7) {
|
||||
print "Stage 7: Create HTML\n";
|
||||
cleanup_abs_paths($cc_dir, "$cc_dir/app_total.info", "$cc_dir/app_total.info");
|
||||
run("cd $cc_dir ; genhtml app_total.info --demangle-cpp"
|
||||
." --rc lcov_branch_coverage=1 --rc genhtml_hi_limit=100 --output-directory html");
|
||||
}
|
||||
|
||||
if ($Opt_Stage <= 9) {
|
||||
print "*-* All Finished *-*\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub clone_sources {
|
||||
my $cc_dir = shift;
|
||||
my $excluded_lines = 0;
|
||||
foreach my $glob (@Source_Globs) {
|
||||
foreach my $infile (glob $glob) {
|
||||
$infile !~ m!^/!
|
||||
or die "%Error: source globs should be relative not absolute filenames,";
|
||||
my $outfile = $cc_dir."/".$infile;
|
||||
(my $outpath = $outfile) =~ s!/[^/]*$!!;
|
||||
mkpath($outpath);
|
||||
my $fh = IO::File->new("<$infile") or die "%Error: $! $infile,";
|
||||
my $ofh = IO::File->new(">$outfile") or die "%Error: $! $outfile,";
|
||||
my $lineno = 0;
|
||||
while (defined(my $line = $fh->getline)) {
|
||||
$lineno++;
|
||||
chomp $line;
|
||||
if ($line !~ m!// LCOV_EXCL_LINE!
|
||||
&& $line =~ /$Exclude_Line_Regexp/) {
|
||||
$line .= " //code_coverage: // LCOV_EXCL_LINE";
|
||||
$excluded_lines++;
|
||||
#print "$infile:$lineno: $line";
|
||||
} else {
|
||||
}
|
||||
$ofh->print("$line\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
print "Source code lines automatically LCOV_EXCL_LINE'ed: $excluded_lines\n";
|
||||
}
|
||||
|
||||
sub cleanup_abs_paths {
|
||||
my $cc_dir = shift;
|
||||
my $infile = shift;
|
||||
my $outfile = shift;
|
||||
my $fh = IO::File->new("<$infile") or die "%Error: $! $infile,";
|
||||
my @lines;
|
||||
while (defined(my $line = $fh->getline)) {
|
||||
if ($line =~ m!^SF:!) {
|
||||
$line =~ s!$ENV{VERILATOR_ROOT}/!!;
|
||||
$line =~ s!$cc_dir/!!;
|
||||
$line =~ s!obj_dbg/verilog.y$!verilog.y!;
|
||||
#print "Remaining SF: ",$line;
|
||||
}
|
||||
push @lines, $line;
|
||||
}
|
||||
my $ofh = IO::File->new(">$outfile") or die "%Error: $! $outfile,";
|
||||
$ofh->print(@lines);
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
# .dat file callbacks
|
||||
|
||||
sub exclude_line_regexp {
|
||||
$Exclude_Line_Regexp = shift;
|
||||
}
|
||||
|
||||
sub remove_source {
|
||||
my @srcs = @_;
|
||||
push @Remove_Sources, @srcs;
|
||||
}
|
||||
|
||||
sub source_globs {
|
||||
my @dirs = @_;
|
||||
push @Source_Globs, @dirs;
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
|
||||
sub run {
|
||||
# Run a system command, check errors
|
||||
my $command = shift;
|
||||
print "\t$command\n";
|
||||
system "$command";
|
||||
my $status = $?;
|
||||
($status == 0) or die "%Error: Command Failed $command, $status, stopped";
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
code_coverage - Build and collect Verilator coverage
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
cd $VERILATOR_ROOT
|
||||
nodist/code_coverage
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
code_coverage builds Verilator with C++ coverage support and runs tests
|
||||
with coverage enabled.
|
||||
|
||||
This will rebuild the current object files.
|
||||
|
||||
=head1 ARGUMENTS
|
||||
|
||||
=over 4
|
||||
|
||||
=item --help
|
||||
|
||||
Displays this message and program version and exits.
|
||||
|
||||
=item -stage I<stage>
|
||||
|
||||
Runs a specific stage (see the script).
|
||||
|
||||
=back
|
||||
|
||||
=head1 DISTRIBUTION
|
||||
|
||||
Copyright 2019-2019 by Wilson Snyder. This package 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.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Wilson Snyder <[email protected]>
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
C<lcov>
|
||||
|
||||
=cut
|
||||
|
||||
######################################################################
|
||||
### Local Variables:
|
||||
### compile-command: "cd .. ; nodist/code_coverage "
|
||||
### End:
|
||||
@@ -0,0 +1,42 @@
|
||||
# -*- Perl -*-
|
||||
# DESCRIPTION: Verilator: Internal C++ code lcov control file
|
||||
#
|
||||
# Copyright 2019-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.
|
||||
|
||||
source_globs("src/*.cpp",
|
||||
"src/*.h",
|
||||
"src/*.l",
|
||||
"src/*.y",
|
||||
"src/obj_dbg/*.h",
|
||||
"src/obj_dbg/*.cpp",
|
||||
"include/*.c",
|
||||
"include/*.cpp",
|
||||
"include/*.h",
|
||||
"include/*/*.h",
|
||||
"include/*/*.cpp",
|
||||
"include/*/*.c",
|
||||
);
|
||||
|
||||
remove_source("/usr/include/*");
|
||||
remove_source("*/include/sysc/*");
|
||||
remove_source("*/V3ClkGater.cpp");
|
||||
remove_source("*/V3ClkGater.h");
|
||||
remove_source("*/V3GraphDfa.cpp");
|
||||
remove_source("*/V3GraphDfa.h");
|
||||
remove_source("*/V3Lexer_pregen.yy.cpp");
|
||||
remove_source("*/V3PreLex_pregen.yy.cpp");
|
||||
remove_source("*/verilog.c");
|
||||
remove_source("*include/gtkwave/*");
|
||||
# Something wrong in generation, unfortunately as would like this
|
||||
#genhtml: ERROR: cannot read /svaha/wsnyder/SandBox/homecvs/v4/verilator/src/obj_dbg/verilog.y
|
||||
#remove_source("*/src/obj_dbg/verilog.y");
|
||||
remove_source("*test_regress/*");
|
||||
remove_source("*examples/*");
|
||||
|
||||
|
||||
exclude_line_regexp(qr/(\bv3fatalSrc\b|\bVL_UNCOVERABLE\b)/);
|
||||
|
||||
1;
|
||||
+1
-1
@@ -132,7 +132,7 @@ sub edit_file {
|
||||
foreach my $lineno (sort {$a <=> $b} keys %$editlines) {
|
||||
push @editits, $lineno if $editlines->{$lineno}{editit};
|
||||
}
|
||||
|
||||
|
||||
return if $#editits < 0;
|
||||
if (ignore($filename)) {
|
||||
print "%Warning: Ignoring $filename\n";
|
||||
|
||||
+9
-8
@@ -60,6 +60,8 @@ pkgdatadir = @pkgdatadir@
|
||||
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
|
||||
CFG_WITH_DEFENV = @CFG_WITH_DEFENV@
|
||||
CPPFLAGS += @CPPFLAGS@
|
||||
CFLAGS += @CFLAGS@
|
||||
CXXFLAGS += @CXXFLAGS@
|
||||
LDFLAGS += @LDFLAGS@
|
||||
EXEEXT = @EXEEXT@
|
||||
CFG_CXXFLAGS_SRC = @CFG_CXXFLAGS_SRC@
|
||||
@@ -135,7 +137,7 @@ VLCOVGEN = $(srcdir)/vlcovgen
|
||||
all: make_info $(TGT)
|
||||
|
||||
make_info:
|
||||
@echo " Compile flags: " $(CXX) ${CPPFLAGS}
|
||||
@echo " Compile flags: " $(CXX) ${CFLAGS} ${CXXFLAGS} ${CPPFLAGS}
|
||||
|
||||
clean mostlyclean distclean maintainer-clean::
|
||||
-rm -f *.o *.d perlxsi.c *_gen_*
|
||||
@@ -167,7 +169,6 @@ RAW_OBJS = \
|
||||
V3Cdc.o \
|
||||
V3Changed.o \
|
||||
V3Clean.o \
|
||||
V3ClkGater.o \
|
||||
V3Clock.o \
|
||||
V3Combine.o \
|
||||
V3Config.o \
|
||||
@@ -280,21 +281,21 @@ V3Number_test: V3Number_test.o
|
||||
$(PERL) $(ASTGEN) -I$(srcdir) $*.cpp
|
||||
|
||||
%.o: %.cpp
|
||||
$(OBJCACHE) ${CXX} ${CPPFLAGSWALL} -c $<
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSWALL} -c $<
|
||||
%.o: %.c
|
||||
$(OBJCACHE) ${CC} ${CPPFLAGSWALL} -c $<
|
||||
$(OBJCACHE) ${CC} ${CFLAGS} ${CPPFLAGSWALL} -c $<
|
||||
|
||||
V3ParseLex.o: V3ParseLex.cpp V3Lexer.yy.cpp V3ParseBison.c
|
||||
$(OBJCACHE) ${CXX} ${CPPFLAGSPARSER} -c $<
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSPARSER} -c $<
|
||||
|
||||
V3ParseGrammar.o: V3ParseGrammar.cpp V3ParseBison.c
|
||||
$(OBJCACHE) ${CXX} ${CPPFLAGSPARSER} -c $<
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSPARSER} -c $<
|
||||
|
||||
V3ParseImp.o: V3ParseImp.cpp V3ParseBison.c
|
||||
$(OBJCACHE) ${CXX} ${CPPFLAGSPARSER} -c $<
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSPARSER} -c $<
|
||||
|
||||
V3PreProc.o: V3PreProc.cpp V3PreLex.yy.cpp
|
||||
$(OBJCACHE) ${CXX} ${CPPFLAGSPARSER} -c $<
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSPARSER} -c $<
|
||||
|
||||
#### Generated files
|
||||
|
||||
|
||||
+16
-16
@@ -66,7 +66,7 @@ private:
|
||||
|
||||
// METHODS
|
||||
void addActive(AstActive* nodep) {
|
||||
if (!m_scopep) nodep->v3fatalSrc("NULL scope");
|
||||
UASSERT_OBJ(m_scopep, nodep, "NULL scope");
|
||||
m_scopep->addActivep(nodep);
|
||||
}
|
||||
// VISITORS
|
||||
@@ -168,13 +168,14 @@ private:
|
||||
// Convert to a non-delayed assignment
|
||||
UINFO(5," ASSIGNDLY "<<nodep<<endl);
|
||||
if (m_check == CT_INITIAL) {
|
||||
nodep->v3warn(INITIALDLY, "Delayed assignments (<=) in initial"
|
||||
" or final block; suggest blocking assignments (=).");
|
||||
nodep->v3warn(INITIALDLY, "Delayed assignments (<=) in initial or final block\n"
|
||||
<<nodep->warnMore()<<"... Suggest blocking assignments (=)");
|
||||
} else if (m_check == CT_LATCH) {
|
||||
// Suppress. Shouldn't matter that the interior of the latch races
|
||||
} else {
|
||||
nodep->v3warn(COMBDLY, "Delayed assignments (<=) in non-clocked"
|
||||
" (non flop or latch) block; suggest blocking assignments (=).");
|
||||
" (non flop or latch) block\n"
|
||||
<<nodep->warnMore()<<"... Suggest blocking assignments (=)");
|
||||
}
|
||||
AstNode* newp = new AstAssign(nodep->fileline(),
|
||||
nodep->lhsp()->unlinkFrBack(),
|
||||
@@ -196,16 +197,15 @@ private:
|
||||
if (m_check == CT_SEQ
|
||||
&& m_assignp
|
||||
&& !varp->isUsedLoopIdx() // Ignore loop indicies
|
||||
&& !varp->isTemp()
|
||||
) {
|
||||
&& !varp->isTemp()) {
|
||||
// Allow turning off warnings on the always, or the variable also
|
||||
if (!m_alwaysp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
|
||||
&& !m_assignp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
|
||||
&& !varp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
|
||||
) {
|
||||
&& !varp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)) {
|
||||
m_assignp->v3warn(BLKSEQ, "Blocking assignments (=) in sequential (flop or latch) block\n"
|
||||
<<m_assignp->warnMore()<<"... Suggest delayed assignments (<=)");
|
||||
m_alwaysp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); // Complain just once for the entire always
|
||||
varp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true);
|
||||
nodep->v3warn(BLKSEQ, "Blocking assignments (=) in sequential (flop or latch) block; suggest delayed assignments (<=).");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -314,7 +314,8 @@ private:
|
||||
&& VN_IS(oldsensesp->sensesp(), SenItem)
|
||||
&& VN_CAST(oldsensesp->sensesp(), SenItem)->isNever()) {
|
||||
// Never executing. Kill it.
|
||||
if (oldsensesp->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated.");
|
||||
UASSERT_OBJ(!oldsensesp->sensesp()->nextp(), nodep,
|
||||
"Never senitem should be alone, else the never should be eliminated.");
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
return;
|
||||
}
|
||||
@@ -391,11 +392,10 @@ private:
|
||||
}
|
||||
virtual void visit(AstSenGate* nodep) {
|
||||
AstSenItem* subitemp = nodep->sensesp();
|
||||
if (subitemp->edgeType() != AstEdgeType::ET_ANYEDGE
|
||||
&& subitemp->edgeType() != AstEdgeType::ET_POSEDGE
|
||||
&& subitemp->edgeType() != AstEdgeType::ET_NEGEDGE) {
|
||||
nodep->v3fatalSrc("Strange activity type under SenGate");
|
||||
}
|
||||
UASSERT_OBJ(subitemp->edgeType() == AstEdgeType::ET_ANYEDGE
|
||||
|| subitemp->edgeType() == AstEdgeType::ET_POSEDGE
|
||||
|| subitemp->edgeType() == AstEdgeType::ET_NEGEDGE,
|
||||
nodep, "Strange activity type under SenGate");
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstSenItem* nodep) {
|
||||
@@ -408,7 +408,7 @@ private:
|
||||
// V3LinkResolve should have cleaned most of these up
|
||||
if (!nodep->varrefp()->width1()) {
|
||||
nodep->v3error("Unsupported: Non-single bit wide signal pos/negedge sensitivity: "
|
||||
<<nodep->varrefp()->prettyName());
|
||||
<<nodep->varrefp()->prettyNameQ());
|
||||
}
|
||||
m_itemSequent = true;
|
||||
nodep->varrefp()->varp()->usedClock(true);
|
||||
|
||||
+11
-13
@@ -72,14 +72,13 @@ private:
|
||||
UINFO(4," ACTIVE "<<nodep<<endl);
|
||||
V3Const::constifyExpensiveEdit(nodep); // Remove duplicate clocks and such; sensesp() may change!
|
||||
AstSenTree* sensesp = nodep->sensesp();
|
||||
if (!sensesp) nodep->v3fatalSrc("NULL");
|
||||
UASSERT_OBJ(sensesp, nodep, "NULL");
|
||||
if (sensesp->sensesp()
|
||||
&& VN_IS(sensesp->sensesp(), SenItem)
|
||||
&& VN_CAST(sensesp->sensesp(), SenItem)->isNever()) {
|
||||
// Never executing. Kill it.
|
||||
if (sensesp->sensesp()->nextp()) {
|
||||
nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated.");
|
||||
}
|
||||
UASSERT_OBJ(!sensesp->sensesp()->nextp(), nodep,
|
||||
"Never senitem should be alone, else the never should be eliminated.");
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
return;
|
||||
}
|
||||
@@ -101,9 +100,8 @@ private:
|
||||
// Move the active's contents to the other active
|
||||
UINFO(4," merge active "<<sensesp<<" into "<<wantp<<endl);
|
||||
if (nodep->sensesStorep()) {
|
||||
if (sensesp != nodep->sensesStorep()) {
|
||||
nodep->v3fatalSrc("sensesStore should have been deleted earlier if different");
|
||||
}
|
||||
UASSERT_OBJ(sensesp == nodep->sensesStorep(), nodep,
|
||||
"sensesStore should have been deleted earlier if different");
|
||||
sensesp->unlinkFrBack();
|
||||
// There may be other references to same sense tree,
|
||||
// we'll be removing all references when we get to them,
|
||||
@@ -115,22 +113,22 @@ private:
|
||||
// No need to do statements under it, they're already moved.
|
||||
//iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstInitial* nodep) {
|
||||
virtual void visit(AstInitial* nodep) { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
}
|
||||
virtual void visit(AstAssignAlias* nodep) {
|
||||
virtual void visit(AstAssignAlias* nodep) { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
}
|
||||
virtual void visit(AstAssignW* nodep) {
|
||||
virtual void visit(AstAssignW* nodep) { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) {
|
||||
virtual void visit(AstAlways* nodep) { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
}
|
||||
virtual void visit(AstAlwaysPublic* nodep) {
|
||||
virtual void visit(AstAlwaysPublic* nodep) { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
}
|
||||
virtual void visit(AstFinal* nodep) {
|
||||
virtual void visit(AstFinal* nodep) { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc("Node should have been deleted");
|
||||
}
|
||||
// Empty visitors, speed things up
|
||||
|
||||
+4
-3
@@ -120,7 +120,7 @@ private:
|
||||
} else {
|
||||
// V3Coverage assigned us a bucket to increment.
|
||||
AstCoverInc* covincp = VN_CAST(snodep->coverincp(), CoverInc);
|
||||
if (!covincp) snodep->v3fatalSrc("Missing AstCoverInc under assertion");
|
||||
UASSERT_OBJ(covincp, snodep, "Missing AstCoverInc under assertion");
|
||||
covincp->unlinkFrBack();
|
||||
if (message!="") covincp->declp()->comment(message);
|
||||
bodysp = covincp;
|
||||
@@ -317,10 +317,11 @@ private:
|
||||
iterateChildren(nodep);
|
||||
uint32_t ticks = 1;
|
||||
if (nodep->ticksp()) {
|
||||
if (!VN_IS(nodep->ticksp(), Const)) nodep->v3fatalSrc("Expected constant ticks, checked in V3Width");
|
||||
UASSERT_OBJ(VN_IS(nodep->ticksp(), Const), nodep,
|
||||
"Expected constant ticks, checked in V3Width");
|
||||
ticks = VN_CAST(nodep->ticksp(), Const)->toUInt();
|
||||
}
|
||||
if (ticks<1) nodep->v3fatalSrc("0 tick should have been checked in V3Width");
|
||||
UASSERT_OBJ(ticks>=1, nodep, "0 tick should have been checked in V3Width");
|
||||
AstNode* inp = nodep->exprp()->unlinkFrBack();
|
||||
AstVar* invarp = NULL;
|
||||
AstSenTree* sentreep = nodep->sentreep(); sentreep->unlinkFrBack();
|
||||
|
||||
+88
-50
@@ -248,7 +248,7 @@ inline void AstNode::debugTreeChange(const char* prefix, int lineno, bool next)
|
||||
|
||||
AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) {
|
||||
// Add to m_nextp, returns this
|
||||
UDEBUGONLY(if (!newp) nodep->v3fatalSrc("Null item passed to addNext"););
|
||||
UDEBUGONLY(UASSERT_OBJ(newp, nodep, "Null item passed to addNext"););
|
||||
nodep->debugTreeChange("-addNextThs: ", __LINE__, false);
|
||||
newp->debugTreeChange("-addNextNew: ", __LINE__, true);
|
||||
if (!nodep) { // verilog.y and lots of other places assume this
|
||||
@@ -259,7 +259,8 @@ AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) {
|
||||
if (oldtailp->m_nextp) {
|
||||
if (oldtailp->m_headtailp) {
|
||||
oldtailp = oldtailp->m_headtailp; // This=beginning of list, jump to end
|
||||
UDEBUGONLY(if (oldtailp->m_nextp) nodep->v3fatalSrc("Node had next, but headtail says it shouldn't"););
|
||||
UDEBUGONLY(UASSERT_OBJ(!oldtailp->m_nextp, nodep,
|
||||
"Node had next, but headtail says it shouldn't"););
|
||||
} else {
|
||||
// Though inefficent, we are occasionally passed a addNext in the middle of a list.
|
||||
while (oldtailp->m_nextp != NULL) oldtailp = oldtailp->m_nextp;
|
||||
@@ -337,9 +338,9 @@ void AstNode::addNextHere(AstNode* newp) {
|
||||
|
||||
void AstNode::setOp1p(AstNode* newp) {
|
||||
UASSERT(newp, "Null item passed to setOp1p");
|
||||
UDEBUGONLY(if (m_op1p) this->v3fatalSrc("Adding to non-empty, non-list op1"););
|
||||
UDEBUGONLY(if (newp->m_backp) newp->v3fatalSrc("Adding already linked node"););
|
||||
UDEBUGONLY(if (newp->m_nextp) newp->v3fatalSrc("Adding list to non-list op1"););
|
||||
UDEBUGONLY(UASSERT_OBJ(!m_op1p, this, "Adding to non-empty, non-list op1"););
|
||||
UDEBUGONLY(UASSERT_OBJ(!newp->m_backp, newp, "Adding already linked node"););
|
||||
UDEBUGONLY(UASSERT_OBJ(!newp->m_nextp, newp, "Adding list to non-list op1"););
|
||||
this->debugTreeChange("-setOp1pThs: ", __LINE__, false);
|
||||
newp->debugTreeChange("-setOp1pNew: ", __LINE__, true);
|
||||
m_op1p = newp;
|
||||
@@ -350,9 +351,9 @@ void AstNode::setOp1p(AstNode* newp) {
|
||||
|
||||
void AstNode::setOp2p(AstNode* newp) {
|
||||
UASSERT(newp, "Null item passed to setOp2p");
|
||||
UDEBUGONLY(if (m_op2p) this->v3fatalSrc("Adding to non-empty, non-list op2"););
|
||||
UDEBUGONLY(if (newp->m_backp) newp->v3fatalSrc("Adding already linked node"););
|
||||
UDEBUGONLY(if (newp->m_nextp) newp->v3fatalSrc("Adding list to non-list op2"););
|
||||
UDEBUGONLY(UASSERT_OBJ(!m_op2p, this, "Adding to non-empty, non-list op2"););
|
||||
UDEBUGONLY(UASSERT_OBJ(!newp->m_backp, newp, "Adding already linked node"););
|
||||
UDEBUGONLY(UASSERT_OBJ(!newp->m_nextp, newp, "Adding list to non-list op2"););
|
||||
this->debugTreeChange("-setOp2pThs: ", __LINE__, false);
|
||||
newp->debugTreeChange("-setOp2pNew: ", __LINE__, true);
|
||||
m_op2p = newp;
|
||||
@@ -363,9 +364,9 @@ void AstNode::setOp2p(AstNode* newp) {
|
||||
|
||||
void AstNode::setOp3p(AstNode* newp) {
|
||||
UASSERT(newp, "Null item passed to setOp3p");
|
||||
UDEBUGONLY(if (m_op3p) this->v3fatalSrc("Adding to non-empty, non-list op3"););
|
||||
UDEBUGONLY(if (newp->m_backp) newp->v3fatalSrc("Adding already linked node"););
|
||||
UDEBUGONLY(if (newp->m_nextp) newp->v3fatalSrc("Adding list to non-list op3"););
|
||||
UDEBUGONLY(UASSERT_OBJ(!m_op3p, this, "Adding to non-empty, non-list op3"););
|
||||
UDEBUGONLY(UASSERT_OBJ(!newp->m_backp, newp, "Adding already linked node"););
|
||||
UDEBUGONLY(UASSERT_OBJ(!newp->m_nextp, newp, "Adding list to non-list op3"););
|
||||
this->debugTreeChange("-setOp3pThs: ", __LINE__, false);
|
||||
newp->debugTreeChange("-setOp3pNew: ", __LINE__, true);
|
||||
m_op3p = newp;
|
||||
@@ -376,9 +377,9 @@ void AstNode::setOp3p(AstNode* newp) {
|
||||
|
||||
void AstNode::setOp4p(AstNode* newp) {
|
||||
UASSERT(newp, "Null item passed to setOp4p");
|
||||
UDEBUGONLY(if (m_op4p) this->v3fatalSrc("Adding to non-empty, non-list op4"););
|
||||
UDEBUGONLY(if (newp->m_backp) newp->v3fatalSrc("Adding already linked node"););
|
||||
UDEBUGONLY(if (newp->m_nextp) newp->v3fatalSrc("Adding list to non-list op4"););
|
||||
UDEBUGONLY(UASSERT_OBJ(!m_op4p, this, "Adding to non-empty, non-list op4"););
|
||||
UDEBUGONLY(UASSERT_OBJ(!newp->m_backp, newp, "Adding already linked node"););
|
||||
UDEBUGONLY(UASSERT_OBJ(!newp->m_nextp, newp, "Adding list to non-list op4"););
|
||||
this->debugTreeChange("-setOp4pThs: ", __LINE__, false);
|
||||
newp->debugTreeChange("-setOp4pNew: ", __LINE__, true);
|
||||
m_op4p = newp;
|
||||
@@ -580,9 +581,11 @@ void AstNode::relinkOneLink(AstNode*& pointpr, // Ref to pointer that gets set
|
||||
// Insert the whole old list following the new node's list.
|
||||
// Thus a unlink without next, followed by relink, gives the same list.
|
||||
AstNode* newlistlastp = newp->m_headtailp;
|
||||
if (newlistlastp->m_nextp && newlistlastp!=newp) newp->v3fatalSrc("Headtailp tail isn't at the tail");
|
||||
UASSERT_OBJ(!(newlistlastp->m_nextp && newlistlastp!=newp), newp,
|
||||
"Headtailp tail isn't at the tail");
|
||||
AstNode* oldlistlastp = pointpr->m_headtailp;
|
||||
if (oldlistlastp->m_nextp && oldlistlastp!=pointpr) newp->v3fatalSrc("Old headtailp tail isn't at the tail");
|
||||
UASSERT_OBJ(!(oldlistlastp->m_nextp && oldlistlastp!=pointpr), newp,
|
||||
"Old headtailp tail isn't at the tail");
|
||||
// Next links
|
||||
newlistlastp->m_nextp = pointpr;
|
||||
pointpr->m_backp = newlistlastp;
|
||||
@@ -772,14 +775,15 @@ void AstNode::iterateAndNext(AstNVisitor& v) {
|
||||
// there's no lower level reason yet though the back must exist.
|
||||
AstNode* nodep = this;
|
||||
#ifdef VL_DEBUG // Otherwise too hot of a function for debug
|
||||
if (VL_UNLIKELY(nodep && !nodep->m_backp)) nodep->v3fatalSrc("iterateAndNext node has no back");
|
||||
UASSERT_OBJ(!(nodep && !nodep->m_backp), nodep,
|
||||
"iterateAndNext node has no back");
|
||||
#endif
|
||||
if (nodep) ASTNODE_PREFETCH(nodep->m_nextp);
|
||||
while (nodep) { // effectively: if (!this) return; // Callers rely on this
|
||||
if (nodep->m_nextp) ASTNODE_PREFETCH(nodep->m_nextp->m_nextp);
|
||||
AstNode* niterp = nodep; // This address may get stomped via m_iterpp if the node is edited
|
||||
// Desirable check, but many places where multiple iterations are OK
|
||||
//if (VL_UNLIKELY(niterp->m_iterpp)) niterp->v3fatalSrc("IterateAndNext under iterateAndNext may miss edits");
|
||||
// UASSERT_OBJ(!niterp->m_iterpp, niterp, "IterateAndNext under iterateAndNext may miss edits");
|
||||
// Optimization note: Doing PREFETCH_RW on m_iterpp is a net even
|
||||
// cppcheck-suppress nullPointer
|
||||
niterp->m_iterpp = &niterp;
|
||||
@@ -856,7 +860,7 @@ AstNode* AstNode::iterateSubtreeReturnEdits(AstNVisitor& v) {
|
||||
else if (this->m_backp->m_op3p == this) nextnodepp = &(this->m_backp->m_op3p);
|
||||
else if (this->m_backp->m_op4p == this) nextnodepp = &(this->m_backp->m_op4p);
|
||||
else if (this->m_backp->m_nextp == this) nextnodepp = &(this->m_backp->m_nextp);
|
||||
if (!nextnodepp) this->v3fatalSrc("Node's back doesn't point to forward to node itself");
|
||||
UASSERT_OBJ(nextnodepp, this, "Node's back doesn't point to forward to node itself");
|
||||
{
|
||||
nodep->accept(v); VL_DANGLING(nodep); // nodep to null as may be replaced
|
||||
}
|
||||
@@ -934,13 +938,11 @@ V3Hash::V3Hash(const string& name) {
|
||||
|
||||
void AstNode::checkTreeIter(AstNode* backp) {
|
||||
// private: Check a tree and children
|
||||
if (backp != this->backp()) {
|
||||
this->v3fatalSrc("Back node inconsistent");
|
||||
}
|
||||
UASSERT_OBJ(backp == this->backp(), this, "Back node inconsistent");
|
||||
if (VN_IS(this, NodeTermop) || VN_IS(this, NodeVarRef)) {
|
||||
// Termops have a short-circuited iterateChildren, so check usage
|
||||
if (op1p()||op2p()||op3p()||op4p())
|
||||
this->v3fatalSrc("Terminal operation with non-terminals");
|
||||
UASSERT_OBJ(!(op1p()||op2p()||op3p()||op4p()), this,
|
||||
"Terminal operation with non-terminals");
|
||||
}
|
||||
if (m_op1p) m_op1p->checkTreeIterList(this);
|
||||
if (m_op2p) m_op2p->checkTreeIterList(this);
|
||||
@@ -955,12 +957,13 @@ void AstNode::checkTreeIterList(AstNode* backp) {
|
||||
AstNode* tailp = this;
|
||||
for (AstNode* nodep=headp; nodep; nodep=nodep->nextp()) {
|
||||
nodep->checkTreeIter(backp);
|
||||
if (headp!=this && nextp()) this->v3fatalSrc("Headtailp should be null in middle of lists");
|
||||
UASSERT_OBJ(headp==this || !nextp(), this,
|
||||
"Headtailp should be null in middle of lists");
|
||||
tailp = nodep;
|
||||
backp = nodep;
|
||||
}
|
||||
if (headp->m_headtailp != tailp) headp->v3fatalSrc("Tail in headtailp is inconsistent");
|
||||
if (tailp->m_headtailp != headp) tailp->v3fatalSrc("Head in headtailp is inconsistent");
|
||||
UASSERT_OBJ(headp->m_headtailp == tailp, headp, "Tail in headtailp is inconsistent");
|
||||
UASSERT_OBJ(tailp->m_headtailp == headp, tailp, "Head in headtailp is inconsistent");
|
||||
}
|
||||
|
||||
void AstNode::checkTree() {
|
||||
@@ -973,21 +976,21 @@ void AstNode::checkTree() {
|
||||
}
|
||||
}
|
||||
|
||||
void AstNode::dumpGdb() { // For GDB only
|
||||
dumpGdbHeader();
|
||||
cout<<" "; dump(cout); cout<<endl;
|
||||
void AstNode::dumpGdb() { // For GDB only // LCOV_EXCL_LINE
|
||||
dumpGdbHeader(); // LCOV_EXCL_LINE
|
||||
cout<<" "; dump(cout); cout<<endl; // LCOV_EXCL_LINE
|
||||
}
|
||||
void AstNode::dumpGdbHeader() const { // For GDB only
|
||||
dumpPtrs(cout);
|
||||
cout<<" Fileline = "<<fileline()<<endl;
|
||||
void AstNode::dumpGdbHeader() const { // For GDB only // LCOV_EXCL_LINE
|
||||
dumpPtrs(cout); // LCOV_EXCL_LINE
|
||||
cout<<" Fileline = "<<fileline()<<endl; // LCOV_EXCL_LINE
|
||||
}
|
||||
void AstNode::dumpTreeGdb() { // For GDB only
|
||||
dumpGdbHeader();
|
||||
dumpTree(cout);
|
||||
void AstNode::dumpTreeGdb() { // For GDB only // LCOV_EXCL_LINE
|
||||
dumpGdbHeader(); // LCOV_EXCL_LINE
|
||||
dumpTree(cout); // LCOV_EXCL_LINE
|
||||
}
|
||||
void AstNode::dumpTreeFileGdb(const char* filenamep) { // For GDB only
|
||||
string filename = filenamep ? filenamep : v3Global.debugFilename("debug.tree", 98);
|
||||
v3Global.rootp()->dumpTreeFile(filename);
|
||||
void AstNode::dumpTreeFileGdb(const char* filenamep) { // For GDB only // LCOV_EXCL_LINE
|
||||
string filename = filenamep ? filenamep : v3Global.debugFilename("debug.tree", 98); // LCOV_EXCL_LINE
|
||||
v3Global.rootp()->dumpTreeFile(filename); // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
void AstNode::checkIter() const {
|
||||
@@ -1021,8 +1024,12 @@ void AstNode::dumpPtrs(std::ostream& os) const {
|
||||
}
|
||||
|
||||
void AstNode::dumpTree(std::ostream& os, const string& indent, int maxDepth) {
|
||||
static int s_debugFileline = v3Global.opt.debugSrcLevel("fileline"); // --debugi-fileline 9
|
||||
os<<indent<<" "<<this<<endl;
|
||||
if (debug()>8) { os<<indent<<" "; dumpPtrs(os); }
|
||||
if (s_debugFileline >= 9) {
|
||||
os<<fileline()->warnContextSecondary();
|
||||
}
|
||||
if (maxDepth==1) {
|
||||
if (op1p()||op2p()||op3p()||op4p()) { os<<indent<<"1: ...(maxDepth)"<<endl; }
|
||||
} else {
|
||||
@@ -1076,9 +1083,43 @@ void AstNode::v3errorEndFatal(std::ostringstream& str) const {
|
||||
v3errorEnd(str); assert(0); VL_UNREACHABLE
|
||||
}
|
||||
|
||||
string AstNode::locationStr() const {
|
||||
string str = "... In instance ";
|
||||
const AstNode* backp = this;
|
||||
while (backp) {
|
||||
const AstScope* scopep;
|
||||
if ((scopep = VN_CAST_CONST(backp, Scope))) {
|
||||
// The design is flattened and there are no useful scopes
|
||||
// This is probably because of inilining
|
||||
if (scopep->isTop()) break;
|
||||
|
||||
str += scopep->prettyName();
|
||||
return str;
|
||||
}
|
||||
backp = backp->backp();
|
||||
}
|
||||
backp = this;
|
||||
while (backp) {
|
||||
const AstModule* modp;
|
||||
const AstNodeVarRef* nvrp;
|
||||
if ((modp = VN_CAST_CONST(backp, Module)) && !modp->hierName().empty()) {
|
||||
str += modp->hierName();
|
||||
return str;
|
||||
} else if ((nvrp = VN_CAST_CONST(backp, NodeVarRef))) {
|
||||
string prettyName = nvrp->prettyName();
|
||||
// VarRefs have not been flattened yet and do not contain location information
|
||||
if (prettyName != nvrp->name()) {
|
||||
str += prettyName;
|
||||
return str;
|
||||
}
|
||||
}
|
||||
backp = backp->backp();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
void AstNode::v3errorEnd(std::ostringstream& str) const {
|
||||
if (!m_fileline) {
|
||||
V3Error::v3errorEnd(str);
|
||||
V3Error::v3errorEnd(str, locationStr());
|
||||
} else {
|
||||
std::ostringstream nsstr;
|
||||
nsstr<<str.str();
|
||||
@@ -1088,31 +1129,28 @@ void AstNode::v3errorEnd(std::ostringstream& str) const {
|
||||
const_cast<AstNode*>(this)->dump(nsstr);
|
||||
nsstr<<endl;
|
||||
}
|
||||
m_fileline->v3errorEnd(nsstr);
|
||||
m_fileline->v3errorEnd(nsstr, locationStr());
|
||||
}
|
||||
}
|
||||
|
||||
string AstNode::warnMore() const {
|
||||
return fileline()->warnMore();
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Data type conversion
|
||||
|
||||
void AstNode::dtypeChgSigned(bool flag) {
|
||||
if (!dtypep()) this->v3fatalSrc("No dtype when changing to (un)signed");
|
||||
UASSERT_OBJ(dtypep(), this, "No dtype when changing to (un)signed");
|
||||
dtypeChgWidthSigned(dtypep()->width(), dtypep()->widthMin(),
|
||||
flag ? AstNumeric::SIGNED : AstNumeric::UNSIGNED);
|
||||
AstNumeric::fromBool(flag));
|
||||
}
|
||||
void AstNode::dtypeChgWidth(int width, int widthMin) {
|
||||
if (!dtypep()) this->v3fatalSrc("No dtype when changing width"); // Use ChgWidthSigned(...UNSIGNED) otherwise
|
||||
UASSERT_OBJ(dtypep(), this,
|
||||
"No dtype when changing width"); // Use ChgWidthSigned(...UNSIGNED) otherwise
|
||||
dtypeChgWidthSigned(width, widthMin, dtypep()->numeric());
|
||||
}
|
||||
|
||||
void AstNode::dtypeChgWidthSigned(int width, int widthMin, AstNumeric numeric) {
|
||||
if (!dtypep()) {
|
||||
// We allow dtypep() to be null, as before/during widthing dtypes are not resolved
|
||||
dtypeSetLogicSized(width, widthMin, numeric);
|
||||
dtypeSetLogicUnsized(width, widthMin, numeric);
|
||||
} else {
|
||||
if (width==dtypep()->width()
|
||||
&& widthMin==dtypep()->widthMin()
|
||||
@@ -1122,7 +1160,7 @@ void AstNode::dtypeChgWidthSigned(int width, int widthMin, AstNumeric numeric) {
|
||||
// work OK but this assumption may break in the future.
|
||||
// Note we can't just clone and do a widthForce, as if it's a BasicDType
|
||||
// the msb() indications etc will be incorrect.
|
||||
dtypeSetLogicSized(width, widthMin, numeric);
|
||||
dtypeSetLogicUnsized(width, widthMin, numeric);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+32
-8
@@ -51,16 +51,19 @@ typedef std::set<int> MTaskIdSet; // Set of mtaskIds for Var sorting
|
||||
//######################################################################
|
||||
|
||||
// For broken() function, return error string if have a match
|
||||
#define BROKEN_RTN(test) do { if (VL_UNLIKELY(test)) return # test; } while(0)
|
||||
#define BROKEN_RTN(test) do { if (VL_UNCOVERABLE(test)) return # test; } while(0)
|
||||
|
||||
// (V)erilator (N)ode is: True if AstNode is of a a given AstType
|
||||
#define VN_IS(nodep,nodetypename) (AstNode::privateIs ## nodetypename(nodep))
|
||||
|
||||
// (V)erilator (N)ode cast: Cast to given type if can; effectively
|
||||
// dynamic_cast(nodep)(nodetypename)
|
||||
// dynamic_cast<nodetypename>(nodep)
|
||||
#define VN_CAST(nodep,nodetypename) (AstNode::privateCast ## nodetypename(nodep))
|
||||
#define VN_CAST_CONST(nodep,nodetypename) (AstNode::privateConstCast ## nodetypename(nodep) )
|
||||
|
||||
// (V)erilator (N)ode deleted: Reference to deleted child (for assertions only)
|
||||
#define VN_DELETED(nodep) VL_UNLIKELY((vluint64_t)(nodep) == 0x1)
|
||||
|
||||
//######################################################################
|
||||
|
||||
class AstType {
|
||||
@@ -1114,6 +1117,14 @@ public:
|
||||
};
|
||||
std::ostream& operator<<(std::ostream& os, const V3Hash& rhs);
|
||||
|
||||
//######################################################################
|
||||
// Callback base class to determine if node matches some formula
|
||||
|
||||
class VNodeMatcher {
|
||||
public:
|
||||
virtual bool nodeMatch(const AstNode* nodep) const { return true; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// AstNode -- Base type of all Ast types
|
||||
|
||||
@@ -1180,6 +1191,7 @@ private:
|
||||
bool ignNext, bool gateOnly);
|
||||
void deleteTreeIter();
|
||||
void deleteNode();
|
||||
string locationStr() const;
|
||||
public:
|
||||
static void relinkOneLink(AstNode*& pointpr, AstNode* newp);
|
||||
// cppcheck-suppress functionConst
|
||||
@@ -1264,10 +1276,13 @@ public:
|
||||
string shortName() const; // Name with __PVT__ removed for concatenating scopes
|
||||
static string dedotName(const string& namein); // Name with dots removed
|
||||
static string prettyName(const string& namein); // Name for printing out to the user
|
||||
static string prettyNameQ(const string& namein) { // Quoted pretty name (for errors)
|
||||
return string("'")+prettyName(namein)+"'"; }
|
||||
static string encodeName(const string& namein); // Encode user name into internal C representation
|
||||
static string encodeNumber(vlsint64_t num); // Encode number into internal C representation
|
||||
static string vcdName(const string& namein); // Name for printing out to vcd files
|
||||
string prettyName() const { return prettyName(name()); }
|
||||
string prettyNameQ() const { return prettyNameQ(name()); }
|
||||
string prettyTypeName() const; // "VARREF" for error messages
|
||||
virtual string prettyOperatorName() const { return "operator "+prettyTypeName(); }
|
||||
FileLine* fileline() const { return m_fileline; }
|
||||
@@ -1381,10 +1396,12 @@ public:
|
||||
void dtypeChgSigned(bool flag=true);
|
||||
void dtypeChgWidth(int width, int widthMin);
|
||||
void dtypeChgWidthSigned(int width, int widthMin, AstNumeric numeric);
|
||||
void dtypeSetBitSized(int width, int widthMin, AstNumeric numeric) {
|
||||
void dtypeSetBitUnsized(int width, int widthMin, AstNumeric numeric) {
|
||||
dtypep(findBitDType(width, widthMin, numeric)); }
|
||||
void dtypeSetLogicSized(int width, int widthMin, AstNumeric numeric) {
|
||||
void dtypeSetLogicUnsized(int width, int widthMin, AstNumeric numeric) {
|
||||
dtypep(findLogicDType(width, widthMin, numeric)); }
|
||||
void dtypeSetLogicSized(int width, AstNumeric numeric) {
|
||||
dtypep(findLogicDType(width, width, numeric)); } // Since sized, widthMin is width
|
||||
void dtypeSetLogicBool() { dtypep(findLogicBoolDType()); }
|
||||
void dtypeSetDouble() { dtypep(findDoubleDType()); }
|
||||
void dtypeSetString() { dtypep(findStringDType()); }
|
||||
@@ -1408,7 +1425,11 @@ public:
|
||||
// METHODS - dump and error
|
||||
void v3errorEnd(std::ostringstream& str) const;
|
||||
void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN;
|
||||
string warnMore() const;
|
||||
string warnContextPrimary() const { return fileline()->warnContextPrimary(); }
|
||||
string warnContextSecondary() const { return fileline()->warnContextSecondary(); }
|
||||
string warnMore() const { return fileline()->warnMore(); }
|
||||
string warnOther() const { return fileline()->warnOther(); }
|
||||
|
||||
virtual void dump(std::ostream& str=std::cout);
|
||||
void dumpGdb(); // For GDB only
|
||||
void dumpGdbHeader() const;
|
||||
@@ -1884,7 +1905,7 @@ public:
|
||||
//
|
||||
// Changing the width may confuse the data type resolution, so must clear
|
||||
// TypeTable cache after use.
|
||||
void widthForce(int width, int sized) { m_width = width; m_widthMin = sized; }
|
||||
void widthForce(int width, int widthMin) { m_width = width; m_widthMin = widthMin; }
|
||||
// For backward compatibility inherit width and signing from the subDType/base type
|
||||
void widthFromSub(AstNodeDType* nodep) { m_width = nodep->m_width; m_widthMin = nodep->m_widthMin; m_numeric = nodep->m_numeric; }
|
||||
//
|
||||
@@ -1923,7 +1944,7 @@ public:
|
||||
// AstNumeric::NOSIGN overloaded to indicate not packed
|
||||
m_packed = (numericUnpack != AstNumeric::NOSIGN);
|
||||
m_isFourstate = false; // V3Width computes
|
||||
numeric(numericUnpack.isSigned() ? AstNumeric::SIGNED : AstNumeric::UNSIGNED);
|
||||
numeric(AstNumeric::fromBool(numericUnpack.isSigned()));
|
||||
}
|
||||
ASTNODE_BASE_FUNCS(NodeClassDType)
|
||||
virtual const char* broken() const;
|
||||
@@ -2036,7 +2057,7 @@ class AstNodeStream : public AstNodeBiop {
|
||||
public:
|
||||
AstNodeStream(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {
|
||||
if (lhsp->dtypep()) {
|
||||
dtypeSetLogicSized(lhsp->dtypep()->width(), lhsp->dtypep()->width(), AstNumeric::UNSIGNED);
|
||||
dtypeSetLogicSized(lhsp->dtypep()->width(), AstNumeric::UNSIGNED);
|
||||
}
|
||||
}
|
||||
ASTNODE_BASE_FUNCS(NodeStream)
|
||||
@@ -2168,6 +2189,7 @@ class AstNodeModule : public AstNode {
|
||||
private:
|
||||
string m_name; // Name of the module
|
||||
string m_origName; // Name of the module, ignoring name() changes, for dot lookup
|
||||
string m_hierName; // Hierachical name for errors, etc.
|
||||
bool m_modPublic:1; // Module has public references
|
||||
bool m_modTrace:1; // Tracing this module
|
||||
bool m_inLibrary:1; // From a library, no error if not used, never top level
|
||||
@@ -2198,6 +2220,8 @@ public:
|
||||
// ACCESSORS
|
||||
virtual void name(const string& name) { m_name = name; }
|
||||
string origName() const { return m_origName; }
|
||||
string hierName() const { return m_hierName; }
|
||||
void hierName(const string& hierName) { m_hierName = hierName; }
|
||||
bool inLibrary() const { return m_inLibrary; }
|
||||
void inLibrary(bool flag) { m_inLibrary = flag; }
|
||||
void level(int level) { m_level = level; }
|
||||
|
||||
+11
-47
@@ -65,7 +65,7 @@ void AstNodeClassDType::repairMemberCache() {
|
||||
clearCache();
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) {
|
||||
if (m_members.find(itemp->name())!=m_members.end()) {
|
||||
itemp->v3error("Duplicate declaration of member name: "<<itemp->prettyName()); }
|
||||
itemp->v3error("Duplicate declaration of member name: "<<itemp->prettyNameQ()); }
|
||||
else m_members.insert(make_pair(itemp->name(), itemp));
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ const char* AstNodeClassDType::broken() const {
|
||||
exists.insert(itemp);
|
||||
}
|
||||
for (MemberNameMap::const_iterator it=m_members.begin(); it!=m_members.end(); ++it) {
|
||||
if (VL_UNLIKELY(exists.find(it->second) == exists.end())) {
|
||||
if (VL_UNCOVERABLE(exists.find(it->second) == exists.end())) {
|
||||
this->v3error("Internal: Structure member broken: "<<it->first);
|
||||
return "member broken";
|
||||
}
|
||||
@@ -215,8 +215,8 @@ string AstVar::verilogKwd() const {
|
||||
}
|
||||
|
||||
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc) const {
|
||||
if (forReturn) named = false;
|
||||
if (forReturn) v3fatalSrc("verilator internal data is never passed as return, but as first argument");
|
||||
UASSERT_OBJ(!forReturn, this,
|
||||
"Internal data is never passed as return, but as first argument");
|
||||
string otype;
|
||||
AstBasicDType* bdtypep = basicp();
|
||||
bool strtype = bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::STRING;
|
||||
@@ -642,28 +642,28 @@ string AstScopeName::scopeNameFormatter(AstText* scopeTextp) const {
|
||||
}
|
||||
|
||||
bool AstSenTree::hasClocked() const {
|
||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
||||
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||
if (senp->isClocked()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasSettle() const {
|
||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
||||
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||
if (senp->isSettle()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasInitial() const {
|
||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
||||
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||
if (senp->isInitial()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasCombo() const {
|
||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
||||
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||
if (senp->isCombo()) return true;
|
||||
}
|
||||
@@ -676,12 +676,6 @@ void AstTypeTable::clearCache() {
|
||||
for (int i=0; i < static_cast<int>(AstBasicDTypeKwd::_ENUM_MAX); ++i) {
|
||||
m_basicps[i] = NULL;
|
||||
}
|
||||
for (int isbit=0; isbit<_IDX0_MAX; ++isbit) {
|
||||
for (int numer=0; numer<AstNumeric::_ENUM_MAX; ++numer) {
|
||||
LogicMap& mapr = m_logicMap[isbit][numer];
|
||||
mapr.clear();
|
||||
}
|
||||
}
|
||||
m_detailedMap.clear();
|
||||
// Clear generic()'s so dead detection will work
|
||||
for (AstNode* nodep = typesp(); nodep; nodep=nodep->nextp()) {
|
||||
@@ -718,24 +712,10 @@ AstBasicDType* AstTypeTable::findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd)
|
||||
|
||||
AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd,
|
||||
int width, int widthMin, AstNumeric numeric) {
|
||||
int idx = IDX0_LOGIC;
|
||||
if (kwd == AstBasicDTypeKwd::LOGIC) idx = IDX0_LOGIC;
|
||||
else if (kwd == AstBasicDTypeKwd::BIT) idx = IDX0_BIT;
|
||||
else fl->v3fatalSrc("Bad kwd for findLogicBitDType");
|
||||
std::pair<int,int> widths = make_pair(width, widthMin);
|
||||
LogicMap& mapr = m_logicMap[idx][static_cast<int>(numeric)];
|
||||
LogicMap::const_iterator it = mapr.find(widths);
|
||||
if (it != mapr.end()) return it->second;
|
||||
//
|
||||
AstBasicDType* new1p = new AstBasicDType(fl, kwd, numeric, width, widthMin);
|
||||
// Because the detailed map doesn't update this map,
|
||||
// check the detailed map for this same node, and if found update this map
|
||||
// Also adds this new node to the detailed map
|
||||
AstBasicDType* newp = findInsertSameDType(new1p);
|
||||
if (newp != new1p) new1p->deleteTree();
|
||||
else addTypesp(newp);
|
||||
//
|
||||
mapr.insert(make_pair(widths, newp));
|
||||
return newp;
|
||||
}
|
||||
|
||||
@@ -764,12 +744,12 @@ AstBasicDType* AstTypeTable::findInsertSameDType(AstBasicDType* nodep) {
|
||||
// Special walking tree inserters
|
||||
|
||||
void AstNode::addBeforeStmt(AstNode* newp, AstNode*) {
|
||||
if (!backp()) newp->v3fatalSrc("Can't find current statement to addBeforeStmt");
|
||||
UASSERT_OBJ(backp(), newp, "Can't find current statement to addBeforeStmt");
|
||||
// Look up; virtual call will find where to put it
|
||||
this->backp()->addBeforeStmt(newp, this);
|
||||
}
|
||||
void AstNode::addNextStmt(AstNode* newp, AstNode*) {
|
||||
if (!backp()) newp->v3fatalSrc("Can't find current statement to addNextStmt");
|
||||
UASSERT_OBJ(backp(), newp, "Can't find current statement to addNextStmt");
|
||||
// Look up; virtual call will find where to put it
|
||||
this->backp()->addNextStmt(newp, this);
|
||||
}
|
||||
@@ -832,7 +812,7 @@ void AstNode::dump(std::ostream& str) {
|
||||
//<<" "<<cvtToHex(this)->m_backp
|
||||
<<" <e"<<std::dec<<editCount()
|
||||
<<((editCount()>=editCountLast())?"#>":">")
|
||||
<<" {"<<fileline()->filenameLetters()<<std::dec<<fileline()->lineno()<<"}";
|
||||
<<" {"<<fileline()->filenameLetters()<<std::dec<<fileline()->lastLineno()<<"}";
|
||||
if (user1p()) str<<" u1="<<cvtToHex(user1p());
|
||||
if (user2p()) str<<" u2="<<cvtToHex(user2p());
|
||||
if (user3p()) str<<" u3="<<cvtToHex(user3p());
|
||||
@@ -1030,22 +1010,6 @@ void AstTypeTable::dump(std::ostream& str) {
|
||||
subnodep->dump(str);
|
||||
}
|
||||
}
|
||||
for (int isbit=0; isbit<2; ++isbit) {
|
||||
for (int issigned=0; issigned<AstNumeric::_ENUM_MAX; ++issigned) {
|
||||
LogicMap& mapr = m_logicMap[isbit][issigned];
|
||||
for (LogicMap::const_iterator it = mapr.begin(); it != mapr.end(); ++it) {
|
||||
AstBasicDType* dtypep = it->second;
|
||||
str<<endl; // Newline from caller, so newline first
|
||||
std::stringstream nsstr;
|
||||
nsstr<<(isbit?"bw":"lw")
|
||||
<<it->first.first<<"/"<<it->first.second;
|
||||
str<<"\t\t"<<std::setw(8)<<nsstr.str();
|
||||
if (issigned) str<<" s"; else str<<" u";
|
||||
str<<" -> ";
|
||||
dtypep->dump(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
DetailedMap& mapr = m_detailedMap;
|
||||
for (DetailedMap::const_iterator it = mapr.begin(); it != mapr.end(); ++it) {
|
||||
|
||||
+91
-68
@@ -55,10 +55,10 @@ private:
|
||||
} else if (m_num.isString()) {
|
||||
dtypeSetString();
|
||||
} else {
|
||||
dtypeSetLogicSized(m_num.width(), (m_num.sized() ? 0 : m_num.widthMin()),
|
||||
m_num.isSigned() ? AstNumeric::SIGNED
|
||||
: AstNumeric::UNSIGNED);
|
||||
dtypeSetLogicUnsized(m_num.width(), (m_num.sized() ? 0 : m_num.widthMin()),
|
||||
AstNumeric::fromBool(m_num.isSigned()));
|
||||
}
|
||||
m_num.nodep(this);
|
||||
}
|
||||
public:
|
||||
AstConst(FileLine* fl, const V3Number& num)
|
||||
@@ -72,6 +72,12 @@ public:
|
||||
, m_num(this, width, value) {
|
||||
initWithNumber();
|
||||
}
|
||||
class DtypedValue{}; // for creator type-overload selection
|
||||
AstConst(FileLine* fl, DtypedValue, AstNodeDType* nodedtypep, uint32_t value)
|
||||
: AstNodeMath(fl)
|
||||
, m_num(this, nodedtypep->width(), value, nodedtypep->widthSized()) {
|
||||
initWithNumber();
|
||||
}
|
||||
class StringToParse {}; // for creator type-overload selection
|
||||
AstConst(FileLine* fl, StringToParse, const char* sourcep)
|
||||
: AstNodeMath(fl)
|
||||
@@ -86,44 +92,43 @@ public:
|
||||
}
|
||||
AstConst(FileLine* fl, uint32_t num)
|
||||
: AstNodeMath(fl)
|
||||
, m_num(V3Number(this, 32, num)) {
|
||||
dtypeSetLogicSized(m_num.width(),
|
||||
m_num.sized() ? 0 : m_num.widthMin(),
|
||||
AstNumeric::UNSIGNED);
|
||||
, m_num(this, 32, num) {
|
||||
dtypeSetLogicUnsized(m_num.width(), 0, AstNumeric::UNSIGNED);
|
||||
}
|
||||
class Unsized32 {}; // for creator type-overload selection
|
||||
AstConst(FileLine* fl, Unsized32, uint32_t num) // Unsized 32-bit integer of specified value
|
||||
: AstNodeMath(fl)
|
||||
, m_num(V3Number(this, 32, num)) {
|
||||
, m_num(this, 32, num) {
|
||||
m_num.width(32, false);
|
||||
dtypeSetLogicSized(32, m_num.widthMin(), AstNumeric::UNSIGNED);
|
||||
dtypeSetLogicUnsized(32, m_num.widthMin(), AstNumeric::UNSIGNED);
|
||||
}
|
||||
class Signed32 {}; // for creator type-overload selection
|
||||
AstConst(FileLine* fl, Signed32, int32_t num) // Signed 32-bit integer of specified value
|
||||
: AstNodeMath(fl)
|
||||
, m_num(V3Number(this, 32, num)) {
|
||||
, m_num(this, 32, num) {
|
||||
m_num.width(32, 32);
|
||||
dtypeSetLogicSized(32, m_num.widthMin(), AstNumeric::SIGNED);
|
||||
dtypeSetLogicUnsized(32, m_num.widthMin(), AstNumeric::SIGNED);
|
||||
}
|
||||
class RealDouble {}; // for creator type-overload selection
|
||||
AstConst(FileLine* fl, RealDouble, double num)
|
||||
: AstNodeMath(fl)
|
||||
, m_num(V3Number(this, 64)) { m_num.setDouble(num); dtypeSetDouble(); }
|
||||
, m_num(this, 64) { m_num.setDouble(num); dtypeSetDouble(); }
|
||||
class String {}; // for creator type-overload selection
|
||||
AstConst(FileLine* fl, String, const string& num)
|
||||
: AstNodeMath(fl)
|
||||
, m_num(V3Number(V3Number::String(), this, num)) { dtypeSetString(); }
|
||||
, m_num(V3Number::String(), this, num) { dtypeSetString(); }
|
||||
class LogicFalse {};
|
||||
AstConst(FileLine* fl, LogicFalse) // Shorthand const 0, dtype should be a logic of size 1
|
||||
: AstNodeMath(fl)
|
||||
, m_num(V3Number(this, 1, 0)) { dtypeSetLogicBool(); }
|
||||
, m_num(this, 1, 0) { dtypeSetLogicBool(); }
|
||||
class LogicTrue {};
|
||||
AstConst(FileLine* fl, LogicTrue) // Shorthand const 1, dtype should be a logic of size 1
|
||||
: AstNodeMath(fl)
|
||||
, m_num(V3Number(this, 1, 1)) { dtypeSetLogicBool(); }
|
||||
, m_num(this, 1, 1) { dtypeSetLogicBool(); }
|
||||
ASTNODE_NODE_FUNCS(Const)
|
||||
virtual string name() const { return num().ascii(); } // * = Value
|
||||
virtual const V3Number& num() const { return m_num; } // * = Value
|
||||
const V3Number& num() const { return m_num; } // * = Value
|
||||
V3Number& num() { return m_num; } // * = Value
|
||||
uint32_t toUInt() const { return num().toUInt(); }
|
||||
vlsint32_t toSInt() const { return num().toSInt(); }
|
||||
vluint64_t toUQuad() const { return num().toUQuad(); }
|
||||
@@ -588,6 +593,7 @@ public:
|
||||
class AstIfaceRefDType : public AstNodeDType {
|
||||
// Reference to an interface, either for a port, or inside parent cell
|
||||
private:
|
||||
FileLine* m_modportFileline; // Where modport token was
|
||||
string m_cellName; // "" = no cell, such as when connects to 'input' iface
|
||||
string m_ifaceName; // Interface name
|
||||
string m_modportName; // "" = no modport
|
||||
@@ -595,11 +601,15 @@ private:
|
||||
AstCell* m_cellp; // When exact parent cell known; not a guess
|
||||
AstModport* m_modportp; // NULL = unlinked or no modport
|
||||
public:
|
||||
AstIfaceRefDType(FileLine* fl, const string& cellName, const string& ifaceName)
|
||||
: AstNodeDType(fl), m_cellName(cellName), m_ifaceName(ifaceName), m_modportName(""),
|
||||
AstIfaceRefDType(FileLine* fl,
|
||||
const string& cellName, const string& ifaceName)
|
||||
: AstNodeDType(fl), m_modportFileline(NULL),
|
||||
m_cellName(cellName), m_ifaceName(ifaceName), m_modportName(""),
|
||||
m_ifacep(NULL), m_cellp(NULL), m_modportp(NULL) { }
|
||||
AstIfaceRefDType(FileLine* fl, const string& cellName, const string& ifaceName, const string& modport)
|
||||
: AstNodeDType(fl), m_cellName(cellName), m_ifaceName(ifaceName), m_modportName(modport),
|
||||
AstIfaceRefDType(FileLine* fl, FileLine* modportFl,
|
||||
const string& cellName, const string& ifaceName, const string& modport)
|
||||
: AstNodeDType(fl), m_modportFileline(modportFl),
|
||||
m_cellName(cellName), m_ifaceName(ifaceName), m_modportName(modport),
|
||||
m_ifacep(NULL), m_cellp(NULL), m_modportp(NULL) { }
|
||||
ASTNODE_NODE_FUNCS(IfaceRefDType)
|
||||
// METHODS
|
||||
@@ -615,6 +625,7 @@ public:
|
||||
virtual V3Hash sameHash() const { return V3Hash(m_cellp); }
|
||||
virtual int widthAlignBytes() const { return 1; }
|
||||
virtual int widthTotalBytes() const { return 1; }
|
||||
FileLine* modportFileline() const { return m_modportFileline; }
|
||||
string cellName() const { return m_cellName; }
|
||||
void cellName(const string& name) { m_cellName = name; }
|
||||
string ifaceName() const { return m_ifaceName; }
|
||||
@@ -953,8 +964,8 @@ class AstSelBit : public AstNodePreSel {
|
||||
public:
|
||||
AstSelBit(FileLine* fl, AstNode* fromp, AstNode* bitp)
|
||||
:AstNodePreSel(fl, fromp, bitp, NULL) {
|
||||
if (v3Global.assertDTypesResolved()) { v3fatalSrc("not coded to create after dtypes resolved"); }
|
||||
}
|
||||
UASSERT_OBJ(!v3Global.assertDTypesResolved(), this,
|
||||
"not coded to create after dtypes resolved"); }
|
||||
ASTNODE_NODE_FUNCS(SelBit)
|
||||
AstNode* bitp() const { return rhsp(); }
|
||||
};
|
||||
@@ -996,7 +1007,6 @@ public:
|
||||
m_declElWidth = 1;
|
||||
if (VN_IS(widthp, Const)) {
|
||||
dtypeSetLogicSized(VN_CAST(widthp, Const)->toUInt(),
|
||||
VN_CAST(widthp, Const)->toUInt(),
|
||||
AstNumeric::UNSIGNED);
|
||||
}
|
||||
}
|
||||
@@ -1004,7 +1014,7 @@ public:
|
||||
: AstNodeTriop(fl, fromp,
|
||||
new AstConst(fl, lsb), new AstConst(fl, bitwidth)) {
|
||||
m_declElWidth = 1;
|
||||
dtypeSetLogicSized(bitwidth, bitwidth, AstNumeric::UNSIGNED);
|
||||
dtypeSetLogicSized(bitwidth, AstNumeric::UNSIGNED);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Sel)
|
||||
virtual void dump(std::ostream& str);
|
||||
@@ -1139,6 +1149,8 @@ private:
|
||||
VDirection m_direction; // Direction input/output etc
|
||||
VDirection m_declDirection; // Declared direction input/output etc
|
||||
AstBasicDTypeKwd m_declKwd; // Keyword at declaration time
|
||||
bool m_ansi:1; // ANSI port list variable (for dedup check)
|
||||
bool m_declTyped:1; // Declared as type (for dedup check)
|
||||
bool m_tristate:1; // Inout or triwire or trireg
|
||||
bool m_primaryIO:1; // In/out to top level (or directly assigned from same)
|
||||
bool m_sc:1; // SystemC variable
|
||||
@@ -1171,6 +1183,7 @@ private:
|
||||
MTaskIdSet m_mtaskIds; // MTaskID's that read or write this var
|
||||
|
||||
void init() {
|
||||
m_ansi = false; m_declTyped = false;
|
||||
m_tristate = false; m_primaryIO = false;
|
||||
m_sc = false; m_scClocked = false; m_scSensitive = false;
|
||||
m_usedClock = false; m_usedParam = false; m_usedLoopIdx = false;
|
||||
@@ -1211,7 +1224,7 @@ public:
|
||||
, m_name(name), m_origName(name) {
|
||||
init();
|
||||
combineType(type);
|
||||
dtypeSetLogicSized(wantwidth, wantwidth, AstNumeric::UNSIGNED);
|
||||
dtypeSetLogicSized(wantwidth, AstNumeric::UNSIGNED);
|
||||
m_declKwd = AstBasicDTypeKwd::LOGIC;
|
||||
}
|
||||
AstVar(FileLine* fl, AstVarType type, const string& name, VFlagBitPacked, int wantwidth)
|
||||
@@ -1219,7 +1232,7 @@ public:
|
||||
, m_name(name), m_origName(name) {
|
||||
init();
|
||||
combineType(type);
|
||||
dtypeSetLogicSized(wantwidth, wantwidth, AstNumeric::UNSIGNED);
|
||||
dtypeSetLogicSized(wantwidth, AstNumeric::UNSIGNED);
|
||||
m_declKwd = AstBasicDTypeKwd::BIT;
|
||||
}
|
||||
AstVar(FileLine* fl, AstVarType type, const string& name, AstVar* examplep)
|
||||
@@ -1273,6 +1286,8 @@ public:
|
||||
AstNode* attrsp() const { return op4p(); } // op4 = Attributes during early parse
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
|
||||
void ansi(bool flag) { m_ansi = flag; }
|
||||
void declTyped(bool flag) { m_declTyped = flag; }
|
||||
void attrClockEn(bool flag) { m_attrClockEn = flag; }
|
||||
void attrClocker(AstVarAttrClocker flag) { m_attrClocker = flag; }
|
||||
void attrFileDescr(bool flag) { m_fileDescr = flag; }
|
||||
@@ -1306,6 +1321,8 @@ public:
|
||||
virtual void name(const string& name) { m_name = name; }
|
||||
virtual void tag(const string& text) { m_tag = text;}
|
||||
virtual string tag() const { return m_tag; }
|
||||
bool isAnsi() const { return m_ansi; }
|
||||
bool isDeclTyped() const { return m_declTyped; }
|
||||
bool isInoutish() const { return m_direction.isInoutish(); }
|
||||
bool isNonOutput() const { return m_direction.isNonOutput(); }
|
||||
bool isReadOnly() const { return m_direction.isReadOnly(); }
|
||||
@@ -1629,7 +1646,7 @@ public:
|
||||
return modVarp() ? ((modVarp()->direction().isAny()
|
||||
? modVarp()->direction().prettyName()+" "
|
||||
: "")
|
||||
+"port connection '"+modVarp()->prettyName()+"'")
|
||||
+"port connection "+modVarp()->prettyNameQ())
|
||||
: "port connection"; }
|
||||
bool dotStar() const { return name() == ".*"; } // Fake name for .* connections until linked
|
||||
int pinNum() const { return m_pinNum; }
|
||||
@@ -1815,6 +1832,7 @@ public:
|
||||
class AstCell : public AstNode {
|
||||
// A instantiation cell or interface call (don't know which until link)
|
||||
private:
|
||||
FileLine* m_modNameFileline; // Where module the cell instances token was
|
||||
string m_name; // Cell name
|
||||
string m_origName; // Original name before dot addition
|
||||
string m_modName; // Module the cell instances
|
||||
@@ -1823,9 +1841,11 @@ private:
|
||||
bool m_recursive:1; // Self-recursive module
|
||||
bool m_trace:1; // Trace this cell
|
||||
public:
|
||||
AstCell(FileLine* fl, const string& instName, const string& modName,
|
||||
AstCell(FileLine* fl, FileLine* mfl,
|
||||
const string& instName, const string& modName,
|
||||
AstPin* pinsp, AstPin* paramsp, AstRange* rangep)
|
||||
: AstNode(fl)
|
||||
, m_modNameFileline(mfl)
|
||||
, m_name(instName), m_origName(instName), m_modName(modName)
|
||||
, m_modp(NULL), m_hasIfaceVar(false), m_recursive(false), m_trace(true) {
|
||||
addNOp1p(pinsp); addNOp2p(paramsp); setNOp3p(rangep); }
|
||||
@@ -1841,6 +1861,7 @@ public:
|
||||
void origName(const string& name) { m_origName = name; }
|
||||
string modName() const { return m_modName; } // * = Instance name
|
||||
void modName(const string& name) { m_modName = name; }
|
||||
FileLine* modNameFileline() const { return m_modNameFileline; }
|
||||
AstPin* pinsp() const { return VN_CAST(op1p(), Pin); } // op1 = List of cell ports
|
||||
AstPin* paramsp() const { return VN_CAST(op2p(), Pin); } // op2 = List of parameter #(##) values
|
||||
AstRange* rangep() const { return VN_CAST(op3p(), Range); } // op3 = Range of arrayed instants (NULL=not ranged)
|
||||
@@ -1934,7 +1955,7 @@ public:
|
||||
AstBind(FileLine* fl, const string& name, AstNode* cellsp)
|
||||
: AstNode(fl)
|
||||
, m_name(name) {
|
||||
if (!VN_IS(cellsp, Cell)) cellsp->v3fatalSrc("Only cells allowed to be bound");
|
||||
UASSERT_OBJ(VN_IS(cellsp, Cell), cellsp, "Only cells allowed to be bound");
|
||||
addNOp1p(cellsp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Bind)
|
||||
@@ -2659,6 +2680,35 @@ public:
|
||||
void filep(AstNodeVarRef* nodep) { setNOp3p(nodep); }
|
||||
};
|
||||
|
||||
class AstElabDisplay : public AstNode {
|
||||
// Parents: stmtlist
|
||||
// Children: SFORMATF to generate print string
|
||||
private:
|
||||
AstDisplayType m_displayType;
|
||||
public:
|
||||
AstElabDisplay(FileLine* fileline, AstDisplayType dispType, AstNode* exprsp)
|
||||
: AstNode(fileline) {
|
||||
setOp1p(new AstSFormatF(fileline, AstSFormatF::NoFormat(), exprsp));
|
||||
m_displayType = dispType;
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(ElabDisplay)
|
||||
virtual const char* broken() const { BROKEN_RTN(!fmtp()); return NULL; }
|
||||
virtual string verilogKwd() const { return (string("$")+string(displayType().ascii())); }
|
||||
virtual bool isGateOptimizable() const { return false; }
|
||||
virtual bool isPredictOptimizable() const { return false; }
|
||||
virtual bool isPure() const { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
virtual bool isOutputter() const { return true; } // SPECIAL: $display makes output
|
||||
virtual bool isUnlikely() const { return true; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(displayType()); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return displayType()==static_cast<const AstElabDisplay*>(samep)->displayType(); }
|
||||
virtual int instrCount() const { return instrCountPli(); }
|
||||
AstDisplayType displayType() const { return m_displayType; }
|
||||
void displayType(AstDisplayType type) { m_displayType = type; }
|
||||
void fmtp(AstSFormatF* nodep) { addOp1p(nodep); } // op1 = To-String formatter
|
||||
AstSFormatF* fmtp() const { return VN_CAST(op1p(), SFormatF); }
|
||||
};
|
||||
|
||||
class AstSFormat : public AstNodeStmt {
|
||||
// Parents: statement container
|
||||
// Children: string to load
|
||||
@@ -3209,27 +3259,6 @@ public:
|
||||
AstJumpLabel* labelp() const { return m_labelp; }
|
||||
};
|
||||
|
||||
class AstUntilStable : public AstNodeStmt {
|
||||
// Quasi-while loop until given signals are stable
|
||||
// Parents: CFUNC (generally)
|
||||
// Children: VARREF, statements
|
||||
public:
|
||||
AstUntilStable(FileLine* fileline, AstVarRef* stablesp, AstNode* bodysp)
|
||||
: AstNodeStmt(fileline) {
|
||||
addNOp2p(stablesp); addNOp3p(bodysp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(UntilStable)
|
||||
AstVarRef* stablesp() const { return VN_CAST(op2p(), VarRef); } // op2 = list of variables that must become stable
|
||||
AstNode* bodysp() const { return op3p(); } // op3 = body of loop
|
||||
void addStablesp(AstVarRef* newp) { addOp2p(newp); }
|
||||
void addBodysp(AstNode* newp) { addOp3p(newp); }
|
||||
virtual bool isGateOptimizable() const { return false; } // Not relevant
|
||||
virtual bool isPredictOptimizable() const { return false; } // Not relevant
|
||||
virtual int instrCount() const { return instrCountBranch(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
};
|
||||
|
||||
class AstChangeXor : public AstNodeBiComAsv {
|
||||
// A comparison to determine change detection, common & must be fast.
|
||||
// Returns 32-bit or 64-bit value where 0 indicates no change.
|
||||
@@ -3843,7 +3872,7 @@ class AstExtend : public AstNodeUniop {
|
||||
public:
|
||||
AstExtend(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) {}
|
||||
AstExtend(FileLine* fl, AstNode* lhsp, int width) : AstNodeUniop(fl, lhsp) {
|
||||
dtypeSetLogicSized(width, width, AstNumeric::UNSIGNED); }
|
||||
dtypeSetLogicSized(width, AstNumeric::UNSIGNED); }
|
||||
ASTNODE_NODE_FUNCS(Extend)
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opAssign(lhs); }
|
||||
virtual string emitVerilog() { return "%l"; }
|
||||
@@ -3859,7 +3888,7 @@ public:
|
||||
AstExtendS(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) {}
|
||||
AstExtendS(FileLine* fl, AstNode* lhsp, int width) : AstNodeUniop(fl, lhsp) {
|
||||
// Important that widthMin be correct, as opExtend requires it after V3Expand
|
||||
dtypeSetLogicSized(width, width, AstNumeric::UNSIGNED); }
|
||||
dtypeSetLogicSized(width, AstNumeric::UNSIGNED); }
|
||||
ASTNODE_NODE_FUNCS(ExtendS)
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs) {
|
||||
out.opExtendS(lhs, lhsp()->widthMinV());
|
||||
@@ -3876,8 +3905,8 @@ class AstSigned : public AstNodeUniop {
|
||||
// $signed(lhs)
|
||||
public:
|
||||
AstSigned(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) {
|
||||
if (v3Global.assertDTypesResolved()) { v3fatalSrc("not coded to create after dtypes resolved"); }
|
||||
}
|
||||
UASSERT_OBJ(!v3Global.assertDTypesResolved(), this,
|
||||
"not coded to create after dtypes resolved"); }
|
||||
ASTNODE_NODE_FUNCS(Signed)
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opAssign(lhs); out.isSigned(false); }
|
||||
virtual string emitVerilog() { return "%f$signed(%l)"; }
|
||||
@@ -3891,8 +3920,8 @@ class AstUnsigned : public AstNodeUniop {
|
||||
// $unsigned(lhs)
|
||||
public:
|
||||
AstUnsigned(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) {
|
||||
if (v3Global.assertDTypesResolved()) { v3fatalSrc("not coded to create after dtypes resolved"); }
|
||||
}
|
||||
UASSERT_OBJ(!v3Global.assertDTypesResolved(), this,
|
||||
"not coded to create after dtypes resolved"); }
|
||||
ASTNODE_NODE_FUNCS(Unsigned)
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opAssign(lhs); out.isSigned(false); }
|
||||
virtual string emitVerilog() { return "%f$unsigned(%l)"; }
|
||||
@@ -4097,7 +4126,7 @@ public:
|
||||
m_size = setwidth;
|
||||
if (setwidth) {
|
||||
if (minwidth==-1) minwidth = setwidth;
|
||||
dtypeSetLogicSized(setwidth, minwidth, AstNumeric::UNSIGNED);
|
||||
dtypeSetLogicUnsized(setwidth, minwidth, AstNumeric::UNSIGNED);
|
||||
}
|
||||
}
|
||||
AstCCast(FileLine* fl, AstNode* lhsp, AstNode* typeFromp) : AstNodeUniop(fl, lhsp) {
|
||||
@@ -4865,7 +4894,7 @@ class AstShiftL : public AstNodeBiop {
|
||||
public:
|
||||
AstShiftL(FileLine* fl, AstNode* lhsp, AstNode* rhsp, int setwidth=0)
|
||||
: AstNodeBiop(fl, lhsp, rhsp) {
|
||||
if (setwidth) { dtypeSetLogicSized(setwidth, setwidth, AstNumeric::UNSIGNED); }
|
||||
if (setwidth) { dtypeSetLogicSized(setwidth, AstNumeric::UNSIGNED); }
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(ShiftL)
|
||||
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstShiftL(this->fileline(), lhsp, rhsp); }
|
||||
@@ -4883,7 +4912,7 @@ class AstShiftR : public AstNodeBiop {
|
||||
public:
|
||||
AstShiftR(FileLine* fl, AstNode* lhsp, AstNode* rhsp, int setwidth=0)
|
||||
: AstNodeBiop(fl, lhsp, rhsp) {
|
||||
if (setwidth) { dtypeSetLogicSized(setwidth, setwidth, AstNumeric::UNSIGNED); }
|
||||
if (setwidth) { dtypeSetLogicSized(setwidth, AstNumeric::UNSIGNED); }
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(ShiftR)
|
||||
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstShiftR(this->fileline(), lhsp, rhsp); }
|
||||
@@ -4905,7 +4934,7 @@ public:
|
||||
AstShiftRS(FileLine* fl, AstNode* lhsp, AstNode* rhsp, int setwidth=0)
|
||||
: AstNodeBiop(fl, lhsp, rhsp) {
|
||||
// Important that widthMin be correct, as opExtend requires it after V3Expand
|
||||
if (setwidth) { dtypeSetLogicSized(setwidth, setwidth, AstNumeric::SIGNED); }
|
||||
if (setwidth) { dtypeSetLogicSized(setwidth, AstNumeric::SIGNED); }
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(ShiftRS)
|
||||
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstShiftRS(this->fileline(), lhsp, rhsp); }
|
||||
@@ -5282,7 +5311,6 @@ public:
|
||||
AstConcat(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {
|
||||
if (lhsp->dtypep() && rhsp->dtypep()) {
|
||||
dtypeSetLogicSized(lhsp->dtypep()->width()+rhsp->dtypep()->width(),
|
||||
lhsp->dtypep()->width()+rhsp->dtypep()->width(),
|
||||
AstNumeric::UNSIGNED);
|
||||
}
|
||||
}
|
||||
@@ -5325,7 +5353,6 @@ private:
|
||||
if (lhsp()) {
|
||||
if (const AstConst* constp = VN_CAST(rhsp(), Const)) {
|
||||
dtypeSetLogicSized(lhsp()->width()*constp->toUInt(),
|
||||
lhsp()->width()*constp->toUInt(),
|
||||
AstNumeric::UNSIGNED);
|
||||
}
|
||||
}
|
||||
@@ -5969,7 +5996,7 @@ public:
|
||||
AstCMath(FileLine* fl, const string& textStmt, int setwidth, bool cleanOut=true)
|
||||
: AstNodeMath(fl), m_cleanOut(cleanOut) {
|
||||
addNOp1p(new AstText(fl, textStmt, true));
|
||||
if (setwidth) { dtypeSetLogicSized(setwidth, setwidth, AstNumeric::UNSIGNED); }
|
||||
if (setwidth) { dtypeSetLogicSized(setwidth, AstNumeric::UNSIGNED); }
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(CMath)
|
||||
virtual bool isGateOptimizable() const { return false; }
|
||||
@@ -6071,12 +6098,8 @@ public:
|
||||
class AstTypeTable : public AstNode {
|
||||
// Container for hash of standard data types
|
||||
// Children: NODEDTYPEs
|
||||
typedef std::map<std::pair<int,int>,AstBasicDType*> LogicMap;
|
||||
AstBasicDType* m_basicps[AstBasicDTypeKwd::_ENUM_MAX];
|
||||
//
|
||||
enum { IDX0_LOGIC, IDX0_BIT, _IDX0_MAX };
|
||||
LogicMap m_logicMap[_IDX0_MAX][AstNumeric::_ENUM_MAX]; // uses above IDX enums
|
||||
//
|
||||
typedef std::map<VBasicTypeKey,AstBasicDType*> DetailedMap;
|
||||
DetailedMap m_detailedMap;
|
||||
public:
|
||||
@@ -6111,7 +6134,7 @@ private:
|
||||
AstExecGraph* m_execGraphp; // Execution MTask graph for threads>1 mode
|
||||
public:
|
||||
AstNetlist()
|
||||
: AstNode(new FileLine("AstRoot", 0))
|
||||
: AstNode(new FileLine(FileLine::builtInFilename()))
|
||||
, m_typeTablep(NULL)
|
||||
, m_dollarUnitPkgp(NULL)
|
||||
, m_evalp(NULL)
|
||||
|
||||
+1
-1
@@ -141,7 +141,7 @@ private:
|
||||
|
||||
// Remap var names and replace lower Begins
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
if (nodep->genforp()) nodep->v3fatalSrc("GENFORs should have been expanded earlier");
|
||||
UASSERT_OBJ(!nodep->genforp(), nodep, "GENFORs should have been expanded earlier");
|
||||
}
|
||||
m_namedScope = oldScope;
|
||||
m_unnamedScope = oldUnnamed;
|
||||
|
||||
+33
-42
@@ -60,10 +60,9 @@ public:
|
||||
// Called by operator delete on any node - only if VL_LEAK_CHECKS
|
||||
if (debug()>=9) cout<<"-nodeDel: "<<cvtToHex(nodep)<<endl;
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
if (iter==s_nodes.end() || !(iter->second & FLAG_ALLOCATED)) {
|
||||
reinterpret_cast<const AstNode*>(nodep)
|
||||
->v3fatalSrc("Deleting AstNode object that was never tracked or already deleted");
|
||||
}
|
||||
UASSERT_OBJ(!(iter==s_nodes.end() || !(iter->second & FLAG_ALLOCATED)),
|
||||
reinterpret_cast<const AstNode*>(nodep),
|
||||
"Deleting AstNode object that was never tracked or already deleted");
|
||||
if (iter!=s_nodes.end()) s_nodes.erase(iter);
|
||||
}
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 4
|
||||
@@ -74,9 +73,8 @@ public:
|
||||
// Called by operator new on any node - only if VL_LEAK_CHECKS
|
||||
if (debug()>=9) cout<<"-nodeNew: "<<cvtToHex(nodep)<<endl;
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
if (iter !=s_nodes.end() && (iter->second & FLAG_ALLOCATED)) {
|
||||
nodep->v3fatalSrc("Newing AstNode object that is already allocated");
|
||||
}
|
||||
UASSERT_OBJ(!(iter !=s_nodes.end() && (iter->second & FLAG_ALLOCATED)),
|
||||
nodep, "Newing AstNode object that is already allocated");
|
||||
if (iter == s_nodes.end()) {
|
||||
int flags = FLAG_ALLOCATED; // This int needed to appease GCC 4.1.2
|
||||
s_nodes.insert(make_pair(nodep, flags));
|
||||
@@ -96,19 +94,17 @@ public:
|
||||
if (!linkable) return; // save some time, else the map will get huge!
|
||||
#endif
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
if (iter == s_nodes.end()) {
|
||||
if (VL_UNCOVERABLE(iter == s_nodes.end())) {
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
nodep->v3fatalSrc("AstNode is in tree, but not allocated");
|
||||
#endif
|
||||
} else {
|
||||
if (!(iter->second & FLAG_ALLOCATED)) {
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
nodep->v3fatalSrc("AstNode is in tree, but not allocated");
|
||||
UASSERT_OBJ(iter->second & FLAG_ALLOCATED, nodep,
|
||||
"AstNode is in tree, but not allocated");
|
||||
#endif
|
||||
}
|
||||
if (iter->second & FLAG_IN_TREE) {
|
||||
nodep->v3fatalSrc("AstNode is already in tree at another location");
|
||||
}
|
||||
UASSERT_OBJ(!(iter->second & FLAG_IN_TREE), nodep,
|
||||
"AstNode is already in tree at another location");
|
||||
}
|
||||
int or_flags = FLAG_IN_TREE | (linkable?FLAG_LINKABLE:0);
|
||||
if (iter == s_nodes.end()) {
|
||||
@@ -230,35 +226,31 @@ public:
|
||||
class BrokenCheckVisitor : public AstNVisitor {
|
||||
private:
|
||||
void checkWidthMin(const AstNode* nodep) {
|
||||
if (nodep->width() != nodep->widthMin()
|
||||
&& v3Global.widthMinUsage()==VWidthMinUsage::MATCHES_WIDTH) {
|
||||
nodep->v3fatalSrc("Width != WidthMin");
|
||||
}
|
||||
UASSERT_OBJ(nodep->width() == nodep->widthMin()
|
||||
|| v3Global.widthMinUsage() != VWidthMinUsage::MATCHES_WIDTH,
|
||||
nodep, "Width != WidthMin");
|
||||
}
|
||||
void processAndIterate(AstNode* nodep) {
|
||||
BrokenTable::setUnder(nodep, true);
|
||||
if (const char* whyp=nodep->broken()) {
|
||||
nodep->v3fatalSrc("Broken link in node (or something without maybePointedTo): "<<whyp);
|
||||
}
|
||||
const char* whyp = nodep->broken();
|
||||
UASSERT_OBJ(!whyp, nodep,
|
||||
"Broken link in node (or something without maybePointedTo): "<<whyp);
|
||||
if (nodep->dtypep()) {
|
||||
if (!nodep->dtypep()->brokeExists()) {
|
||||
nodep->v3fatalSrc("Broken link in node->dtypep() to "
|
||||
<<cvtToHex(nodep->dtypep()));
|
||||
} else if (!VN_IS(nodep->dtypep(), NodeDType)) {
|
||||
nodep->v3fatalSrc("Non-dtype link in node->dtypep() to "
|
||||
<<cvtToHex(nodep->dtypep()));
|
||||
}
|
||||
UASSERT_OBJ(nodep->dtypep()->brokeExists(), nodep,
|
||||
"Broken link in node->dtypep() to "<<cvtToHex(nodep->dtypep()));
|
||||
UASSERT_OBJ(VN_IS(nodep->dtypep(), NodeDType), nodep,
|
||||
"Non-dtype link in node->dtypep() to "<<cvtToHex(nodep->dtypep()));
|
||||
}
|
||||
if (v3Global.assertDTypesResolved()) {
|
||||
if (nodep->hasDType()) {
|
||||
if (!nodep->dtypep()) nodep->v3fatalSrc(
|
||||
"No dtype on node with hasDType(): "<<nodep->prettyTypeName());
|
||||
UASSERT_OBJ(nodep->dtypep(), nodep,
|
||||
"No dtype on node with hasDType(): "<<nodep->prettyTypeName());
|
||||
} else {
|
||||
if (nodep->dtypep()) nodep->v3fatalSrc(
|
||||
"DType on node without hasDType(): "<<nodep->prettyTypeName());
|
||||
UASSERT_OBJ(!nodep->dtypep(), nodep,
|
||||
"DType on node without hasDType(): "<<nodep->prettyTypeName());
|
||||
}
|
||||
if (nodep->getChildDTypep()) nodep->v3fatalSrc(
|
||||
"childDTypep() non-null on node after should have removed");
|
||||
UASSERT_OBJ(!nodep->getChildDTypep(), nodep,
|
||||
"childDTypep() non-null on node after should have removed");
|
||||
if (const AstNodeDType* dnodep = VN_CAST(nodep, NodeDType)) checkWidthMin(dnodep);
|
||||
}
|
||||
checkWidthMin(nodep);
|
||||
@@ -267,12 +259,11 @@ private:
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep) {
|
||||
processAndIterate(nodep);
|
||||
if (v3Global.assertDTypesResolved()
|
||||
&& nodep->brokeLhsMustBeLvalue()
|
||||
&& VN_IS(nodep->lhsp(), NodeVarRef)
|
||||
&& !VN_CAST(nodep->lhsp(), NodeVarRef)->lvalue()) {
|
||||
nodep->v3fatalSrc("Assignment LHS is not an lvalue");
|
||||
}
|
||||
UASSERT_OBJ(!(v3Global.assertDTypesResolved()
|
||||
&& nodep->brokeLhsMustBeLvalue()
|
||||
&& VN_IS(nodep->lhsp(), NodeVarRef)
|
||||
&& !VN_CAST(nodep->lhsp(), NodeVarRef)->lvalue()),
|
||||
nodep, "Assignment LHS is not an lvalue");
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
processAndIterate(nodep);
|
||||
@@ -291,9 +282,9 @@ public:
|
||||
void V3Broken::brokenAll(AstNetlist* nodep) {
|
||||
//UINFO(9,__FUNCTION__<<": "<<endl);
|
||||
static bool inBroken = false;
|
||||
if (inBroken) {
|
||||
if (VL_UNCOVERABLE(inBroken)) {
|
||||
// A error called by broken can recurse back into broken; avoid this
|
||||
UINFO(1,"Broken called under broken, skipping recursion.\n");
|
||||
UINFO(1,"Broken called under broken, skipping recursion.\n"); // LCOV_EXCL_LINE
|
||||
} else {
|
||||
inBroken = true;
|
||||
BrokenTable::prepForTree();
|
||||
|
||||
+1
-1
@@ -106,7 +106,7 @@ private:
|
||||
//######################################################################
|
||||
|
||||
void V3CCtors::evalAsserts() {
|
||||
AstNodeModule* modp = v3Global.rootp()->modulesp(); // Top module
|
||||
AstNodeModule* modp = v3Global.rootp()->modulesp(); // Top module wrapper
|
||||
AstCFunc* funcp = new AstCFunc(modp->fileline(), "_eval_debug_assertions", NULL, "void");
|
||||
funcp->declPrivate(true);
|
||||
funcp->isStatic(false);
|
||||
|
||||
+4
-4
@@ -72,7 +72,7 @@ private:
|
||||
itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
if (itemp->isDefault()) {
|
||||
if (hitDefault) {
|
||||
nodep->v3error("Multiple default statements in case statement.");
|
||||
itemp->v3error("Multiple default statements in case statement.");
|
||||
}
|
||||
hitDefault = true;
|
||||
}
|
||||
@@ -171,7 +171,7 @@ private:
|
||||
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
|
||||
//if (debug()>=9) icondp->dumpTree(cout, " caseitem: ");
|
||||
AstConst* iconstp = VN_CAST(icondp, Const);
|
||||
if (!iconstp) nodep->v3fatalSrc("above 'can't parse' should have caught this");
|
||||
UASSERT_OBJ(iconstp, nodep, "above 'can't parse' should have caught this");
|
||||
if (neverItem(nodep, iconstp)) {
|
||||
// X in casez can't ever be executed
|
||||
} else {
|
||||
@@ -186,8 +186,8 @@ private:
|
||||
if (!m_valueItem[i]) {
|
||||
m_valueItem[i] = itemp;
|
||||
} else if (!itemp->ignoreOverlap() && !bitched) {
|
||||
itemp->v3warn(CASEOVERLAP, "Case values overlap (example pattern 0x"
|
||||
<<std::hex<<i<<")");
|
||||
icondp->v3warn(CASEOVERLAP, "Case values overlap (example pattern 0x"
|
||||
<<std::hex<<i<<")");
|
||||
bitched = true;
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
}
|
||||
|
||||
+4
-4
@@ -303,7 +303,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
string spaces(int level) { string out; while (level--) out += " "; return out; }
|
||||
string spaces(int level) { string out; while (level--) out += " "; return out; } // LCOV_EXCL_LINE
|
||||
|
||||
string pad(unsigned column, const string& in) {
|
||||
string out = in;
|
||||
@@ -424,7 +424,7 @@ private:
|
||||
}
|
||||
//UINFO(9," finalflop "<<targetp->name()<<" "<<targetp->nodep()->fileline()<<endl);
|
||||
warnAndFile(markp->nodep(), V3ErrorCode::CDCRSTLOGIC,
|
||||
"Logic in path that feeds async reset, via signal: "+nodep->prettyName());
|
||||
"Logic in path that feeds async reset, via signal: "+nodep->prettyNameQ());
|
||||
dumpAsyncRecurse(targetp, "", " ", 0);
|
||||
}
|
||||
bool dumpAsyncRecurse(CdcEitherVertex* vertexp, const string& prefix,
|
||||
@@ -633,9 +633,9 @@ private:
|
||||
}
|
||||
virtual void visit(AstNodeVarRef* nodep) {
|
||||
if (m_scopep) {
|
||||
if (!m_logicVertexp) nodep->v3fatalSrc("Var ref not under a logic block");
|
||||
UASSERT_OBJ(m_logicVertexp, nodep, "Var ref not under a logic block");
|
||||
AstVarScope* varscp = nodep->varScopep();
|
||||
if (!varscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp");
|
||||
UASSERT_OBJ(varscp, nodep, "Var didn't get varscoped in V3Scope.cpp");
|
||||
CdcVarVertex* varvertexp = makeVarVertex(varscp);
|
||||
UINFO(5," VARREF to "<<varscp<<endl);
|
||||
// We use weight of one for normal edges,
|
||||
|
||||
+6
-6
@@ -87,7 +87,7 @@ public:
|
||||
m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp));
|
||||
} else {
|
||||
AstCReturn* returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn);
|
||||
if (!returnp) m_scopetopp->v3fatalSrc("Lost CReturn in top change function");
|
||||
UASSERT_OBJ(returnp, m_scopetopp, "Lost CReturn in top change function");
|
||||
// This is currently using AstLogOr which will shortcut the
|
||||
// evaluation if any function returns true. This is likely what
|
||||
// we want and is similar to the logic already in use inside
|
||||
@@ -182,7 +182,7 @@ private:
|
||||
if (debug()) nodep->dumpTree(cout, "-DETECTARRAY-class-");
|
||||
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable"
|
||||
" (probably with UNOPTFLAT warning suppressed): "
|
||||
<<m_vscp->varp()->prettyName());
|
||||
<<m_vscp->varp()->prettyNameQ());
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
@@ -190,7 +190,7 @@ private:
|
||||
if (debug()) nodep->dumpTree(cout, "-DETECTARRAY-general-");
|
||||
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable"
|
||||
" (probably with UNOPTFLAT warning suppressed): "
|
||||
<<m_vscp->varp()->prettyName());
|
||||
<<m_vscp->varp()->prettyNameQ());
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
@@ -241,7 +241,7 @@ private:
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void genChangeDet(AstVarScope* vscp) {
|
||||
vscp->v3warn(IMPERFECTSCH, "Imperfect scheduling of variable: "<<vscp->prettyName());
|
||||
vscp->v3warn(IMPERFECTSCH, "Imperfect scheduling of variable: "<<vscp->prettyNameQ());
|
||||
ChangedInsertVisitor visitor (vscp, m_statep);
|
||||
}
|
||||
|
||||
@@ -260,8 +260,8 @@ private:
|
||||
AstNode::user1ClearTree();
|
||||
// Create the change detection function
|
||||
AstScope* scopep = nodep->scopep();
|
||||
if (!scopep) nodep->v3fatalSrc("No scope found on top level,"
|
||||
" perhaps you have no statements?");
|
||||
UASSERT_OBJ(scopep, nodep,
|
||||
"No scope found on top level, perhaps you have no statements?");
|
||||
m_statep->m_scopetopp = scopep;
|
||||
|
||||
// Create a wrapper change detection function that calls each change detection function
|
||||
|
||||
+2
-1
@@ -78,7 +78,8 @@ private:
|
||||
} else {
|
||||
nodep->dtypeChgWidth(width, nodep->widthMin());
|
||||
AstNodeDType* new_dtypep2 = nodep->dtypep();
|
||||
if (new_dtypep2 == old_dtypep) nodep->v3fatalSrc("Dtype didn't change when width changed");
|
||||
UASSERT_OBJ(new_dtypep2 != old_dtypep, nodep,
|
||||
"Dtype didn't change when width changed");
|
||||
old_dtypep->user3p(new_dtypep2); // Remember for next time
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,924 +0,0 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Break always into sensitivity active domains
|
||||
//
|
||||
// Code available from: http://www.veripool.org/verilator
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2008-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.
|
||||
//
|
||||
// Verilator is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//*************************************************************************
|
||||
// V3ClkGater's Transformations:
|
||||
//
|
||||
// Look for clock gaters
|
||||
// Note there's overlap between this process and V3Split's
|
||||
// ALWAYS: Build graph
|
||||
// IF: Form vertex pointing to any IFs or ALWAYS above
|
||||
// VARREF: Form vertex pointing to IFs it is under
|
||||
// ASSIGN: If under normal assignment, disable optimization
|
||||
// FUTURE OPTIMIZE: If signal is set to itself, consider that OK for gating.
|
||||
// !splitable: Mark all VARREFs in this statement as comming from it
|
||||
// Optimize graph so if signal is referenced under multiple IF branches it moves up
|
||||
// Make ALWAYS for each new gating term, and move statements there
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3Stats.h"
|
||||
#include "V3Graph.h"
|
||||
#include "V3ClkGater.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
//######################################################################
|
||||
// Base for debug
|
||||
|
||||
class GaterBaseVisitor : public AstNVisitor {
|
||||
protected:
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Support classes
|
||||
|
||||
class GaterVarVertex;
|
||||
|
||||
class GaterVertex : public V3GraphVertex {
|
||||
static uint32_t s_rankNum;
|
||||
public:
|
||||
explicit GaterVertex(V3Graph* graphp)
|
||||
: V3GraphVertex(graphp) {
|
||||
s_rankNum++; rank(s_rankNum);
|
||||
}
|
||||
virtual ~GaterVertex() {}
|
||||
virtual int typeNum() const = 0;
|
||||
static void clearRank() { s_rankNum = 0; }
|
||||
virtual int sortCmp(const V3GraphVertex* rhsp) const;
|
||||
};
|
||||
uint32_t GaterVertex::s_rankNum = 0;
|
||||
|
||||
class GaterHeadVertex : public GaterVertex {
|
||||
public:
|
||||
explicit GaterHeadVertex(V3Graph* graphp)
|
||||
: GaterVertex(graphp) {}
|
||||
virtual ~GaterHeadVertex() {}
|
||||
virtual int typeNum() const { return __LINE__; } // C++ typeof() equivelent
|
||||
virtual string name() const { return "*HEAD*"; }
|
||||
virtual string dotColor() const { return "green"; }
|
||||
};
|
||||
|
||||
class GaterPliVertex : public GaterVertex {
|
||||
public:
|
||||
explicit GaterPliVertex(V3Graph* graphp)
|
||||
: GaterVertex(graphp) {}
|
||||
virtual ~GaterPliVertex() {}
|
||||
virtual int typeNum() const { return __LINE__; } // C++ typeof() equivelent
|
||||
virtual string name() const { return "*PLI*"; }
|
||||
virtual string dotColor() const { return "red"; }
|
||||
};
|
||||
|
||||
class GaterIfVertex : public GaterVertex {
|
||||
AstNodeIf* m_nodep;
|
||||
public:
|
||||
AstNodeIf* nodep() const { return m_nodep; }
|
||||
GaterIfVertex(V3Graph* graphp, AstNodeIf* nodep)
|
||||
: GaterVertex(graphp), m_nodep(nodep) { }
|
||||
virtual ~GaterIfVertex() {}
|
||||
virtual int typeNum() const { return __LINE__; } // C++ typeof() equivelent
|
||||
virtual string name() const {
|
||||
return cvtToHex(m_nodep)+" {"+cvtToStr(m_nodep->fileline()->lineno())+"}"; }
|
||||
};
|
||||
|
||||
class GaterVarVertex : public GaterVertex {
|
||||
AstVarScope* m_nodep;
|
||||
public:
|
||||
AstVarScope* nodep() const { return m_nodep; }
|
||||
GaterVarVertex(V3Graph* graphp, AstVarScope* nodep)
|
||||
: GaterVertex(graphp), m_nodep(nodep) { }
|
||||
virtual ~GaterVarVertex() {}
|
||||
virtual int typeNum() const { return __LINE__; } // C++ typeof() equivelent
|
||||
virtual string name() const { return nodep()->name(); }
|
||||
virtual string dotColor() const { return "skyblue"; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Edge types
|
||||
|
||||
class GaterEdge : public V3GraphEdge {
|
||||
uint32_t m_ifelse; // True branch of if
|
||||
public:
|
||||
// These are used as shift amounts into node's user()
|
||||
#define VU_DEFINE enum VarUsage { VU_NONE=0, VU_IF=1, VU_ELSE=2, VU_PLI=4, VU_MADE=8}
|
||||
VU_DEFINE;
|
||||
|
||||
uint32_t ifelse() const { return m_ifelse; }
|
||||
bool ifelseTrue() const { return m_ifelse & VU_IF; }
|
||||
bool ifelseFalse() const { return m_ifelse & VU_ELSE; }
|
||||
bool ifelseBoth() const { return ifelseTrue() && ifelseFalse(); }
|
||||
GaterEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, uint32_t ifelse)
|
||||
: V3GraphEdge(graphp, fromp, top, 10, false), m_ifelse(ifelse) {}
|
||||
virtual ~GaterEdge() {}
|
||||
virtual string dotColor() const { return ((ifelse() & VU_PLI) ? "red"
|
||||
: (ifelseBoth() ? "blue"
|
||||
: (ifelseTrue() ? "green"
|
||||
: "darkgreen"))); }
|
||||
virtual int sortCmp(const V3GraphEdge* rEdgep) const {
|
||||
// Our master sort sorts by edges first, so we don't want to
|
||||
// consider the upstream vertex::sortCmp when sorting by edges.
|
||||
// We do want to order by something though; rank works
|
||||
if (fromp()->rank() < rEdgep->fromp()->rank()) return -1;
|
||||
if (fromp()->rank() > rEdgep->fromp()->rank()) return 1;
|
||||
// If same, resolve by ifelse
|
||||
const GaterEdge* crEdgep = static_cast<const GaterEdge*>(rEdgep);
|
||||
if (m_ifelse < crEdgep->m_ifelse) return -1;
|
||||
if (m_ifelse > crEdgep->m_ifelse) return 1;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
int GaterVertex::sortCmp(const V3GraphVertex* rhsp) const {
|
||||
const GaterVertex* crhsp = static_cast<const GaterVertex*>(rhsp);
|
||||
// We really only care about ordering Var's together, but...
|
||||
// First put same type together
|
||||
if (typeNum() < crhsp->typeNum()) return -1;
|
||||
if (typeNum() > crhsp->typeNum()) return 1;
|
||||
// If variable, group by same input fanin
|
||||
// (know they're the same type based on above compare)
|
||||
if (dynamic_cast<const GaterVarVertex*>(this)) {
|
||||
// We've already sorted by edges, so just see if same tree
|
||||
// If this gets too slow, we could compute a hash up front
|
||||
V3GraphEdge* lEdgep = this->inBeginp();
|
||||
V3GraphEdge* rEdgep = rhsp->inBeginp();
|
||||
while (lEdgep && rEdgep) {
|
||||
const GaterEdge* clEdgep = static_cast<const GaterEdge*>(lEdgep);
|
||||
const GaterEdge* crEdgep = static_cast<const GaterEdge*>(rEdgep);
|
||||
if (lEdgep->fromp()->rank() < rEdgep->fromp()->rank()) return -1;
|
||||
if (lEdgep->fromp()->rank() > rEdgep->fromp()->rank()) return 1;
|
||||
if (clEdgep->ifelse() < crEdgep->ifelse()) return -1;
|
||||
if (clEdgep->ifelse() > crEdgep->ifelse()) return 1;
|
||||
lEdgep = lEdgep->inNextp();
|
||||
rEdgep = rEdgep->inNextp();
|
||||
}
|
||||
if (!lEdgep && !rEdgep) return 0;
|
||||
return lEdgep ? -1 : 1;
|
||||
}
|
||||
// Finally by rank of this vertex
|
||||
if (rank() < rhsp->rank()) return -1;
|
||||
if (rank() > rhsp->rank()) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// Check for non-simple gating equations
|
||||
|
||||
class GaterCondVisitor : public GaterBaseVisitor {
|
||||
private:
|
||||
// RETURN STATE
|
||||
bool m_isSimple; // Set false when we know it isn't simple
|
||||
// METHODS
|
||||
inline void okIterate(AstNode* nodep) {
|
||||
if (m_isSimple) iterateChildren(nodep);
|
||||
}
|
||||
// VISITORS
|
||||
virtual void visit(AstOr* nodep) { okIterate(nodep); }
|
||||
virtual void visit(AstAnd* nodep) { okIterate(nodep); }
|
||||
virtual void visit(AstNot* nodep) { okIterate(nodep); }
|
||||
virtual void visit(AstLogOr* nodep) { okIterate(nodep); }
|
||||
virtual void visit(AstLogAnd* nodep) { okIterate(nodep); }
|
||||
virtual void visit(AstLogNot* nodep) { okIterate(nodep); }
|
||||
virtual void visit(AstVarRef* nodep) { okIterate(nodep); }
|
||||
|
||||
// Other possibilities are equals, etc
|
||||
// But, we don't want to get too complicated or it will take too much
|
||||
// effort to calculate the gater
|
||||
|
||||
virtual void visit(AstNode* nodep) {
|
||||
m_isSimple = false;
|
||||
//iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit GaterCondVisitor(AstNode* nodep) {
|
||||
m_isSimple = true;
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~GaterCondVisitor() {}
|
||||
// PUBLIC METHODS
|
||||
bool isSimple() const { return m_isSimple; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Check for non-simple gating equations
|
||||
|
||||
class GaterBodyVisitor : public GaterBaseVisitor {
|
||||
// NODE STATE
|
||||
// Input state
|
||||
// AstVarScope::user2p() -> AstAlways* moving this variable to
|
||||
|
||||
enum State {
|
||||
// This is used as a bitmask
|
||||
STATE_UNKNOWN = 0,
|
||||
STATE_KEEP = 1, // Seen a variable we need, keep this statement
|
||||
STATE_DELETE = 2 // Seen a variable we need, delete this statement
|
||||
// 3=keep & delete
|
||||
};
|
||||
|
||||
bool m_original; // Deleting original statements, vs deleting new var statements
|
||||
AstNode* m_exprp; // New gater expression we are building
|
||||
bool m_cloning; // Clone this object 0=not sure yet, 1=do
|
||||
uint32_t m_state; // Parsing state
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
if (nodep->lvalue()) {
|
||||
AstVarScope* vscp = nodep->varScopep();
|
||||
if (vscp->user2p() == m_exprp) {
|
||||
// This variable's block needs to move to the new always
|
||||
if (m_original) {
|
||||
UINFO(9," VARREF delete in old: "<<nodep<<endl);
|
||||
m_state |= STATE_DELETE;
|
||||
} else {
|
||||
UINFO(9," VARREF stays in new: "<<nodep<<endl);
|
||||
m_state |= STATE_KEEP;
|
||||
}
|
||||
} else {
|
||||
if (m_original) {
|
||||
UINFO(9," VARREF other stays in old\n");
|
||||
m_state |= STATE_KEEP;
|
||||
} else {
|
||||
UINFO(9," VARREF other delete in new\n");
|
||||
m_state |= STATE_DELETE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//virtual void visit(AstNodeIf* nodep) { ... }
|
||||
// Not needed, it's the same handling as any other statement. Cool, huh?
|
||||
// (We may get empty IFs but the constant propagater will rip them up for us)
|
||||
|
||||
virtual void visit(AstNodeStmt* nodep) {
|
||||
if (!nodep->isStatement()) {
|
||||
iterateChildren(nodep);
|
||||
return;
|
||||
}
|
||||
uint32_t oldstate = m_state;
|
||||
// Find if children want to delete this or not.
|
||||
// Note children may bicker, and want to both keep and delete (branches on a if)
|
||||
uint32_t childstate;
|
||||
{
|
||||
m_state = STATE_UNKNOWN;
|
||||
iterateChildren(nodep);
|
||||
childstate = m_state;
|
||||
}
|
||||
m_state = oldstate;
|
||||
UINFO(9," Did state="<<childstate<<" "<<nodep<<endl);
|
||||
|
||||
// Indeterminate (statement with no lvalue), so keep only in original
|
||||
if (childstate == STATE_UNKNOWN) {
|
||||
if (m_original) childstate |= STATE_KEEP;
|
||||
else childstate |= STATE_DELETE;
|
||||
}
|
||||
|
||||
if ((childstate & STATE_DELETE) && !(childstate & STATE_KEEP)) {
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
// Pass upwards we did delete
|
||||
m_state |= STATE_DELETE;
|
||||
} else {
|
||||
// Pass upwards we must keep
|
||||
m_state |= STATE_KEEP;
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
GaterBodyVisitor(AstAlways* nodep, AstNode* exprp, bool original) {
|
||||
m_exprp = exprp;
|
||||
m_original = original;
|
||||
m_state = STATE_UNKNOWN;
|
||||
m_cloning = false;
|
||||
if (debug()>=9) nodep->dumpTree(cout, " GateBodyIn: ");
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
if (debug()>=9) nodep->dumpTree(cout, " GateBodyOut: ");
|
||||
// If there's no statements we shouldn't have had a resulting graph
|
||||
// vertex asking for this creation
|
||||
}
|
||||
virtual ~GaterBodyVisitor() {}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Create clock gaters
|
||||
|
||||
class GaterVisitor : public GaterBaseVisitor {
|
||||
// NODE STATE
|
||||
// Cleared on Always
|
||||
// AstVarScope::user1p() -> GaterVarVertex*
|
||||
// AstAlways::user4() -> bool. True indicates processed
|
||||
// Cleared on each new Always
|
||||
// AstVarScope::user2p() -> AstAlways* moving this variable to
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
AstUser4InUse m_inuser4;
|
||||
|
||||
// GRAPH STATE
|
||||
// Cleared on simplify
|
||||
// Vertex::user() -> VarUsage: Mark of which if/else edges are hit
|
||||
|
||||
// TYPES
|
||||
VU_DEFINE;
|
||||
|
||||
enum MiscConsts {
|
||||
IF_DEPTH_MAX = 4, // IFs deep we bother to analyze
|
||||
DOMAINS_MAX = 32 // Clock domains before avoiding O(N^2) blowup
|
||||
};
|
||||
|
||||
// MEMBERS
|
||||
string m_nonopt; // Reason block is not optimizable
|
||||
V3Double0 m_statGaters; // Statistic tracking
|
||||
V3Double0 m_statBits; // Statistic tracking
|
||||
bool m_directlyUnderAlw; // Immediately under Always or If
|
||||
int m_ifDepth; // Depth of IF statements
|
||||
int m_numIfs; // Number of IF statements
|
||||
V3Graph m_graph; // Scoreboard of var usages/dependencies
|
||||
GaterPliVertex* m_pliVertexp; // Element specifying PLI ordering
|
||||
GaterHeadVertex* m_headVertexp; // Top vertex
|
||||
V3GraphVertex* m_aboveVertexp; // Vertex above this point in tree
|
||||
uint32_t m_aboveTrue; // Vertex above this point is true branch
|
||||
AstVarScope* m_stmtVscp; // Current statement had variable assigned
|
||||
bool m_stmtInPli; // Current statement has PLI
|
||||
|
||||
// METHODS
|
||||
void nonOptimizable(AstNode* nodep, const char* reasonp) {
|
||||
if (m_nonopt=="") {
|
||||
UINFO(9," Nonopt: "<<reasonp<<": "<<nodep<<endl);
|
||||
m_nonopt = reasonp;
|
||||
}
|
||||
}
|
||||
void scoreboardPli(AstNode* nodep) {
|
||||
// Order all PLI statements with other PLI statements
|
||||
// This ensures $display's and such remain in proper order
|
||||
// We don't prevent splitting out other non-pli statements, however,
|
||||
// because it is common to have $uasserts sprinkled about.
|
||||
if (!m_pliVertexp) {
|
||||
m_pliVertexp = new GaterPliVertex(&m_graph);
|
||||
}
|
||||
if (m_stmtVscp) { // Already saw a variable, be sure to mark it!
|
||||
GaterVarVertex* varVtxp = reinterpret_cast<GaterVarVertex*>(m_stmtVscp->user1p());
|
||||
new GaterEdge(&m_graph, m_pliVertexp, varVtxp, VU_PLI);
|
||||
}
|
||||
m_stmtInPli = true; // Mark all followon variables too
|
||||
}
|
||||
|
||||
// METHODS -- GRAPH stuff
|
||||
void simplifyGraph() {
|
||||
if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_init", false);
|
||||
|
||||
// We now have all PLI variables with edges FROM the pli vertex
|
||||
simplifyPli();
|
||||
if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_pli", false);
|
||||
|
||||
// Any vertex that points along both true & false path to variable
|
||||
// can be simplfied so parent points to that vertex. Any vertex
|
||||
// that points to a (great...) grandparent of a variable can just
|
||||
// point to the edge.
|
||||
m_graph.userClearVertices(); // user() will contain VarUsage
|
||||
simplifyIfElseRecurse(m_headVertexp);
|
||||
if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_ifelse", false);
|
||||
|
||||
m_graph.userClearVertices(); // user() will contain VarUsage
|
||||
simplifyGrandRecurse(m_headVertexp, 1);
|
||||
if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_grand", false);
|
||||
|
||||
simplifyRemoveUngated(m_headVertexp);
|
||||
|
||||
// Give all signals with same gating term same color
|
||||
graphColorSameFeeder();
|
||||
|
||||
if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_done", false);
|
||||
}
|
||||
|
||||
void simplifyPli() {
|
||||
// For now, we'll not gate any logic with PLI lvariables in it. In
|
||||
// the future we may move PLI statements. One way to do so is to
|
||||
// all below pli VARs to go IfVertex -> PliVertex -> VarVertex then
|
||||
// they all must get colored the same. There may be a lot of
|
||||
// duplicated edges to the PLI; they'll need cleanup. All of the
|
||||
// later optimizations need to deal, and the GaterBodyVisitor needs
|
||||
// to know how to move them.
|
||||
if (m_pliVertexp) {
|
||||
// Follow PLI out edges to find all relevant variables
|
||||
for (V3GraphEdge* nextp,* edgep = m_pliVertexp->outBeginp(); edgep; edgep = nextp) {
|
||||
nextp = edgep->outNextp(); // We may edit the list
|
||||
if (GaterVarVertex* vVxp = dynamic_cast<GaterVarVertex*>(edgep->top())) {
|
||||
vVxp->unlinkDelete(&m_graph); VL_DANGLING(vVxp); VL_DANGLING(edgep);
|
||||
} else {
|
||||
m_graph.dump();
|
||||
v3fatalSrc("PLI vertex points to non-signal");
|
||||
}
|
||||
}
|
||||
m_pliVertexp->unlinkDelete(&m_graph); m_pliVertexp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void simplifyIfElseRecurse(V3GraphVertex* vertexp) {
|
||||
// From bottom-up, propagate duplicate IF/ELSE branches to grandparent
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
||||
simplifyIfElseRecurse(edgep->top());
|
||||
}
|
||||
//UINFO(9,"IERecurse "<<vertexp<<endl);
|
||||
if (dynamic_cast<GaterIfVertex*>(vertexp)) {
|
||||
// Clear indications
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
||||
edgep->top()->user(VU_NONE);
|
||||
}
|
||||
// Mark nodes on to/from side
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
||||
V3GraphVertex* toVxp = edgep->top();
|
||||
GaterEdge* cedgep = static_cast<GaterEdge*>(edgep);
|
||||
// We may mark this twice in one pass - if so it's duplicated; no worries
|
||||
if (cedgep->ifelseTrue()) toVxp->user(toVxp->user() | VU_IF);
|
||||
if (cedgep->ifelseFalse()) toVxp->user(toVxp->user() | VU_ELSE);
|
||||
//UINFO(9," mark "<<edgep<<" "<<toVxp<<endl);
|
||||
}
|
||||
// Any with both IF & ELSE mark get removal mark, and new parent edge
|
||||
for (V3GraphEdge* nextp,* edgep = vertexp->outBeginp(); edgep; edgep = nextp) {
|
||||
nextp = edgep->outNextp(); // We may edit the list
|
||||
V3GraphVertex* toVxp = edgep->top();
|
||||
//UINFO(9," to "<<toVxp->user()<<" "<<toVxp<<endl);
|
||||
if ((toVxp->user() & VU_IF) && (toVxp->user() & VU_ELSE)) {
|
||||
edgep->unlinkDelete(); VL_DANGLING(edgep);
|
||||
if (!(toVxp->user() & VU_MADE)) { // Make an edge only once
|
||||
toVxp->user(toVxp->user() | VU_MADE);
|
||||
GaterEdge* inedgep = static_cast<GaterEdge*>(vertexp->inBeginp());
|
||||
V3GraphVertex* grandparent = inedgep->fromp();
|
||||
new GaterEdge(&m_graph, grandparent, toVxp, inedgep->ifelse());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void simplifyGrandRecurse(V3GraphVertex* vertexp, uint32_t depth) {
|
||||
// From top-down delete any vars that grandparents source
|
||||
// IE A -> B -> C -> VAR
|
||||
// \----------^
|
||||
//UINFO(9,"GRecurse "<<depth<<" "<<vertexp<<endl);
|
||||
// Mark all variables to be swept
|
||||
for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) {
|
||||
nextp = edgep->outNextp(); // We may edit the list
|
||||
if (GaterVarVertex* toVxp = dynamic_cast<GaterVarVertex*>(edgep->top())) {
|
||||
if (toVxp->user() && toVxp->user() < depth) {
|
||||
// A recursion "above" us marked it,
|
||||
// Remove this edge, it's redundant with the upper edge
|
||||
edgep->unlinkDelete(); VL_DANGLING(edgep);
|
||||
} else {
|
||||
GaterEdge* cedgep = static_cast<GaterEdge*>(edgep);
|
||||
if (cedgep->ifelseBoth()) {
|
||||
toVxp->user(depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Recurse
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
||||
simplifyGrandRecurse(edgep->top(), depth+1);
|
||||
}
|
||||
// Clean our marks
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
||||
V3GraphVertex* toVxp = edgep->top();
|
||||
if (toVxp->user() && toVxp->user() < depth) { // A recursion "above" us marked it; don't mess with it
|
||||
} else {
|
||||
toVxp->user(0); // We marked it originally, so unmark now
|
||||
}
|
||||
}
|
||||
// Delete any If nodes with no children
|
||||
// Last, as want bottom-up cleanup
|
||||
for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) {
|
||||
nextp = edgep->outNextp(); // We may edit the list
|
||||
if (GaterIfVertex* toVxp = dynamic_cast<GaterIfVertex*>(edgep->top())) {
|
||||
if (!toVxp->outBeginp()) {
|
||||
if (!nextp || nextp->top() != edgep->top()) { // Else next would disappear; we'll do it next loop
|
||||
toVxp->unlinkDelete(&m_graph); VL_DANGLING(toVxp); VL_DANGLING(edgep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void simplifyRemoveUngated(V3GraphVertex* vertexp) {
|
||||
// Remove variables that are ungated
|
||||
// At this point, any variable under the head is ungated
|
||||
for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) {
|
||||
nextp = edgep->outNextp(); // We may edit the list
|
||||
if (GaterVarVertex* toVxp = dynamic_cast<GaterVarVertex*>(edgep->top())) {
|
||||
if (!nextp || nextp->top() != edgep->top()) { // Else next would disappear; we'll do it next loop
|
||||
toVxp->unlinkDelete(&m_graph); VL_DANGLING(toVxp); VL_DANGLING(edgep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void graphColorSameFeeder() {
|
||||
// Assign same color to all destination vertices that have same
|
||||
// subgraph feeding into them
|
||||
// (I.E. all "from" nodes are common within each color)
|
||||
// We could hash, but instead it's faster to sort edges, so we know
|
||||
// the same edge list is always adjacent. The result is a
|
||||
// O(vertices*edges) loop, but we'd need that to hash too.
|
||||
if (debug()>9) { cout<<"PreColor:\n"; m_graph.dump(); }
|
||||
|
||||
m_graph.sortEdges();
|
||||
|
||||
// Now sort vertices, so same inbound edge set ends up adjacent
|
||||
m_graph.sortVertices();
|
||||
|
||||
// Now walk and assign colors; same color is adjacent
|
||||
m_graph.clearColors();
|
||||
m_graph.userClearEdges(); // Used by newExprFromGraph
|
||||
uint32_t color = 1;
|
||||
GaterVarVertex* lastVxp = NULL;
|
||||
for (V3GraphVertex* vertexp = m_graph.verticesBeginp();
|
||||
vertexp; vertexp=vertexp->verticesNextp()) {
|
||||
if (GaterVarVertex* vVxp = dynamic_cast<GaterVarVertex*>(vertexp)) {
|
||||
if (!vVxp->inBeginp()) {
|
||||
// At this point, any variable not linked is an error
|
||||
// (It should have at least landed under the Head node)
|
||||
vVxp->nodep()->v3fatalSrc("Variable became stranded in clk gate detection");
|
||||
}
|
||||
if (!lastVxp || vVxp->sortCmp(lastVxp)) {
|
||||
// Different sources for this new node
|
||||
color++;
|
||||
}
|
||||
vVxp->color(color);
|
||||
lastVxp = vVxp;
|
||||
}
|
||||
}
|
||||
|
||||
if (debug()>9) { cout<<"PostColor:\n"; m_graph.dump(); }
|
||||
}
|
||||
|
||||
AstNode* newExprFromGraph(GaterVarVertex* vertexp) {
|
||||
// Recurse backwards, then form equation on return path
|
||||
// We could use user()!=0, but zeroing it is slow, so instead we'll mark with a generation
|
||||
// We get equations like "a | (!a & b)" which obviously could be reduced here,
|
||||
// instead out of generality, there's a V3Const::matchOrAndNot that'll clean it up
|
||||
static uint32_t s_generation = 0;
|
||||
++s_generation;
|
||||
nafgMarkRecurse(vertexp, s_generation);
|
||||
AstNode* nodep = nafgCreateRecurse(m_headVertexp, s_generation);
|
||||
if (debug()>=9) nodep->dumpTree(cout, " GateExpr: ");
|
||||
return nodep;
|
||||
}
|
||||
void nafgMarkRecurse(V3GraphVertex* vertexp, uint32_t generation) {
|
||||
// Backwards mark user() on the path we recurse
|
||||
//UINFO(9," nafgMark: v "<<cvtToHex(vertexp)<<" "<<vertexp->name()<<endl);
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
//UINFO(9," nafgMark: "<<cvtToHex(edgep)<<" "<<edgep->name()<<endl);
|
||||
edgep->user(generation);
|
||||
nafgMarkRecurse(edgep->fromp(), generation);
|
||||
}
|
||||
}
|
||||
AstNode* nafgCreateRecurse(V3GraphVertex* vertexp, uint32_t generation) {
|
||||
// Forewards follow user() marked previously and build tree
|
||||
AstNode* nodep = NULL;
|
||||
// OR across all edges found at this level
|
||||
//UINFO(9," nafgEnter: v "<<cvtToHex(vertexp)<<" "<<vertexp->name()<<endl);
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (edgep->user() == generation) {
|
||||
GaterEdge* cedgep = static_cast<GaterEdge*>(edgep);
|
||||
AstNode* eqnp = NULL;
|
||||
//UINFO(9," nafgFollow: "<<cvtToHex(edgep)<<" "<<edgep->name()<<endl);
|
||||
if (dynamic_cast<GaterHeadVertex*>(edgep->fromp())) {
|
||||
// Just OR in all lower terms
|
||||
eqnp = nafgCreateRecurse(edgep->top(), generation);
|
||||
} else if (GaterIfVertex* cVxp = dynamic_cast<GaterIfVertex*>(edgep->fromp())) {
|
||||
// Edges from IFs represent a real IF branch in the equation tree
|
||||
//UINFO(9," ifver "<<cvtToHex(edgep)<<" cc"<<edgep->dotColor()<<endl);
|
||||
eqnp = cVxp->nodep()->condp()->cloneTree(true);
|
||||
if (!eqnp) cVxp->nodep()->v3fatalSrc("null condition");
|
||||
if (cedgep->ifelseFalse()) {
|
||||
eqnp = new AstNot(eqnp->fileline(), eqnp);
|
||||
}
|
||||
// We need to AND this term onto whatever was found below it
|
||||
AstNode* belowp = nafgCreateRecurse(edgep->top(), generation);
|
||||
if (belowp) eqnp = new AstAnd(eqnp->fileline(), eqnp, belowp);
|
||||
}
|
||||
// Top level we could choose to make multiple gaters, or ORs under the gater
|
||||
// Right now we'll put OR lower down and let other optimizations deal
|
||||
if (nodep) nodep = new AstOr(nodep->fileline(), nodep, eqnp);
|
||||
else nodep = eqnp;
|
||||
//if (debug()>=9) nodep->dumpTree(cout, " followExpr: ");
|
||||
}
|
||||
}
|
||||
//UINFO(9," nafgExit: "<<cvtToHex(vertexp)<<" "<<vertexp->name()<<endl);
|
||||
return nodep;
|
||||
}
|
||||
|
||||
void newAlwaysTrees(AstAlways* nodep) {
|
||||
// Across all variables we're moving
|
||||
uint32_t lastColor = 0;
|
||||
AstNode* lastExprp = NULL;
|
||||
for (V3GraphVertex* vertexp = m_graph.verticesBeginp();
|
||||
vertexp; vertexp=vertexp->verticesNextp()) {
|
||||
if (GaterVarVertex* vVxp = dynamic_cast<GaterVarVertex*>(vertexp)) {
|
||||
if (!lastExprp || lastColor != vVxp->color()) {
|
||||
lastColor = vVxp->color();
|
||||
// Create the block we've just finished
|
||||
if (lastExprp) newAlwaysTree(nodep, lastExprp); // Duplicate below
|
||||
// New expression for this color
|
||||
lastExprp = newExprFromGraph(vVxp);
|
||||
}
|
||||
// Mark variable to move
|
||||
if (vVxp->nodep()->user2p()) vVxp->nodep()->v3fatalSrc("One variable got marked under two gaters");
|
||||
vVxp->nodep()->user2p(lastExprp);
|
||||
m_statBits += vVxp->nodep()->width(); // Moving a wide bus counts more!
|
||||
// There shouldn't be two possibilities we want to
|
||||
// move to, IE {A,B} <= Z because we marked such
|
||||
// things as unoptimizable
|
||||
}
|
||||
}
|
||||
// Create the final block we've just finished
|
||||
if (lastExprp) newAlwaysTree(nodep, lastExprp); // Duplicate above
|
||||
}
|
||||
|
||||
void newAlwaysTree(AstAlways* nodep, AstNode* exprp) {
|
||||
// Create new always with specified gating expression
|
||||
// Relevant vars are already marked with user2p
|
||||
|
||||
// Should we put the gater under the always itself,
|
||||
// or make a new signal?
|
||||
// New signal: May cause replicated logic until we can combine
|
||||
// Need way to toggle signal on complicated pos/negedge
|
||||
// Under Always: More complicated, may not propagate logic with gateer
|
||||
// Untill we get better gate optimization, we'll go this route.
|
||||
//#define GATER_NOP
|
||||
#ifdef GATER_NOP
|
||||
// For testing only, don't clock gate but still make new always
|
||||
AstSenTree* sensesp = nodep->sensesp()->cloneTree(true);
|
||||
#else
|
||||
// Make a SenGate
|
||||
AstSenItem* oldsenitemsp = VN_CAST(nodep->sensesp()->sensesp(), SenItem);
|
||||
if (!oldsenitemsp) nodep->v3fatalSrc("SenTree doesn't have any SenItem under it");
|
||||
|
||||
AstSenTree* sensesp = new AstSenTree(nodep->fileline(),
|
||||
new AstSenGate(nodep->fileline(),
|
||||
oldsenitemsp->cloneTree(true),
|
||||
exprp));
|
||||
#endif
|
||||
|
||||
// Make new body; note clone will preserve user2p() indicating moving vars
|
||||
AstNode* bodyp = nodep->bodysp()->cloneTree(true);
|
||||
|
||||
AstAlways* alwp = new AstAlways(nodep->fileline(),
|
||||
nodep->keyword(),
|
||||
sensesp,
|
||||
bodyp);
|
||||
|
||||
alwp->user4(1); // No need to process the new always again!
|
||||
nodep->addNextHere(alwp);
|
||||
|
||||
// Blow moved statements from old body
|
||||
{ GaterBodyVisitor vis(nodep, exprp, true); }
|
||||
// Blow old statements from new body
|
||||
{ GaterBodyVisitor vis(alwp, exprp, false); }
|
||||
|
||||
++m_statGaters;
|
||||
if (debug()>=9) alwp->dumpTree(cout, " new: ");
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstAlways* nodep) {
|
||||
if (debug()>=9) cout<<endl<<endl<<endl;
|
||||
UINFO(5, "Gater: ALWAYS: "<<nodep<<endl);
|
||||
if (nodep->user4SetOnce()) return;
|
||||
|
||||
clear();
|
||||
|
||||
if (debug()>=9) nodep->dumpTree(cout, " Alwin: ");
|
||||
|
||||
// Look for constructs we can't optimize
|
||||
// Form graph with Vertices at each IF, and each Variable
|
||||
m_aboveTrue = VU_IF | VU_ELSE;
|
||||
iterateChildrenAlw(nodep, true);
|
||||
|
||||
// Other reasons to not optimize
|
||||
if (!m_numIfs) nonOptimizable(nodep, "No if statements");
|
||||
|
||||
// Something to optimize, oh my!
|
||||
if (m_nonopt!="") {
|
||||
UINFO(5, " Gater non-opt: "<<m_nonopt<<endl);
|
||||
} else {
|
||||
// Process it
|
||||
simplifyGraph();
|
||||
|
||||
// See how much moves
|
||||
uint32_t lastColor = 0;
|
||||
for (V3GraphVertex* vertexp = m_graph.verticesBeginp();
|
||||
vertexp; vertexp=vertexp->verticesNextp()) {
|
||||
if (GaterVarVertex* vVxp = dynamic_cast<GaterVarVertex*>(vertexp)) {
|
||||
if (lastColor < vVxp->color()) {
|
||||
lastColor = vVxp->color();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lastColor == 0) { // Nothing we moved!
|
||||
nonOptimizable(nodep, "Nothing moved");
|
||||
}
|
||||
else if (lastColor > DOMAINS_MAX) {
|
||||
// Our move algorithm is fairly slow and if we're splitting
|
||||
// up too much it'll get really nasty. It's probably a bad
|
||||
// move for performance to split too much, anyhow, as the
|
||||
// number of gaters will result in calling many small c functions.
|
||||
nonOptimizable(nodep, "Too much moved");
|
||||
}
|
||||
if (m_nonopt=="") {
|
||||
newAlwaysTrees(nodep);
|
||||
if (debug()>=9) nodep->dumpTree(cout, " Gaterout: ");
|
||||
}
|
||||
}
|
||||
UINFO(5, " Gater done"<<endl);
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
if (nodep->lvalue()) {
|
||||
AstVarScope* vscp = nodep->varScopep();
|
||||
if (nodep->varp()->isSigPublic()) {
|
||||
// Public signals shouldn't be changed, pli code might be messing with them
|
||||
scoreboardPli(nodep);
|
||||
}
|
||||
// If another lvalue in this node, give up optimizing.
|
||||
// We could just not optimize this variable, but we've already marked the
|
||||
// other variable as optimizable, so we can instead pretend it's a PLI node.
|
||||
if (m_stmtVscp) {
|
||||
UINFO(5, " Multiple lvalues in one statement: "<<nodep<<endl);
|
||||
scoreboardPli(nodep); // This will set m_stmtInPli
|
||||
}
|
||||
m_stmtVscp = vscp;
|
||||
// Find, or make new Vertex
|
||||
GaterVarVertex* vertexp = reinterpret_cast<GaterVarVertex*>(vscp->user1p());
|
||||
if (!vertexp) {
|
||||
vertexp = new GaterVarVertex(&m_graph, vscp);
|
||||
vscp->user1p(vertexp);
|
||||
}
|
||||
new GaterEdge(&m_graph, m_aboveVertexp, vertexp, m_aboveTrue);
|
||||
if (m_stmtInPli) {
|
||||
new GaterEdge(&m_graph, m_pliVertexp, vertexp, VU_PLI);
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeIf* nodep) {
|
||||
m_ifDepth++;
|
||||
bool allowGater = m_directlyUnderAlw && m_ifDepth <= IF_DEPTH_MAX;
|
||||
if (allowGater) {
|
||||
GaterCondVisitor condVisitor(nodep->condp());
|
||||
if (!condVisitor.isSimple()) {
|
||||
// Don't clear optimization, simply drop this IF as part of the gating
|
||||
UINFO(5," IFnon-simple-condition: "<<nodep<<endl);
|
||||
allowGater = false;
|
||||
}
|
||||
}
|
||||
if (!allowGater) {
|
||||
// IF burried under something complicated
|
||||
iterateChildrenAlw(nodep, true);
|
||||
} else {
|
||||
UINFO(5, " IF: "<<nodep<<endl);
|
||||
m_numIfs++;
|
||||
// Recurse to next level of if.
|
||||
V3GraphVertex* lastabovep = m_aboveVertexp;
|
||||
uint32_t lasttrue = m_aboveTrue;
|
||||
GaterIfVertex* vertexp = new GaterIfVertex(&m_graph, nodep);
|
||||
new GaterEdge(&m_graph, m_aboveVertexp, vertexp, m_aboveTrue);
|
||||
{
|
||||
iterateAndNextNull(nodep->condp()); // directlyUnder stays as-is
|
||||
}
|
||||
{
|
||||
m_aboveVertexp = vertexp; // Vars will point at this edge
|
||||
m_aboveTrue = VU_IF;
|
||||
iterateAndNextNull(nodep->ifsp()); // directlyUnder stays as-is (true)
|
||||
}
|
||||
{
|
||||
m_aboveVertexp = vertexp; // Vars will point at this edge
|
||||
m_aboveTrue = VU_ELSE;
|
||||
iterateAndNextNull(nodep->elsesp()); // directlyUnder stays as-is (true)
|
||||
}
|
||||
m_aboveVertexp = lastabovep;
|
||||
m_aboveTrue = lasttrue;
|
||||
}
|
||||
m_ifDepth--;
|
||||
}
|
||||
|
||||
virtual void visit(AstAssignDly* nodep) {
|
||||
// iterateChildrenAlw will detect this is a statement for us
|
||||
iterateChildrenAlw(nodep, false);
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeAssign* nodep) {
|
||||
// Note NOT AssignDly; handled above, We'll just mark this block as
|
||||
// not optimizable.
|
||||
//
|
||||
// A future alternative is to look for any lvalues that are also
|
||||
// variables in the sensitivity list, or rvalues in this block. If
|
||||
// any hit, disable optimization. Unlikely to be useful (For loops
|
||||
// being an exception, but they're already unrolled.)
|
||||
nonOptimizable(nodep, "Non-delayed assignment");
|
||||
// No reason to iterate.
|
||||
}
|
||||
|
||||
virtual void visit(AstSenItem* nodep) {
|
||||
if (!nodep->isClocked()) {
|
||||
nonOptimizable(nodep, "Non-clocked sensitivity");
|
||||
}
|
||||
iterateChildrenAlw(nodep, false);
|
||||
}
|
||||
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) {
|
||||
if (m_nonopt=="") { // Else accelerate
|
||||
iterateChildrenAlw(nodep, false);
|
||||
}
|
||||
}
|
||||
inline void iterateChildrenAlw(AstNode* nodep, bool under) {
|
||||
// **** USE THIS INSTEAD OF iterateChildren!
|
||||
// Note If visitor doesn't call here; does it its own way
|
||||
bool lastdua = m_directlyUnderAlw;
|
||||
AstVarScope* lastvscp = m_stmtVscp;
|
||||
bool lastpli = m_stmtInPli;
|
||||
m_directlyUnderAlw = under;
|
||||
if (VN_IS(nodep, NodeStmt)) { // Restored below
|
||||
UINFO(9," Stmt: "<<nodep<<endl);
|
||||
m_stmtVscp = NULL;
|
||||
m_stmtInPli = false;
|
||||
}
|
||||
if (!nodep->isPure() || nodep->isBrancher()) {
|
||||
// May also be a new statement (above if); if so we mark it immediately
|
||||
UINFO(9," NotPure "<<nodep<<endl);
|
||||
scoreboardPli(nodep);
|
||||
}
|
||||
{
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_directlyUnderAlw = lastdua;
|
||||
if (VN_IS(nodep, NodeStmt)) { // Reset what set above; else propagate up to above statement
|
||||
m_stmtVscp = lastvscp;
|
||||
m_stmtInPli = lastpli;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit GaterVisitor(AstNetlist* nodep) {
|
||||
// AstAlways visitor does the real work, so most zeroing needs to be in clear()
|
||||
clear();
|
||||
iterate(nodep);
|
||||
}
|
||||
void clear() {
|
||||
m_nonopt = "";
|
||||
m_directlyUnderAlw = false;
|
||||
m_ifDepth = 0;
|
||||
m_numIfs = 0;
|
||||
AstNode::user1ClearTree();
|
||||
AstNode::user2ClearTree();
|
||||
// Prepare graph
|
||||
m_graph.clear();
|
||||
GaterVertex::clearRank();
|
||||
m_pliVertexp = NULL;
|
||||
m_headVertexp = new GaterHeadVertex(&m_graph);
|
||||
m_aboveVertexp = m_headVertexp;
|
||||
m_aboveTrue = false;
|
||||
m_stmtVscp = NULL;
|
||||
m_stmtInPli = false;
|
||||
}
|
||||
virtual ~GaterVisitor() {
|
||||
V3Stats::addStat("Optimizations, Gaters inserted", m_statGaters);
|
||||
V3Stats::addStat("Optimizations, Gaters impacted bits", m_statBits);
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Active class functions
|
||||
|
||||
void V3ClkGater::clkGaterAll(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{
|
||||
// While the gater does well at some modules, it seems to slow down many others
|
||||
UINFO(5,"ClkGater is disabled due to performance issues\n");
|
||||
} // Destruct before checking
|
||||
//GaterVisitor visitor (nodep);
|
||||
V3Global::dumpCheckGlobalTree("clkgater", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Break always into clock gated blocks
|
||||
//
|
||||
// Code available from: http://www.veripool.org/verilator
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-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.
|
||||
//
|
||||
// Verilator is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef _V3CLKGATER_H_
|
||||
#define _V3CLKGATER_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3ClkGater {
|
||||
public:
|
||||
static void clkGaterAll(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
+10
-7
@@ -74,7 +74,7 @@ private:
|
||||
if (vscp->user1p()) return static_cast<AstVarScope*>(vscp->user1p());
|
||||
AstVar* varp = vscp->varp();
|
||||
if (!varp->width1()) varp->v3error("Unsupported: Clock edge on non-single bit signal: "
|
||||
<<varp->prettyName());
|
||||
<<varp->prettyNameQ());
|
||||
string newvarname = (string("__Vclklast__")
|
||||
+vscp->scopep()->nameDotless()+"__"+varp->name());
|
||||
AstVar* newvarp = new AstVar(vscp->fileline(),
|
||||
@@ -179,7 +179,7 @@ private:
|
||||
}
|
||||
AstIf* makeActiveIf(AstSenTree* sensesp) {
|
||||
AstNode* senEqnp = createSenseEquation(sensesp->sensesp());
|
||||
if (!senEqnp) sensesp->v3fatalSrc("No sense equation, shouldn't be in sequent activation.");
|
||||
UASSERT_OBJ(senEqnp, sensesp, "No sense equation, shouldn't be in sequent activation.");
|
||||
AstIf* newifp = new AstIf(sensesp->fileline(), senEqnp, NULL, NULL);
|
||||
return (newifp);
|
||||
}
|
||||
@@ -193,7 +193,8 @@ private:
|
||||
UINFO(4," TOPSCOPE "<<nodep<<endl);
|
||||
m_topScopep = nodep;
|
||||
m_scopep = nodep->scopep();
|
||||
if (!m_scopep) nodep->v3fatalSrc("No scope found on top level, perhaps you have no statements?");
|
||||
UASSERT_OBJ(m_scopep, nodep,
|
||||
"No scope found on top level, perhaps you have no statements?");
|
||||
//VV***** We reset all user1p()
|
||||
AstNode::user1ClearTree();
|
||||
// Make top functions
|
||||
@@ -263,7 +264,7 @@ private:
|
||||
m_scopep = nodep;
|
||||
iterateChildren(nodep);
|
||||
if (AstNode* movep = nodep->finalClksp()) {
|
||||
if (!m_topScopep) nodep->v3fatalSrc("Final clocks under non-top scope");
|
||||
UASSERT_OBJ(m_topScopep, nodep, "Final clocks under non-top scope");
|
||||
movep->unlinkFrBackWithNext();
|
||||
m_evalFuncp->addFinalsp(movep);
|
||||
}
|
||||
@@ -346,13 +347,14 @@ private:
|
||||
if (!m_topScopep || !nodep->stmtsp()) {
|
||||
// Not at the top or empty block...
|
||||
// Only empty blocks should be leftover on the non-top. Killem.
|
||||
if (nodep->stmtsp()) nodep->v3fatalSrc("Non-empty lower active");
|
||||
UASSERT_OBJ(!nodep->stmtsp(), nodep, "Non-empty lower active");
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
} else if (m_mtaskBodyp) {
|
||||
UINFO(4," TR ACTIVE "<<nodep<<endl);
|
||||
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||
if (nodep->hasClocked()) {
|
||||
if (nodep->hasInitial()) nodep->v3fatalSrc("Initial block should not have clock sensitivity");
|
||||
UASSERT_OBJ(!nodep->hasInitial(), nodep,
|
||||
"Initial block should not have clock sensitivity");
|
||||
if (m_lastSenp && nodep->sensesp()->sameTree(m_lastSenp)) {
|
||||
UINFO(4," sameSenseTree\n");
|
||||
} else {
|
||||
@@ -377,7 +379,8 @@ private:
|
||||
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||
if (nodep->hasClocked()) {
|
||||
// Remember the latest sensitivity so we can compare it next time
|
||||
if (nodep->hasInitial()) nodep->v3fatalSrc("Initial block should not have clock sensitivity");
|
||||
UASSERT_OBJ(!nodep->hasInitial(), nodep,
|
||||
"Initial block should not have clock sensitivity");
|
||||
if (m_lastSenp && nodep->sensesp()->sameTree(m_lastSenp)) {
|
||||
UINFO(4," sameSenseTree\n");
|
||||
} else {
|
||||
|
||||
+3
-2
@@ -96,7 +96,8 @@ public:
|
||||
AstCCall* callp = eqit->second;
|
||||
if (!callp->user3()) { // !already done
|
||||
UINFO(4, " Called "<<callp<<endl);
|
||||
if (callp->funcp() != oldfuncp) callp->v3fatalSrc("Call list broken, points to call w/different func");
|
||||
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
|
||||
@@ -236,7 +237,7 @@ private:
|
||||
V3Hash hashval = it->first;
|
||||
AstNode* node1p = it->second;
|
||||
if (!VN_IS(node1p, CFunc)) continue;
|
||||
if (hashval.isIllegal()) node1p->v3fatalSrc("Illegal (unhashed) nodes");
|
||||
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;
|
||||
|
||||
+3
-3
@@ -119,7 +119,7 @@ public:
|
||||
}
|
||||
inline void applyIgnores(FileLine* filelinep) {
|
||||
// HOT routine, called each parsed token line
|
||||
if (m_lastLineno != filelinep->lineno()
|
||||
if (m_lastLineno != filelinep->lastLineno()
|
||||
|| m_lastFilename != filelinep->filename()) {
|
||||
//UINFO(9," ApplyIgnores for "<<filelinep->ascii()<<endl);
|
||||
if (VL_UNLIKELY(m_lastFilename != filelinep->filename())) {
|
||||
@@ -127,7 +127,7 @@ public:
|
||||
m_lastFilename = filelinep->filename();
|
||||
}
|
||||
// Process all on/offs for lines up to and including the current line
|
||||
int curlineno = filelinep->lineno();
|
||||
int curlineno = filelinep->lastLineno();
|
||||
for (; m_lastIt != m_lastEnd; ++m_lastIt) {
|
||||
if (m_lastIt->m_lineno > curlineno) break;
|
||||
//UINFO(9," Hit "<<*m_lastIt<<endl);
|
||||
@@ -138,7 +138,7 @@ public:
|
||||
UINFO(9," NXT "<<*it<<endl);
|
||||
}
|
||||
}
|
||||
m_lastLineno = filelinep->lineno();
|
||||
m_lastLineno = filelinep->lastLineno();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
+23
-23
@@ -589,9 +589,8 @@ private:
|
||||
void replaceNum(AstNode* oldp, const V3Number& num) {
|
||||
// Replace oldp node with a constant set to specified value
|
||||
UASSERT(oldp, "Null old");
|
||||
if (VN_IS(oldp, Const) && !VN_CAST(oldp, Const)->num().isFourState()) {
|
||||
oldp->v3fatalSrc("Already constant??");
|
||||
}
|
||||
UASSERT_OBJ(!(VN_IS(oldp, Const) && !VN_CAST(oldp, Const)->num().isFourState()),
|
||||
oldp, "Already constant??");
|
||||
AstNode* newp = new AstConst(oldp->fileline(), num);
|
||||
newp->dtypeFrom(oldp);
|
||||
if (debug()>5) oldp->dumpTree(cout, " const_old: ");
|
||||
@@ -810,7 +809,8 @@ private:
|
||||
int rstart = rselp->lsbConst();
|
||||
int rwidth = rselp->widthConst();
|
||||
|
||||
if ((rstart + rwidth) != lstart) nodep->v3fatalSrc("tried to merge two selects which are not adjacent");
|
||||
UASSERT_OBJ((rstart + rwidth) == lstart, nodep,
|
||||
"tried to merge two selects which are not adjacent");
|
||||
AstSel* newselp = new AstSel(lselp->fromp()->fileline(),
|
||||
rselp->fromp()->cloneTree(false), rstart, lwidth+rwidth);
|
||||
UINFO(5, "merged two adjacent sel "<<lselp <<" and "<<rselp<< " to one "<<newselp<<endl);
|
||||
@@ -833,7 +833,7 @@ private:
|
||||
// use the lhs to replace the parent concat
|
||||
lp->lhsp()->replaceWith(newlp);
|
||||
lp->rhsp()->replaceWith(newrp);
|
||||
lp->dtypeChgWidthSigned(newlp->width(), newlp->width(), AstNumeric::fromBool(true));
|
||||
lp->dtypeChgWidthSigned(newlp->width(), newlp->width(), AstNumeric::UNSIGNED);
|
||||
UINFO(5, "merged "<< nodep <<endl);
|
||||
rp->unlinkFrBack()->deleteTree(); VL_DANGLING(rp);
|
||||
nodep->replaceWith(lp->unlinkFrBack()); nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
@@ -1066,7 +1066,7 @@ private:
|
||||
int msb2 = lsb2+lc2p->width()-1;
|
||||
int lsb1 = msb2+1;
|
||||
int msb1 = lsb1+lc1p->width()-1;
|
||||
if (msb1!=(conp->width()-1)) nodep->v3fatalSrc("Width calc mismatch");
|
||||
UASSERT_OBJ(msb1 == (conp->width()-1), nodep, "Width calc mismatch");
|
||||
// Form ranges
|
||||
AstSel* sel1p = new AstSel(conp->fileline(), rhsp, lsb1, msb1-lsb1+1);
|
||||
AstSel* sel2p = new AstSel(conp->fileline(), rhs2p, lsb2, msb2-lsb2+1);
|
||||
@@ -1081,7 +1081,7 @@ private:
|
||||
newp = AstNode::addNext(newp, asn1ap);
|
||||
newp = AstNode::addNext(newp, asn2ap);
|
||||
} else {
|
||||
if (!m_modp) nodep->v3fatalSrc("Not under module");
|
||||
UASSERT_OBJ(m_modp, nodep, "Not under module");
|
||||
// We could create just one temp variable, but we'll get better optimization
|
||||
// if we make one per term.
|
||||
string name1 = (string("__Vconcswap")+cvtToStr(m_modp->varNumGetInc()));
|
||||
@@ -1150,9 +1150,8 @@ private:
|
||||
AstNode* srcp = nodep->rhsp()->unlinkFrBack();
|
||||
// Connect the rhs to the stream operator and update its width
|
||||
VN_CAST(streamp, StreamL)->lhsp(srcp);
|
||||
streamp->dtypeSetLogicSized((srcp->width()),
|
||||
(srcp->widthMin()),
|
||||
AstNumeric::UNSIGNED);
|
||||
streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(),
|
||||
AstNumeric::UNSIGNED);
|
||||
// Shrink the RHS if necessary
|
||||
if (sWidth > dWidth) {
|
||||
streamp = new AstSel(streamp->fileline(), streamp, sWidth-dWidth, dWidth);
|
||||
@@ -1214,7 +1213,7 @@ private:
|
||||
new AstConst(nodep->fileline(), val),
|
||||
fromp);
|
||||
// widthMin no longer applicable if different C-expanded width
|
||||
newp->dtypeSetLogicSized(nodep->width(), nodep->width(), AstNumeric::UNSIGNED);
|
||||
newp->dtypeSetLogicSized(nodep->width(), AstNumeric::UNSIGNED);
|
||||
nodep->replaceWith(newp);
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
if (debug()>=9) newp->dumpTree(cout, " _new: ");
|
||||
@@ -1228,13 +1227,13 @@ private:
|
||||
AstNode* errorp = simvis.whyNotNodep(); if (!errorp) errorp = nodep;
|
||||
nodep->v3error("Expecting expression to be constant, but can't determine constant for "
|
||||
<<nodep->prettyTypeName()<<endl
|
||||
<<errorp->warnMore()<<"... Location of non-constant "
|
||||
<<errorp->warnOther()<<"... Location of non-constant "
|
||||
<<errorp->prettyTypeName()<<": "<<simvis.whyNotMessage());
|
||||
replaceZero(nodep); VL_DANGLING(nodep);
|
||||
} else {
|
||||
// Fetch the result
|
||||
V3Number* outnump = simvis.fetchNumberNull(nodep);
|
||||
if (!outnump) nodep->v3fatalSrc("No number returned from simulation");
|
||||
UASSERT_OBJ(outnump, nodep, "No number returned from simulation");
|
||||
// Replace it
|
||||
replaceNum(nodep,*outnump); VL_DANGLING(nodep);
|
||||
}
|
||||
@@ -1440,7 +1439,7 @@ private:
|
||||
AstNode* fromp = repp->lhsp();
|
||||
AstConst* lsbp = VN_CAST(nodep->lsbp(), Const); if (!lsbp) return false;
|
||||
AstNode* widthp = nodep->widthp(); if (!VN_IS(widthp, Const)) return false;
|
||||
if (!fromp->width()) nodep->v3fatalSrc("Not widthed");
|
||||
UASSERT_OBJ(fromp->width(), nodep, "Not widthed");
|
||||
if ((lsbp->toUInt() / fromp->width())
|
||||
!= ((lsbp->toUInt()+nodep->width()-1) / fromp->width())) return false;
|
||||
//
|
||||
@@ -1500,7 +1499,7 @@ private:
|
||||
void replaceSelIntoBiop(AstSel* nodep) {
|
||||
// SEL(BUFIF1(a,b),1,bit) => BUFIF1(SEL(a,1,bit),SEL(b,1,bit))
|
||||
AstNodeBiop* fromp = VN_CAST(nodep->fromp()->unlinkFrBack(), NodeBiop);
|
||||
if (!fromp) nodep->v3fatalSrc("Called on non biop");
|
||||
UASSERT_OBJ(fromp, nodep, "Called on non biop");
|
||||
AstNode* lsbp = nodep->lsbp()->unlinkFrBack();
|
||||
AstNode* widthp = nodep->widthp()->unlinkFrBack();
|
||||
//
|
||||
@@ -1517,7 +1516,7 @@ private:
|
||||
void replaceSelIntoUniop(AstSel* nodep) {
|
||||
// SEL(NOT(a),1,bit) => NOT(SEL(a,bit))
|
||||
AstNodeUniop* fromp = VN_CAST(nodep->fromp()->unlinkFrBack(), NodeUniop);
|
||||
if (!fromp) nodep->v3fatalSrc("Called on non biop");
|
||||
UASSERT_OBJ(fromp, nodep, "Called on non biop");
|
||||
AstNode* lsbp = nodep->lsbp()->unlinkFrBack();
|
||||
AstNode* widthp = nodep->widthp()->unlinkFrBack();
|
||||
//
|
||||
@@ -1564,7 +1563,7 @@ private:
|
||||
}
|
||||
virtual void visit(AstNodeVarRef* nodep) {
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->varp()) nodep->v3fatalSrc("Not linked");
|
||||
UASSERT_OBJ(nodep->varp(), nodep, "Not linked");
|
||||
bool did = false;
|
||||
if (m_doV && nodep->varp()->valuep() && !m_attrp) {
|
||||
//if (debug()) valuep->dumpTree(cout, " visitvaref: ");
|
||||
@@ -1622,15 +1621,15 @@ private:
|
||||
}
|
||||
if (!did && m_required) {
|
||||
nodep->v3error("Expecting expression to be constant, but variable isn't const: "
|
||||
<<nodep->varp()->prettyName());
|
||||
<<nodep->varp()->prettyNameQ());
|
||||
}
|
||||
}
|
||||
virtual void visit(AstEnumItemRef* nodep) {
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->itemp()) nodep->v3fatalSrc("Not linked");
|
||||
UASSERT_OBJ(nodep->itemp(), nodep, "Not linked");
|
||||
bool did = false;
|
||||
if (nodep->itemp()->valuep()) {
|
||||
//if (debug()) nodep->varp()->valuep()->dumpTree(cout, " visitvaref: ");
|
||||
//if (debug()) nodep->itemp()->valuep()->dumpTree(cout, " visitvaref: ");
|
||||
iterateAndNextNull(nodep->itemp()->valuep());
|
||||
if (AstConst* valuep = VN_CAST(nodep->itemp()->valuep(), Const)) {
|
||||
const V3Number& num = valuep->num();
|
||||
@@ -1640,7 +1639,7 @@ private:
|
||||
}
|
||||
if (!did && m_required) {
|
||||
nodep->v3error("Expecting expression to be constant, but variable isn't const: "
|
||||
<<nodep->itemp()->prettyName());
|
||||
<<nodep->itemp()->prettyNameQ());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1682,7 +1681,7 @@ private:
|
||||
UINFO(8,"senItem(NOT...) "<<nodep<<" "<<invert<<endl);
|
||||
if (invert) nodep->edgeType( nodep->edgeType().invert() );
|
||||
AstNodeVarRef* senvarp = VN_CAST(lastSensp->unlinkFrBack(), NodeVarRef);
|
||||
if (!senvarp) sensp->v3fatalSrc("Non-varref sensitivity variable");
|
||||
UASSERT_OBJ(senvarp, sensp, "Non-varref sensitivity variable");
|
||||
sensp->replaceWith(senvarp);
|
||||
sensp->deleteTree(); VL_DANGLING(sensp);
|
||||
} else if (!m_doNConst // Deal with later when doNConst missing
|
||||
@@ -1690,7 +1689,8 @@ private:
|
||||
|| VN_IS(nodep->sensp(), Const))) {
|
||||
} else if (nodep->isIllegal()) { // Deal with later
|
||||
} else {
|
||||
if (nodep->hasVar() && !nodep->varrefp()) nodep->v3fatalSrc("Null sensitivity variable");
|
||||
UASSERT_OBJ(!(nodep->hasVar() && !nodep->varrefp()), nodep,
|
||||
"Null sensitivity variable");
|
||||
}
|
||||
}
|
||||
virtual void visit(AstSenGate* nodep) {
|
||||
|
||||
@@ -81,7 +81,7 @@ private:
|
||||
// duplicated), not the covertoggle, but we need to get back to the
|
||||
// covertoggle which is immediately above, so:
|
||||
AstCoverToggle* removep = VN_CAST(duporigp->backp(), CoverToggle);
|
||||
if (!removep) nodep->v3fatalSrc("CoverageJoin duplicate of wrong type");
|
||||
UASSERT_OBJ(removep, nodep, "CoverageJoin duplicate of wrong type");
|
||||
UINFO(8," Orig "<<nodep<<" -->> "<<nodep->incp()->declp()<<endl);
|
||||
UINFO(8," dup "<<removep<<" -->> "<<removep->incp()->declp()<<endl);
|
||||
// The CoverDecl the duplicate pointed to now needs to point to the
|
||||
|
||||
+18
-15
@@ -114,13 +114,13 @@ private:
|
||||
nodep->user5( nodep->user5() | flags );
|
||||
if ((nodep->user5() & VU_DLY) && (nodep->user5() & VU_NONDLY)) {
|
||||
nodep->v3warn(BLKANDNBLK, "Unsupported: Blocked and non-blocking assignments to same variable: "
|
||||
<<nodep->varp()->prettyName());
|
||||
<<nodep->varp()->prettyNameQ());
|
||||
}
|
||||
}
|
||||
AstVarScope* createVarSc(AstVarScope* oldvarscp, const string& name,
|
||||
int width/*0==fromoldvar*/, AstNodeDType* newdtypep) {
|
||||
// Because we've already scoped it, we may need to add both the AstVar and the AstVarScope
|
||||
if (!oldvarscp->scopep()) oldvarscp->v3fatalSrc("Var unscoped");
|
||||
UASSERT_OBJ(oldvarscp->scopep(), oldvarscp, "Var unscoped");
|
||||
AstVar* varp;
|
||||
AstNodeModule* addmodp = oldvarscp->scopep()->modp();
|
||||
// We need a new AstVar, but only one for all scopes, to match the new AstVarScope
|
||||
@@ -158,15 +158,18 @@ private:
|
||||
}
|
||||
void checkActivePost(AstVarRef* varrefp, AstActive* oldactivep) {
|
||||
// Check for MULTIDRIVEN, and if so make new sentree that joins old & new sentree
|
||||
if (!oldactivep) varrefp->v3fatalSrc("<= old dly assignment not put under sensitivity block");
|
||||
UASSERT_OBJ(oldactivep, varrefp, "<= old dly assignment not put under sensitivity block");
|
||||
if (oldactivep->sensesp() != m_activep->sensesp()) {
|
||||
if (!varrefp->varp()->fileline()->warnIsOff(V3ErrorCode::MULTIDRIVEN)
|
||||
&& !varrefp->varp()->user2()) {
|
||||
varrefp->varp()->v3warn(
|
||||
MULTIDRIVEN, "Signal has multiple driving blocks with different clocking: "
|
||||
<<varrefp->varp()->prettyName()<<endl
|
||||
<<varrefp->warnMore()<<"... Location of first driving block"<<endl
|
||||
<<oldactivep->warnMore()<<"... Location of other driving block");
|
||||
<<varrefp->varp()->prettyNameQ()<<endl
|
||||
<<varrefp->warnOther()<<"... Location of first driving block"<<endl
|
||||
<<varrefp->warnContextPrimary()<<endl
|
||||
<<oldactivep->warnOther()<<"... Location of other driving block"<<endl
|
||||
<<oldactivep->warnContextSecondary()
|
||||
);
|
||||
varrefp->varp()->user2(true);
|
||||
}
|
||||
UINFO(4,"AssignDupDlyVar: "<<varrefp<<endl);
|
||||
@@ -200,10 +203,9 @@ private:
|
||||
} else {
|
||||
arrayselp = VN_CAST(lhsp, ArraySel);
|
||||
}
|
||||
if (!arrayselp) nodep->v3fatalSrc("No arraysel under bitsel?");
|
||||
if (VN_IS(arrayselp->dtypep()->skipRefp(), UnpackArrayDType)) {
|
||||
nodep->v3fatalSrc("ArraySel with unpacked arrays should have been removed in V3Slice");
|
||||
}
|
||||
UASSERT_OBJ(arrayselp, nodep, "No arraysel under bitsel?");
|
||||
UASSERT_OBJ(!VN_IS(arrayselp->dtypep()->skipRefp(), UnpackArrayDType), nodep,
|
||||
"ArraySel with unpacked arrays should have been removed in V3Slice");
|
||||
UINFO(4,"AssignDlyArray: "<<nodep<<endl);
|
||||
//
|
||||
//=== Dimensions: __Vdlyvdim__
|
||||
@@ -214,8 +216,8 @@ private:
|
||||
dimvalp.push_front(valp);
|
||||
}
|
||||
AstVarRef* varrefp = VN_CAST(dimselp, VarRef);
|
||||
if (!varrefp) nodep->v3fatalSrc("No var underneath arraysels");
|
||||
if (!varrefp->varScopep()) varrefp->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp");
|
||||
UASSERT_OBJ(varrefp, nodep, "No var underneath arraysels");
|
||||
UASSERT_OBJ(varrefp->varScopep(), varrefp, "Var didn't get varscoped in V3Scope.cpp");
|
||||
varrefp->unlinkFrBack();
|
||||
AstVar* oldvarp = varrefp->varp();
|
||||
int modVecNum = m_scopeVecMap[varrefp->varScopep()]++;
|
||||
@@ -330,7 +332,8 @@ private:
|
||||
// Optimize as above; if sharing Vdlyvset *ON SAME VARIABLE*,
|
||||
// we can share the IF statement too
|
||||
postLogicp = VN_CAST(finalp->user4p(), If);
|
||||
if (!postLogicp) nodep->v3fatalSrc("Delayed assignment misoptimized; prev var found w/o associated IF");
|
||||
UASSERT_OBJ(postLogicp, nodep,
|
||||
"Delayed assignment misoptimized; prev var found w/o associated IF");
|
||||
} else {
|
||||
postLogicp = new AstIf(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), setvscp, false),
|
||||
@@ -397,12 +400,12 @@ private:
|
||||
if (m_inDly && nodep->lvalue()) {
|
||||
UINFO(4,"AssignDlyVar: "<<nodep<<endl);
|
||||
markVarUsage(nodep->varScopep(), VU_DLY);
|
||||
if (!m_activep) nodep->v3fatalSrc("<= not under sensitivity block");
|
||||
UASSERT_OBJ(m_activep, nodep, "<= not under sensitivity block");
|
||||
if (!m_activep->hasClocked()) {
|
||||
nodep->v3error("Internal: Blocking <= assignment in non-clocked block, should have converted in V3Active");
|
||||
}
|
||||
AstVarScope* oldvscp = nodep->varScopep();
|
||||
if (!oldvscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp");
|
||||
UASSERT_OBJ(oldvscp, nodep, "Var didn't get varscoped in V3Scope.cpp");
|
||||
AstVarScope* dlyvscp = VN_CAST(oldvscp->user1p(), VarScope);
|
||||
if (dlyvscp) { // Multiple use of delayed variable
|
||||
AstActive* oldactivep = VN_CAST(dlyvscp->user2p(), Active);
|
||||
|
||||
+2
-2
@@ -66,7 +66,7 @@ private:
|
||||
// bitmask instead of widths....)
|
||||
// See t_func_crc for an example test that requires this
|
||||
VFlagLogicPacked(), nodep->width());
|
||||
if (!m_funcp) nodep->v3fatalSrc("Deep expression not under a function");
|
||||
UASSERT_OBJ(m_funcp, nodep, "Deep expression not under a function");
|
||||
m_funcp->addInitsp(varp);
|
||||
// Replace node tree with reference to var
|
||||
AstVarRef* newp = new AstVarRef(nodep->fileline(), varp, false);
|
||||
@@ -134,7 +134,7 @@ private:
|
||||
// Marking of non-static functions (because they might need "this")
|
||||
// (Here instead of new vistor after V3Descope just to avoid another visitor)
|
||||
void needNonStaticFunc(AstNode* nodep) {
|
||||
if (!m_funcp) nodep->v3fatalSrc("Non-static accessor not under a function");
|
||||
UASSERT_OBJ(m_funcp, nodep, "Non-static accessor not under a function");
|
||||
if (m_funcp->isStatic().trueU()) {
|
||||
UINFO(5,"Mark non-public due to "<<nodep<<endl);
|
||||
m_funcp->isStatic(false);
|
||||
|
||||
+4
-4
@@ -182,7 +182,7 @@ private:
|
||||
AstCFunc* funcp = eachIt->second;
|
||||
FuncMmap::iterator nextIt2 = eachIt; ++nextIt2;
|
||||
bool moreOfSame = (nextIt2!=m_modFuncs.end() && nextIt2->first == name);
|
||||
if (!funcp->scopep()) funcp->v3fatalSrc("Not scoped");
|
||||
UASSERT_OBJ(funcp->scopep(), funcp, "Not scoped");
|
||||
|
||||
UINFO(6," Wrapping "<<name<<" "<<funcp<<endl);
|
||||
UINFO(6," at "<<newfuncp->argTypes()<<" und "<<funcp->argTypes()<<endl);
|
||||
@@ -255,7 +255,7 @@ private:
|
||||
virtual void visit(AstNodeVarRef* nodep) {
|
||||
iterateChildren(nodep);
|
||||
// Convert the hierch name
|
||||
if (!m_scopep) nodep->v3fatalSrc("Node not under scope");
|
||||
UASSERT_OBJ(m_scopep, nodep, "Node not under scope");
|
||||
bool hierThis;
|
||||
nodep->hiername(descopedName(nodep->varScopep()->scopep(), hierThis/*ref*/,
|
||||
nodep->varScopep()->varp()));
|
||||
@@ -266,8 +266,8 @@ private:
|
||||
//UINFO(9," "<<nodep<<endl);
|
||||
iterateChildren(nodep);
|
||||
// Convert the hierch name
|
||||
if (!m_scopep) nodep->v3fatalSrc("Node not under scope");
|
||||
if (!nodep->funcp()->scopep()) nodep->v3fatalSrc("CFunc not under scope");
|
||||
UASSERT_OBJ(m_scopep, nodep, "Node not under scope");
|
||||
UASSERT_OBJ(nodep->funcp()->scopep(), nodep, "CFunc not under scope");
|
||||
bool hierThis;
|
||||
nodep->hiername(descopedName(nodep->funcp()->scopep(), hierThis/*ref*/));
|
||||
// Can't do this, as we may have more calls later
|
||||
|
||||
+37
-39
@@ -89,7 +89,7 @@ public:
|
||||
puts(nodep->dtypep()->charIQWN());
|
||||
}
|
||||
void emitScIQW(AstVar* nodep) {
|
||||
if (!nodep->isSc()) nodep->v3fatalSrc("emitting SystemC operator on non-SC variable");
|
||||
UASSERT_OBJ(nodep->isSc(), nodep, "emitting SystemC operator on non-SC variable");
|
||||
puts(nodep->isScBigUint() ? "SB"
|
||||
: nodep->isScUint() ? "SU"
|
||||
: nodep->isScBv() ? "SW"
|
||||
@@ -632,9 +632,9 @@ public:
|
||||
}
|
||||
virtual void visit(AstReplicate* nodep) {
|
||||
if (nodep->lhsp()->widthMin() == 1 && !nodep->isWide()) {
|
||||
if ((static_cast<int>(VN_CAST(nodep->rhsp(), Const)->toUInt())
|
||||
* nodep->lhsp()->widthMin()) != nodep->widthMin())
|
||||
nodep->v3fatalSrc("Replicate non-constant or width miscomputed");
|
||||
UASSERT_OBJ((static_cast<int>(VN_CAST(nodep->rhsp(), Const)->toUInt())
|
||||
* nodep->lhsp()->widthMin()) == nodep->widthMin(),
|
||||
nodep, "Replicate non-constant or width miscomputed");
|
||||
puts("VL_REPLICATE_");
|
||||
emitIQW(nodep);
|
||||
puts("OI(");
|
||||
@@ -798,7 +798,7 @@ public:
|
||||
}
|
||||
virtual void visit(AstConst* nodep) {
|
||||
if (nodep->isWide()) {
|
||||
if (!m_wideTempRefp) nodep->v3fatalSrc("Wide Constant w/ no temp");
|
||||
UASSERT_OBJ(m_wideTempRefp, nodep, "Wide Constant w/ no temp");
|
||||
emitConstant(nodep, m_wideTempRefp, "");
|
||||
m_wideTempRefp = NULL; // We used it, barf if set it a second time
|
||||
} else {
|
||||
@@ -909,8 +909,8 @@ class EmitCImp : EmitCStmts {
|
||||
else {
|
||||
AstNode* lhsp = changep->lhsp();
|
||||
AstNode* rhsp = changep->rhsp();
|
||||
if (!VN_IS(lhsp, VarRef) && !VN_IS(lhsp, ArraySel)) changep->v3fatalSrc("Not ref?");
|
||||
if (!VN_IS(rhsp, VarRef) && !VN_IS(rhsp, ArraySel)) changep->v3fatalSrc("Not ref?");
|
||||
UASSERT_OBJ(VN_IS(lhsp, VarRef) || VN_IS(lhsp, ArraySel), changep, "Not ref?");
|
||||
UASSERT_OBJ(VN_IS(rhsp, VarRef) || VN_IS(rhsp, ArraySel), changep, "Not ref?");
|
||||
for (int word=0;
|
||||
word < (changep->lhsp()->isWide() ? changep->lhsp()->widthWords() : 1);
|
||||
++word) {
|
||||
@@ -1164,9 +1164,8 @@ class EmitCImp : EmitCStmts {
|
||||
}
|
||||
|
||||
virtual void visit(AstExecGraph* nodep) {
|
||||
if (nodep != v3Global.rootp()->execGraphp()) {
|
||||
nodep->v3fatalSrc("ExecGraph should be a singleton!");
|
||||
}
|
||||
UASSERT_OBJ(nodep == v3Global.rootp()->execGraphp(), nodep,
|
||||
"ExecGraph should be a singleton!");
|
||||
// The location of the AstExecGraph within the containing _eval()
|
||||
// function is where we want to invoke the graph and wait for it to
|
||||
// complete. Do that now.
|
||||
@@ -1185,10 +1184,8 @@ class EmitCImp : EmitCStmts {
|
||||
const ExecMTask* etp = dynamic_cast<const ExecMTask*>(vxp);
|
||||
if (etp->threadRoot()) execMTasks.push_back(etp);
|
||||
}
|
||||
if (execMTasks.size() >
|
||||
static_cast<unsigned>(v3Global.opt.threads())) {
|
||||
nodep->v3fatalSrc("More root mtasks than available threads");
|
||||
}
|
||||
UASSERT_OBJ(execMTasks.size() <= static_cast<unsigned>(v3Global.opt.threads()),
|
||||
nodep, "More root mtasks than available threads");
|
||||
|
||||
if (!execMTasks.empty()) {
|
||||
for (uint32_t i = 0; i < execMTasks.size(); ++i) {
|
||||
@@ -1256,7 +1253,7 @@ public:
|
||||
|
||||
void EmitCStmts::emitVarDecl(const AstVar* nodep, const string& prefixIfImp) {
|
||||
AstBasicDType* basicp = nodep->basicp();
|
||||
if (!basicp) nodep->v3fatalSrc("Unimplemented: Outputting this data type");
|
||||
UASSERT_OBJ(basicp, nodep, "Unimplemented: Outputting this data type");
|
||||
if (nodep->isIO()) {
|
||||
if (nodep->isSc()) {
|
||||
m_ctorVarsVec.push_back(nodep);
|
||||
@@ -1417,7 +1414,8 @@ void EmitCStmts::emitOpName(AstNode* nodep, const string& format,
|
||||
case 't': detail = true; detailp = thsp; break;
|
||||
case 'P':
|
||||
if (nodep->isWide()) {
|
||||
if (!m_wideTempRefp) nodep->v3fatalSrc("Wide Op w/ no temp, perhaps missing op in V3EmitC?");
|
||||
UASSERT_OBJ(m_wideTempRefp, nodep,
|
||||
"Wide Op w/ no temp, perhaps missing op in V3EmitC?");
|
||||
COMMA;
|
||||
puts(m_wideTempRefp->hiername());
|
||||
puts(m_wideTempRefp->varp()->name());
|
||||
@@ -1448,8 +1446,8 @@ void EmitCStmts::emitOpName(AstNode* nodep, const string& format,
|
||||
break;
|
||||
case 'i':
|
||||
COMMA;
|
||||
if (!detailp) { nodep->v3fatalSrc("emitOperator() references undef node"); }
|
||||
else iterateAndNextNull(detailp);
|
||||
UASSERT_OBJ(detailp, nodep, "emitOperator() references undef node");
|
||||
iterateAndNextNull(detailp);
|
||||
needComma = true;
|
||||
break;
|
||||
default:
|
||||
@@ -1570,10 +1568,10 @@ void EmitCStmts::displayArg(AstNode* dispp, AstNode** elistp, bool isScan,
|
||||
const string& vfmt, char fmtLetter) {
|
||||
// Print display argument, edits elistp
|
||||
AstNode* argp = *elistp;
|
||||
if (!argp) {
|
||||
if (VL_UNCOVERABLE(!argp)) {
|
||||
// expectDisplay() checks this first, so internal error if found here
|
||||
dispp->v3error("Internal: Missing arguments for $display-like format");
|
||||
return;
|
||||
dispp->v3error("Internal: Missing arguments for $display-like format"); // LCOV_EXCL_LINE
|
||||
return; // LCOV_EXCL_LINE
|
||||
}
|
||||
if (argp->widthMin() > VL_VALUE_STRING_MAX_WIDTH) {
|
||||
dispp->v3error("Exceeded limit of "+cvtToStr(VL_VALUE_STRING_MAX_WIDTH)+" bits for any $display-like arguments");
|
||||
@@ -1654,7 +1652,7 @@ void EmitCStmts::displayNode(AstNode* nodep, AstScopeName* scopenamep,
|
||||
case '^': displayArg(nodep,&elistp,isScan, vfmt,'^'); break; // Realtime
|
||||
case 'v': displayArg(nodep, &elistp, isScan, vfmt, 'v'); break;
|
||||
case 'm': {
|
||||
if (!scopenamep) nodep->v3fatalSrc("Display with %m but no AstScopeName");
|
||||
UASSERT_OBJ(scopenamep, nodep, "Display with %m but no AstScopeName");
|
||||
string suffix = scopenamep->scopePrettySymName();
|
||||
if (suffix=="") emitDispState.pushFormat("%S");
|
||||
else emitDispState.pushFormat("%N"); // Add a . when needed
|
||||
@@ -1668,14 +1666,14 @@ void EmitCStmts::displayNode(AstNode* nodep, AstScopeName* scopenamep,
|
||||
break;
|
||||
}
|
||||
default:
|
||||
nodep->v3error("Unknown $display-like format code: %"<<pos[0]);
|
||||
nodep->v3error("Unknown $display-like format code: '%"<<pos[0]<<"'");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (elistp != NULL) {
|
||||
if (VL_UNCOVERABLE(elistp)) {
|
||||
// expectFormat also checks this, and should have found it first, so internal
|
||||
elistp->v3error("Internal: Extra arguments for $display-like format");
|
||||
elistp->v3error("Internal: Extra arguments for $display-like format"); // LCOV_EXCL_LINE
|
||||
}
|
||||
displayEmit(nodep, isScan);
|
||||
}
|
||||
@@ -1687,7 +1685,7 @@ void EmitCImp::emitVarReset(AstVar* varp) {
|
||||
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()) {
|
||||
if (!varp->valuep()) varp->v3fatalSrc("No init for a param?");
|
||||
UASSERT_OBJ(varp->valuep(), varp, "No init for a param?");
|
||||
// If a simple CONST value we initialize it using an enum
|
||||
// If an ARRAYINIT we initialize it using an initial block similar to a signal
|
||||
//puts("// parameter "+varp->name()+" = "+varp->valuep()->name()+"\n");
|
||||
@@ -1705,7 +1703,8 @@ void EmitCImp::emitVarReset(AstVar* varp) {
|
||||
int pos = 0;
|
||||
for (AstNode* itemp = initarp->initsp(); itemp; ++pos, itemp=itemp->nextp()) {
|
||||
int index = initarp->posIndex(pos);
|
||||
if (!initarp->defaultp() && index!=pos) initarp->v3fatalSrc("Not enough values in array initalizement");
|
||||
UASSERT_OBJ(initarp->defaultp() || index==pos, initarp,
|
||||
"Not enough values in array initalizement");
|
||||
emitSetVarConstant(varp->name()+"["+cvtToStr(index)+"]", VN_CAST(itemp, Const));
|
||||
}
|
||||
} else {
|
||||
@@ -1722,7 +1721,8 @@ void EmitCImp::emitVarReset(AstVar* varp) {
|
||||
arrayp;
|
||||
arrayp = VN_CAST(arrayp->subDTypep()->skipRefp(), UnpackArrayDType)) {
|
||||
int vecnum = vects++;
|
||||
if (arrayp->msb() < arrayp->lsb()) varp->v3fatalSrc("Should have swapped msb & lsb earlier.");
|
||||
UASSERT_OBJ(arrayp->msb() >= arrayp->lsb(), varp,
|
||||
"Should have swapped msb & lsb earlier.");
|
||||
string ivar = string("__Vi")+cvtToStr(vecnum);
|
||||
// MSVC++ pre V7 doesn't support 'for (int ...)', so declare in sep block
|
||||
puts("{ int __Vi"+cvtToStr(vecnum)+"="+cvtToStr(0)+";");
|
||||
@@ -1778,7 +1778,7 @@ void EmitCImp::emitCoverageDecl(AstNodeModule* modp) {
|
||||
|
||||
void EmitCImp::emitMTaskVertexCtors(bool* firstp) {
|
||||
AstExecGraph* execGraphp = v3Global.rootp()->execGraphp();
|
||||
if (!execGraphp) v3Global.rootp()->v3fatalSrc("Should have an execGraphp");
|
||||
UASSERT_OBJ(execGraphp, v3Global.rootp(), "Root should have an execGraphp");
|
||||
const V3Graph* depGraphp = execGraphp->depGraphp();
|
||||
|
||||
unsigned finalEdgesInCt = 0;
|
||||
@@ -1960,9 +1960,8 @@ void EmitCImp::emitSavableImp(AstNodeModule* modp) {
|
||||
arrayp;
|
||||
arrayp = VN_CAST(elementp, UnpackArrayDType)) {
|
||||
int vecnum = vects++;
|
||||
if (arrayp->msb() < arrayp->lsb()) {
|
||||
varp->v3fatalSrc("Should have swapped msb & lsb earlier.");
|
||||
}
|
||||
UASSERT_OBJ(arrayp->msb() >= arrayp->lsb(), varp,
|
||||
"Should have swapped msb & lsb earlier.");
|
||||
string ivar = string("__Vi")+cvtToStr(vecnum);
|
||||
// MSVC++ pre V7 doesn't support 'for (int ...)',
|
||||
// so declare in sep block
|
||||
@@ -2060,9 +2059,8 @@ void EmitCImp::emitSensitives() {
|
||||
arrayp;
|
||||
arrayp = VN_CAST(arrayp->subDTypep()->skipRefp(), UnpackArrayDType)) {
|
||||
int vecnum = vects++;
|
||||
if (arrayp->msb() < arrayp->lsb()) {
|
||||
varp->v3fatalSrc("Should have swapped msb & lsb earlier.");
|
||||
}
|
||||
UASSERT_OBJ(arrayp->msb() >= arrayp->lsb(), varp,
|
||||
"Should have swapped msb & lsb earlier.");
|
||||
string ivar = string("__Vi")+cvtToStr(vecnum);
|
||||
// MSVC++ pre V7 doesn't support 'for (int ...)', so declare in sep block
|
||||
puts("{ int __Vi"+cvtToStr(vecnum)+"="+cvtToStr(arrayp->lsb())+";");
|
||||
@@ -2111,7 +2109,7 @@ void EmitCImp::emitWrapEval(AstNodeModule* modp) {
|
||||
puts("#ifdef VL_DEBUG\n");
|
||||
putsDecoration("// Debug assertions\n");
|
||||
puts("_eval_debug_assertions();\n");
|
||||
puts("#endif // VL_DEBUG\n");
|
||||
puts("#endif // VL_DEBUG\n");
|
||||
putsDecoration("// Initialize\n");
|
||||
puts("if (VL_UNLIKELY(!vlSymsp->__Vm_didInit)) _eval_initial_loop(vlSymsp);\n");
|
||||
if (v3Global.opt.inhibitSim()) {
|
||||
@@ -2402,7 +2400,7 @@ void EmitCImp::emitIntFuncDecls(AstNodeModule* modp) {
|
||||
if (modp->isTop() && v3Global.opt.mtasks()) {
|
||||
// Emit the mtask func prototypes.
|
||||
AstExecGraph* execGraphp = v3Global.rootp()->execGraphp();
|
||||
if (!execGraphp) v3Global.rootp()->v3fatalSrc("Root should have an execGraphp");
|
||||
UASSERT_OBJ(execGraphp, v3Global.rootp(), "Root should have an execGraphp");
|
||||
const V3Graph* depGraphp = execGraphp->depGraphp();
|
||||
for (const V3GraphVertex* vxp = depGraphp->verticesBeginp();
|
||||
vxp; vxp = vxp->verticesNextp()) {
|
||||
@@ -2422,7 +2420,7 @@ void EmitCImp::emitIntFuncDecls(AstNodeModule* modp) {
|
||||
void EmitCImp::emitMTaskState() {
|
||||
ofp()->putsPrivate(true);
|
||||
AstExecGraph* execGraphp = v3Global.rootp()->execGraphp();
|
||||
if (!execGraphp) v3Global.rootp()->v3fatalSrc("Root should have an execGraphp");
|
||||
UASSERT_OBJ(execGraphp, v3Global.rootp(), "Root should have an execGraphp");
|
||||
|
||||
const V3Graph* depGraphp = execGraphp->depGraphp();
|
||||
for (const V3GraphVertex* vxp = depGraphp->verticesBeginp();
|
||||
@@ -2564,7 +2562,7 @@ void EmitCImp::emitInt(AstNodeModule* modp) {
|
||||
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (const AstVar* varp = VN_CAST(nodep, Var)) {
|
||||
if (varp->isParam() && (varp->isUsedParam() || varp->isSigPublic())) {
|
||||
if (!varp->valuep()) nodep->v3fatalSrc("No init for a param?");
|
||||
UASSERT_OBJ(varp->valuep(), nodep, "No init for a param?");
|
||||
// These should be static const values, however microsloth VC++ doesn't
|
||||
// support them. They also cause problems with GDB under GCC2.95.
|
||||
if (varp->isWide()) { // Unsupported for output
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ class V3EmitC {
|
||||
public:
|
||||
static void emitc();
|
||||
static void emitcInlines();
|
||||
static void emitcSyms();
|
||||
static void emitcSyms(bool dpiHdrOnly = false);
|
||||
static void emitcTrace();
|
||||
};
|
||||
|
||||
|
||||
+15
-9
@@ -90,6 +90,7 @@ class EmitCSyms : EmitCBaseVisitor {
|
||||
V3LanguageWords m_words; // Reserved word detector
|
||||
int m_coverBins; // Coverage bin number
|
||||
int m_labelNum; // Next label number
|
||||
bool m_dpiHdrOnly; // Only emit the DPI header
|
||||
|
||||
// METHODS
|
||||
void emitSymHdr();
|
||||
@@ -106,7 +107,7 @@ class EmitCSyms : EmitCBaseVisitor {
|
||||
// We'll still check here because the compiler errors
|
||||
// resulting if we miss this warning are SO nasty
|
||||
nodep->v3error("Symbol matching "+rsvd+" reserved word reached emitter,"
|
||||
" should have hit SYMRSVDWORD: '"<<nodep->prettyName()<<"'");
|
||||
" should have hit SYMRSVDWORD: "<<nodep->prettyNameQ());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -181,11 +182,13 @@ class EmitCSyms : EmitCBaseVisitor {
|
||||
stable_sort(m_dpis.begin(), m_dpis.end(), CmpDpi());
|
||||
|
||||
// Output
|
||||
emitSymHdr();
|
||||
emitSymImp();
|
||||
if (!m_dpiHdrOnly) {
|
||||
emitSymHdr();
|
||||
emitSymImp();
|
||||
}
|
||||
if (v3Global.dpi()) {
|
||||
emitDpiHdr();
|
||||
emitDpiImp();
|
||||
if (!m_dpiHdrOnly) emitDpiImp();
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
@@ -206,7 +209,7 @@ class EmitCSyms : EmitCBaseVisitor {
|
||||
m_scopeNames.insert(make_pair(name, ScopeNameData(name, nodep->scopePrettySymName())));
|
||||
}
|
||||
if (nodep->dpiExport()) {
|
||||
if (!m_funcp) nodep->v3fatalSrc("ScopeName not under DPI function");
|
||||
UASSERT_OBJ(m_funcp, nodep, "ScopeName not under DPI function");
|
||||
m_scopeFuncs.insert(make_pair(name + " " + m_funcp->name(),
|
||||
ScopeFuncData(nodep, m_funcp, m_modp)));
|
||||
} else {
|
||||
@@ -253,7 +256,9 @@ class EmitCSyms : EmitCBaseVisitor {
|
||||
//---------------------------------------
|
||||
// ACCESSORS
|
||||
public:
|
||||
explicit EmitCSyms(AstNetlist* nodep) {
|
||||
explicit EmitCSyms(AstNetlist* nodep, bool dpiHdrOnly):
|
||||
m_dpiHdrOnly(dpiHdrOnly)
|
||||
{
|
||||
m_funcp = NULL;
|
||||
m_modp = NULL;
|
||||
m_coverBins = 0;
|
||||
@@ -272,7 +277,8 @@ void EmitCSyms::emitSymHdr() {
|
||||
ofp()->putsHeader();
|
||||
puts("// DESCR" "IPTION: Verilator output: Symbol table internal header\n");
|
||||
puts("//\n");
|
||||
puts("// Internal details; most calling programs do not need this header\n");
|
||||
puts("// Internal details; most calling programs do not need this header,\n");
|
||||
puts("// unless using verilator public meta comments.\n");
|
||||
puts("\n");
|
||||
|
||||
puts("#ifndef _"+symClassName()+"_H_\n");
|
||||
@@ -660,7 +666,7 @@ void EmitCSyms::emitDpiImp() {
|
||||
//######################################################################
|
||||
// EmitC class functions
|
||||
|
||||
void V3EmitC::emitcSyms() {
|
||||
void V3EmitC::emitcSyms(bool dpiHdrOnly) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
EmitCSyms syms (v3Global.rootp());
|
||||
EmitCSyms syms (v3Global.rootp(), dpiHdrOnly);
|
||||
}
|
||||
|
||||
+1
-1
@@ -228,7 +228,7 @@ public:
|
||||
}
|
||||
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) {
|
||||
virtual void visit(AstNode* nodep) { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc("No visitors implemented.");
|
||||
}
|
||||
|
||||
|
||||
+11
-8
@@ -222,6 +222,9 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
||||
virtual void visit(AstDisplay* nodep) {
|
||||
visitNodeDisplay(nodep, nodep->filep(), nodep->fmtp()->text(), nodep->fmtp()->exprsp());
|
||||
}
|
||||
virtual void visit(AstElabDisplay* nodep) {
|
||||
visitNodeDisplay(nodep, NULL, nodep->fmtp()->text(), nodep->fmtp()->exprsp());
|
||||
}
|
||||
virtual void visit(AstFScanF* nodep) {
|
||||
visitNodeDisplay(nodep, nodep->filep(), nodep->text(), nodep->exprsp());
|
||||
}
|
||||
@@ -403,23 +406,23 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
||||
case 'f': putfs(nodep, ""); break;
|
||||
case 'k': putbs(""); break;
|
||||
case 'l': {
|
||||
if (!lhsp) { nodep->v3fatalSrc("emitVerilog() references undef node"); }
|
||||
else iterateAndNextNull(lhsp);
|
||||
UASSERT_OBJ(lhsp, nodep, "emitVerilog() references undef node");
|
||||
iterateAndNextNull(lhsp);
|
||||
break;
|
||||
}
|
||||
case 'r': {
|
||||
if (!rhsp) { nodep->v3fatalSrc("emitVerilog() references undef node"); }
|
||||
else iterateAndNextNull(rhsp);
|
||||
UASSERT_OBJ(rhsp, nodep, "emitVerilog() references undef node");
|
||||
iterateAndNextNull(rhsp);
|
||||
break;
|
||||
}
|
||||
case 't': {
|
||||
if (!thsp) { nodep->v3fatalSrc("emitVerilog() references undef node"); }
|
||||
else iterateAndNextNull(thsp);
|
||||
UASSERT_OBJ(thsp, nodep, "emitVerilog() references undef node");
|
||||
iterateAndNextNull(thsp);
|
||||
break;
|
||||
}
|
||||
case 'd': {
|
||||
if (!nodep->dtypep()) { nodep->v3fatalSrc("emitVerilog() references undef node"); }
|
||||
else iterateAndNextNull(nodep->dtypep());
|
||||
UASSERT_OBJ(nodep->dtypep(), nodep, "emitVerilog() references undef node");
|
||||
iterateAndNextNull(nodep->dtypep());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -180,6 +180,11 @@ class EmitXmlFileVisitor : public AstNVisitor {
|
||||
puts(" displaytype="); putsQuoted(nodep->verilogKwd());
|
||||
outputChildrenEnd(nodep, "");
|
||||
}
|
||||
virtual void visit(AstElabDisplay* nodep) {
|
||||
outputTag(nodep, "");
|
||||
puts(" displaytype="); putsQuoted(nodep->verilogKwd());
|
||||
outputChildrenEnd(nodep, "");
|
||||
}
|
||||
virtual void visit(AstExtend* nodep) {
|
||||
outputTag(nodep, "");
|
||||
puts(" width="); putsQuoted(cvtToStr(nodep->width()));
|
||||
|
||||
+24
-5
@@ -38,6 +38,7 @@ bool V3Error::s_warnFatal = true;
|
||||
int V3Error::s_tellManual = 0;
|
||||
std::ostringstream V3Error::s_errorStr; // Error string being formed
|
||||
V3ErrorCode V3Error::s_errorCode = V3ErrorCode::EC_FATAL;
|
||||
bool V3Error::s_errorContexted = false;
|
||||
bool V3Error::s_errorSuppressed = false;
|
||||
bool V3Error::s_describedEachWarn[V3ErrorCode::_ENUM_MAX];
|
||||
bool V3Error::s_describedWarnings = false;
|
||||
@@ -72,8 +73,7 @@ void V3Error::init() {
|
||||
s_describedEachWarn[i] = false;
|
||||
s_pretendError[i] = V3ErrorCode(i).pretendError();
|
||||
}
|
||||
|
||||
if (string(V3ErrorCode(V3ErrorCode::_ENUM_MAX).ascii()) != " MAX") {
|
||||
if (VL_UNCOVERABLE(string(V3ErrorCode(V3ErrorCode::_ENUM_MAX).ascii()) != " MAX")) {
|
||||
v3fatalSrc("Enum table in V3ErrorCode::EC_ascii() is munged");
|
||||
}
|
||||
}
|
||||
@@ -161,7 +161,7 @@ string V3Error::warnMore() {
|
||||
return string(msgPrefix().size(), ' ');
|
||||
}
|
||||
|
||||
void V3Error::v3errorEnd(std::ostringstream& sstr) {
|
||||
void V3Error::v3errorEnd(std::ostringstream& sstr, const string& locationStr) {
|
||||
#if defined(__COVERITY__) || defined(__cppcheck__)
|
||||
if (s_errorCode==V3ErrorCode::EC_FATAL) __coverity_panic__(x);
|
||||
#endif
|
||||
@@ -170,10 +170,29 @@ void V3Error::v3errorEnd(std::ostringstream& sstr) {
|
||||
// On debug, show only non default-off warning to prevent pages of warnings
|
||||
&& (!debug() || s_errorCode.defaultsOff())) return;
|
||||
string msg = msgPrefix()+sstr.str();
|
||||
if (msg[msg.length()-1] != '\n') msg += '\n';
|
||||
// Suppress duplicates
|
||||
if (s_errorSuppressed) { // If suppressed print only first line to reduce verbosity
|
||||
string::size_type pos;
|
||||
if ((pos = msg.find('\n')) != string::npos) {
|
||||
msg.erase(pos, msg.length()-pos);
|
||||
msg += "...";
|
||||
}
|
||||
}
|
||||
// Trailing newline (generally not on messages) & remove dup newlines
|
||||
{
|
||||
msg += '\n'; // Trailing newlines generally not put on messages so add
|
||||
string::size_type pos;
|
||||
while ((pos = msg.find("\n\n")) != string::npos) {
|
||||
msg.erase(pos+1, 1);
|
||||
}
|
||||
}
|
||||
// Suppress duplicate messages
|
||||
if (s_messages.find(msg) != s_messages.end()) return;
|
||||
s_messages.insert(msg);
|
||||
if (!locationStr.empty()) {
|
||||
string locationMsg = warnMore()+locationStr+"\n";
|
||||
size_t pos = msg.find("\n");
|
||||
msg.insert(pos + 1, locationMsg);
|
||||
}
|
||||
// Output
|
||||
std::cerr<<msg;
|
||||
if (!s_errorSuppressed && !(s_errorCode==V3ErrorCode::EC_INFO
|
||||
|
||||
+27
-13
@@ -49,7 +49,6 @@ public:
|
||||
I_DEF_NETTYPE_WIRE, // `default_nettype is WIRE (false=NONE)
|
||||
// Error codes:
|
||||
E_DETECTARRAY, // Error: Unsupported: Can't detect changes on arrayed variable
|
||||
E_MULTITOP, // Error: Multiple top level modules
|
||||
E_PORTSHORT, // Error: Output port is connected to a constant, electrical short
|
||||
E_TASKNSVAR, // Error: Task I/O not simple
|
||||
//
|
||||
@@ -89,6 +88,7 @@ public:
|
||||
LITENDIAN, // Little bit endian vector
|
||||
MODDUP, // Duplicate module
|
||||
MULTIDRIVEN, // Driven from multiple blocks
|
||||
MULTITOP, // Multiple top level modules
|
||||
PINMISSING, // Cell pin not specified
|
||||
PINNOCONNECT, // Cell pin not connected
|
||||
PINCONNECTEMPTY,// Cell pin connected by name with empty reference
|
||||
@@ -131,7 +131,7 @@ public:
|
||||
// Boolean
|
||||
" I_COVERAGE", " I_TRACING", " I_LINT", " I_DEF_NETTYPE_WIRE",
|
||||
// Errors
|
||||
"DETECTARRAY", "MULTITOP", "PORTSHORT", "TASKNSVAR",
|
||||
"DETECTARRAY", "PORTSHORT", "TASKNSVAR",
|
||||
// Warnings
|
||||
" EC_FIRST_WARN",
|
||||
"ALWCOMBORDER", "ASSIGNDLY", "ASSIGNIN",
|
||||
@@ -144,7 +144,7 @@ public:
|
||||
"IMPERFECTSCH", "IMPLICIT", "IMPORTSTAR", "IMPURE",
|
||||
"INCABSPATH", "INFINITELOOP", "INITIALDLY",
|
||||
"LITENDIAN", "MODDUP",
|
||||
"MULTIDRIVEN",
|
||||
"MULTIDRIVEN", "MULTITOP",
|
||||
"PINMISSING", "PINNOCONNECT", "PINCONNECTEMPTY", "PROCASSWIRE",
|
||||
"REALCVT", "REDEFMACRO",
|
||||
"SELRANGE", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET",
|
||||
@@ -225,6 +225,7 @@ class V3Error {
|
||||
static int s_tellManual; // Tell user to see manual, 0=not yet, 1=doit, 2=disable
|
||||
static std::ostringstream s_errorStr; // Error string being formed
|
||||
static V3ErrorCode s_errorCode; // Error string being formed will abort
|
||||
static bool s_errorContexted; // Error being formed got context
|
||||
static bool s_errorSuppressed; // Error being formed should be suppressed
|
||||
static MessagesSet s_messages; // What errors we've outputted
|
||||
static ErrorExitCb s_errorExitCb; // Callback when error occurs for dumping
|
||||
@@ -246,6 +247,8 @@ class V3Error {
|
||||
static int errorCount() { return s_errCount; }
|
||||
static int warnCount() { return s_warnCount; }
|
||||
static int errorOrWarnCount() { return errorCount()+warnCount(); }
|
||||
static bool errorContexted() { return s_errorContexted; }
|
||||
static void errorContexted(bool flag) { s_errorContexted = flag; }
|
||||
// METHODS
|
||||
static void incErrors();
|
||||
static void incWarnings() { s_warnCount++; }
|
||||
@@ -257,18 +260,21 @@ class V3Error {
|
||||
static bool isError(V3ErrorCode code, bool supp);
|
||||
static string lineStr(const char* filename, int lineno);
|
||||
static V3ErrorCode errorCode() { return s_errorCode; }
|
||||
static void errorExitCb(ErrorExitCb cb) { s_errorExitCb = cb; }
|
||||
static void errorExitCb(ErrorExitCb cb) { s_errorExitCb = cb; }
|
||||
|
||||
// When printing an error/warning, print prefix for multiline message
|
||||
static string warnMore();
|
||||
/// When building an error, don't show context info
|
||||
static string warnContextNone() { V3Error::errorContexted(true); return ""; }
|
||||
|
||||
// Internals for v3error()/v3fatal() macros only
|
||||
// Error end takes the string stream to output, be careful to seek() as needed
|
||||
static void v3errorPrep(V3ErrorCode code) {
|
||||
s_errorStr.str(""); s_errorCode = code; s_errorSuppressed = false; }
|
||||
s_errorStr.str(""); s_errorCode = code;
|
||||
s_errorContexted = false; s_errorSuppressed = false; }
|
||||
static std::ostringstream& v3errorStr() { return s_errorStr; }
|
||||
static void vlAbort();
|
||||
static void v3errorEnd(std::ostringstream& sstr); // static, but often overridden in classes.
|
||||
static void v3errorEnd(std::ostringstream& sstr, const string& locationStr = ""); // static, but often overridden in classes.
|
||||
};
|
||||
|
||||
// Global versions, so that if the class doesn't define a operator, we get the functions anyways.
|
||||
@@ -297,8 +303,8 @@ inline void v3errorEndFatal(std::ostringstream& sstr) {
|
||||
::v3errorEndFatal((V3Error::v3errorPrep(V3ErrorCode::EC_FATAL), \
|
||||
(V3Error::v3errorStr()<<msg), V3Error::v3errorStr()));
|
||||
|
||||
#define UINFO(level,stmsg) {if(VL_UNLIKELY(debug()>=(level))) { cout<<"- "<<V3Error::lineStr(__FILE__,__LINE__)<<stmsg; }}
|
||||
#define UINFONL(level,stmsg) {if(VL_UNLIKELY(debug()>=(level))) { cout<<stmsg; } }
|
||||
#define UINFO(level,stmsg) {if (VL_UNCOVERABLE(debug()>=(level))) { cout<<"- "<<V3Error::lineStr(__FILE__,__LINE__)<<stmsg; }}
|
||||
#define UINFONL(level,stmsg) {if (VL_UNCOVERABLE(debug()>=(level))) { cout<<stmsg; } }
|
||||
|
||||
#ifdef VL_DEBUG
|
||||
# define UDEBUGONLY(stmts) {stmts}
|
||||
@@ -306,11 +312,17 @@ inline void v3errorEndFatal(std::ostringstream& sstr) {
|
||||
# define UDEBUGONLY(stmts) {if (0) {stmts}}
|
||||
#endif
|
||||
|
||||
#define UASSERT(condition,stmsg) { if (VL_UNLIKELY(!(condition))) { v3fatalSrc(stmsg); }}
|
||||
// Assertion without object, generally UOBJASSERT preferred
|
||||
#define UASSERT(condition,stmsg) \
|
||||
do { if (VL_UNCOVERABLE(!(condition))) { v3fatalSrc(stmsg); }} while(0)
|
||||
// Assertion with object
|
||||
#define UASSERT_OBJ(condition,obj,stmsg) \
|
||||
do { if (VL_UNCOVERABLE(!(condition))) { (obj)->v3fatalSrc(stmsg); }} while(0)
|
||||
// For use in V3Ast static functions only
|
||||
#define UASSERT_STATIC(condition,stmsg) \
|
||||
{ if (VL_UNLIKELY(!(condition))) { \
|
||||
std::cerr<<"Internal Error: "<<__FILE__<<":"<<std::dec<<__LINE__<<":"<<(stmsg)<<std::endl; abort(); } }
|
||||
do { if (VL_UNCOVERABLE(!(condition))) { \
|
||||
std::cerr<<"Internal Error: "<<__FILE__<<":"<<std::dec<<__LINE__ \
|
||||
<<":"<<(stmsg)<<std::endl; abort(); } } while(0)
|
||||
// Check self test values for expected value. Safe from side-effects.
|
||||
// Type argument can be removed when go to C++11 (use auto).
|
||||
#define UASSERT_SELFTEST(Type,got,exp) \
|
||||
@@ -318,12 +330,14 @@ inline void v3errorEndFatal(std::ostringstream& sstr) {
|
||||
UASSERT(g==e, "Self-test failed '" #got "==" #exp "'"" got=" \
|
||||
<<g<<" expected="<<e); } while(0)
|
||||
|
||||
#define V3ERROR_NA { v3error("Internal: Unexpected Call"); v3fatalSrc("Unexpected Call"); }
|
||||
#define V3ERROR_NA \
|
||||
do { v3error("Internal: Unexpected Call"); v3fatalSrc("Unexpected Call"); } while(0)
|
||||
|
||||
/// Declare a convenience debug() routine that may be added to any class in
|
||||
/// Verilator so that --debugi-<srcfile> will work to control UINFOs in
|
||||
/// that class:
|
||||
#define VL_DEBUG_FUNC static int debug() { \
|
||||
#define VL_DEBUG_FUNC \
|
||||
static int debug() { \
|
||||
static int level = -1; \
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); \
|
||||
return level; \
|
||||
|
||||
+16
-15
@@ -120,13 +120,15 @@ private:
|
||||
new AstConst(nodep->fileline(), word));
|
||||
} else if (nodep->isQuad() && word==0) {
|
||||
AstNode* quadfromp = nodep->cloneTree(true);
|
||||
quadfromp->dtypeSetBitSized(VL_QUADSIZE, quadfromp->widthMin(), AstNumeric::UNSIGNED);
|
||||
quadfromp->dtypeSetBitUnsized(VL_QUADSIZE, quadfromp->widthMin(),
|
||||
AstNumeric::UNSIGNED);
|
||||
return new AstCCast(nodep->fileline(),
|
||||
quadfromp,
|
||||
VL_WORDSIZE);
|
||||
} else if (nodep->isQuad() && word==1) {
|
||||
AstNode* quadfromp = nodep->cloneTree(true);
|
||||
quadfromp->dtypeSetBitSized(VL_QUADSIZE, quadfromp->widthMin(), AstNumeric::UNSIGNED);
|
||||
quadfromp->dtypeSetBitUnsized(VL_QUADSIZE, quadfromp->widthMin(),
|
||||
AstNumeric::UNSIGNED);
|
||||
return new AstCCast(nodep->fileline(),
|
||||
new AstShiftR(nodep->fileline(),
|
||||
quadfromp,
|
||||
@@ -239,9 +241,8 @@ private:
|
||||
}
|
||||
bool expandWide(AstNodeAssign* nodep, AstArraySel* rhsp) {
|
||||
UINFO(8," Wordize ASSIGN(ARRAYSEL) "<<nodep<<endl);
|
||||
if (VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)) {
|
||||
nodep->v3fatalSrc("ArraySel with unpacked arrays should have been removed in V3Slice");
|
||||
}
|
||||
UASSERT_OBJ(!VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType), nodep,
|
||||
"ArraySel with unpacked arrays should have been removed in V3Slice");
|
||||
for (int w=0; w<nodep->widthWords(); w++) {
|
||||
addWordAssign(nodep, w, newAstWordSelClone(rhsp, w));
|
||||
}
|
||||
@@ -324,11 +325,9 @@ private:
|
||||
newp = new AstCCast(nodep->fileline(), lhsp, nodep);
|
||||
}
|
||||
} else { // Long
|
||||
if (lhsp->isQuad() || lhsp->isWide()) {
|
||||
nodep->v3fatalSrc("extending larger thing into smaller?");
|
||||
} else {
|
||||
lhsp->dtypeFrom(nodep); // Just mark it, else nop
|
||||
}
|
||||
UASSERT_OBJ(!(lhsp->isQuad() || lhsp->isWide()), nodep,
|
||||
"extending larger thing into smaller?");
|
||||
lhsp->dtypeFrom(nodep); // Just mark it, else nop
|
||||
}
|
||||
replaceWithDelete(nodep, newp); VL_DANGLING(nodep);
|
||||
}
|
||||
@@ -349,7 +348,7 @@ private:
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
iterateChildren(nodep);
|
||||
// Remember, Sel's may have non-integer rhs, so need to optimize for that!
|
||||
if (nodep->widthMin() != nodep->widthConst()) nodep->v3fatalSrc("Width mismatch");
|
||||
UASSERT_OBJ(nodep->widthMin() == nodep->widthConst(), nodep, "Width mismatch");
|
||||
if (VN_IS(nodep->backp(), NodeAssign)
|
||||
&& nodep==VN_CAST(nodep->backp(), NodeAssign)->lhsp()) {
|
||||
// Sel is an LHS assignment select
|
||||
@@ -468,7 +467,7 @@ private:
|
||||
}
|
||||
|
||||
bool expandWide(AstNodeAssign* nodep, AstSel* rhsp) {
|
||||
if (nodep->widthMin() != rhsp->widthConst()) nodep->v3fatalSrc("Width mismatch");
|
||||
UASSERT_OBJ(nodep->widthMin() == rhsp->widthConst(), nodep, "Width mismatch");
|
||||
if (VN_IS(rhsp->lsbp(), Const) && VL_BITBIT_I(rhsp->lsbConst())==0) {
|
||||
int lsb = rhsp->lsbConst();
|
||||
UINFO(8," Wordize ASSIGN(SEL,align) "<<nodep<<endl);
|
||||
@@ -729,7 +728,8 @@ private:
|
||||
} else {
|
||||
UINFO(8," REPLICATE "<<nodep<<endl);
|
||||
const AstConst* constp = VN_CAST(nodep->rhsp(), Const);
|
||||
if (!constp) nodep->v3fatalSrc("Replication value isn't a constant. Checked earlier!");
|
||||
UASSERT_OBJ(constp, nodep,
|
||||
"Replication value isn't a constant. Checked earlier!");
|
||||
uint32_t times = constp->toUInt();
|
||||
if (nodep->isQuad() && !lhsp->isQuad()) {
|
||||
lhsp = new AstCCast(nodep->fileline(), lhsp, nodep);
|
||||
@@ -756,13 +756,14 @@ private:
|
||||
AstNode* lhsp = rhsp->lhsp();
|
||||
int lhswidth = lhsp->widthMin();
|
||||
const AstConst* constp = VN_CAST(rhsp->rhsp(), Const);
|
||||
if (!constp) rhsp->v3fatalSrc("Replication value isn't a constant. Checked earlier!");
|
||||
UASSERT_OBJ(constp, rhsp, "Replication value isn't a constant. Checked earlier!");
|
||||
uint32_t times = constp->toUInt();
|
||||
for (int w=0; w<rhsp->widthWords(); w++) {
|
||||
AstNode* newp;
|
||||
if (lhswidth==1) {
|
||||
newp = new AstNegate(nodep->fileline(), lhsp->cloneTree(true));
|
||||
newp->dtypeSetLogicSized(VL_WORDSIZE, VL_WORDSIZE, AstNumeric::UNSIGNED); // Replicate always unsigned
|
||||
newp->dtypeSetLogicSized(VL_WORDSIZE,
|
||||
AstNumeric::UNSIGNED); // Replicate always unsigned
|
||||
} else {
|
||||
newp = newAstWordSelClone(lhsp, w);
|
||||
for (unsigned repnum=1; repnum<times; repnum++) {
|
||||
|
||||
+1
-3
@@ -147,8 +147,6 @@ inline void V3FileDependImp::writeDepend(const string& filename) {
|
||||
*ofp<<" : ";
|
||||
*ofp<<v3Global.opt.bin();
|
||||
*ofp<<" ";
|
||||
*ofp<<V3PreShell::dependFiles();
|
||||
*ofp<<" ";
|
||||
|
||||
for (std::set<DependFile>::iterator iter=m_filenameList.begin();
|
||||
iter!=m_filenameList.end(); ++iter) {
|
||||
@@ -532,7 +530,7 @@ protected:
|
||||
if (listSize(outl) < INFILTER_CACHE_MAX) {
|
||||
// Cache small files (only to save space)
|
||||
// It's quite common to `include "timescale" thousands of times
|
||||
// This isn't so important if it's just a open(), but filtering can be slow
|
||||
// This isn't so important if it's just an open(), but filtering can be slow
|
||||
m_contentsMap.insert(make_pair(filename, listString(outl)));
|
||||
}
|
||||
return true;
|
||||
|
||||
+155
-11
@@ -23,14 +23,18 @@
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3FileLine.h"
|
||||
#include "V3String.h"
|
||||
#ifndef _V3ERROR_NO_GLOBAL_
|
||||
# include "V3Ast.h"
|
||||
# include "V3Global.h"
|
||||
# include "V3Stats.h"
|
||||
# include "V3Config.h"
|
||||
# include "V3File.h"
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <iomanip>
|
||||
#include VL_INCLUDE_UNORDERED_SET
|
||||
|
||||
//######################################################################
|
||||
@@ -68,25 +72,85 @@ int FileLineSingleton::nameToNumber(const string& filename) {
|
||||
}
|
||||
|
||||
//! Support XML output
|
||||
|
||||
//! Experimental. Updated to also put out the language.
|
||||
void FileLineSingleton::fileNameNumMapDumpXml(std::ostream& os) {
|
||||
os<<"<files>\n";
|
||||
for (FileNameNumMap::const_iterator it = m_namemap.begin(); it != m_namemap.end(); ++it) {
|
||||
os<<"<file id=\""<<filenameLetters(it->second)
|
||||
<<"\" filename=\""<<it->first
|
||||
<<"\" filename=\""<<V3OutFormatter::quoteNameControls(it->first, V3OutFormatter::LA_XML)
|
||||
<<"\" language=\""<<numberToLang(it->second).ascii()<<"\"/>\n";
|
||||
}
|
||||
os<<"</files>\n";
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// VFileContents class functions
|
||||
|
||||
int VFileContent::debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
|
||||
void VFileContent::pushText(const string& text) {
|
||||
if (m_lines.size() == 0) {
|
||||
m_lines.push_back(""); // no such thing as line [0]
|
||||
m_lines.push_back(""); // start with no leftover
|
||||
}
|
||||
|
||||
// Any leftover text is stored on largest line (might be "")
|
||||
string leftover = m_lines.back() + text; m_lines.pop_back();
|
||||
|
||||
// Insert line-by-line
|
||||
string::size_type pos = 0;
|
||||
string::size_type line_start = 0;
|
||||
while (1) {
|
||||
string::size_type line_end = leftover.find("\n", line_start);
|
||||
if (line_end != string::npos) {
|
||||
string oneline (leftover, line_start, line_end-line_start+1);
|
||||
m_lines.push_back(oneline); // Keeps newline
|
||||
UINFO(9, "PushStream[ct"<<m_id<<"+"<<(m_lines.size()-1)<<"]: "<<oneline);
|
||||
line_start = line_end+1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Keep leftover for next time
|
||||
m_lines.push_back(string(leftover, line_start)); // Might be ""
|
||||
}
|
||||
|
||||
string VFileContent::getLine(int lineno) const {
|
||||
// Return error text rather than asserting so the user isn't left without a message
|
||||
if (VL_UNCOVERABLE(lineno < 0 || lineno >= (int)m_lines.size())) {
|
||||
if (debug() || v3Global.opt.debugCheck()) {
|
||||
return ("%Error-internal-contents-bad-ct"+cvtToStr(m_id)
|
||||
+"-ln"+cvtToStr(lineno));
|
||||
} else return "";
|
||||
}
|
||||
string text = m_lines[lineno];
|
||||
UINFO(9, "Get Stream[ct"<<m_id<<"+"<<lineno<<"]: "<<text);
|
||||
return text;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, VFileContent* contentp) {
|
||||
if (!contentp) os <<"ct0";
|
||||
else os <<contentp->ascii();
|
||||
return os;
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// FileLine class functions
|
||||
|
||||
FileLine::FileLine(FileLine::EmptySecret) {
|
||||
// Sort of a singleton
|
||||
m_lineno = 0;
|
||||
m_filenameno = singleton().nameToNumber("AstRoot");
|
||||
m_firstLineno = 0;
|
||||
m_lastLineno = 0;
|
||||
m_firstColumn = 0;
|
||||
m_lastColumn = 0;
|
||||
m_filenameno = singleton().nameToNumber(FileLine::builtInFilename());
|
||||
m_contentp = NULL;
|
||||
m_contentLineno = 0;
|
||||
m_parent = NULL;
|
||||
|
||||
m_warnOn = 0;
|
||||
for (int codei=V3ErrorCode::EC_MIN; codei<V3ErrorCode::_ENUM_MAX; codei++) {
|
||||
@@ -96,13 +160,16 @@ FileLine::FileLine(FileLine::EmptySecret) {
|
||||
}
|
||||
|
||||
string FileLine::lineDirectiveStrg(int enterExit) const {
|
||||
char numbuf[20]; sprintf(numbuf, "%d", lineno());
|
||||
char numbuf[20]; sprintf(numbuf, "%d", lastLineno());
|
||||
char levelbuf[20]; sprintf(levelbuf, "%d", enterExit);
|
||||
return (string("`line ")+numbuf+" \""+filename()+"\" "+levelbuf+"\n");
|
||||
}
|
||||
|
||||
void FileLine::lineDirective(const char* textp, int& enterExitRef) {
|
||||
// Handle `line directive
|
||||
// Does not parse streamNumber/streamLineno as the next input token
|
||||
// will come from the same stream as the previous line.
|
||||
|
||||
// Skip `line
|
||||
while (*textp && isspace(*textp)) textp++;
|
||||
while (*textp && !isspace(*textp)) textp++;
|
||||
@@ -133,6 +200,18 @@ void FileLine::lineDirective(const char* textp, int& enterExitRef) {
|
||||
//printf ("PPLINE %d '%s'\n", s_lineno, s_filename.c_str());
|
||||
}
|
||||
|
||||
void FileLine::forwardToken(const char* textp, size_t size, bool trackLines) {
|
||||
for (const char* sp = textp; size && *sp; ++sp, --size) {
|
||||
if (*sp == '\n') {
|
||||
if (trackLines) linenoInc();
|
||||
m_lastColumn = 1;
|
||||
} else if (*sp == '\r') {
|
||||
} else { // Tabs are considered one column; hence column means number of chars
|
||||
++m_lastColumn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileLine* FileLine::copyOrSameFileLine() {
|
||||
// When a fileline is "used" to produce a node, calls this function.
|
||||
// Return this, or a copy of this
|
||||
@@ -176,12 +255,19 @@ const string FileLine::profileFuncname() const {
|
||||
!= string::npos) {
|
||||
name.replace(pos, 1, "_");
|
||||
}
|
||||
name += "__l"+cvtToStr(lineno());
|
||||
name += "__l"+cvtToStr(lastLineno());
|
||||
return name;
|
||||
}
|
||||
|
||||
string FileLine::asciiLineCol() const {
|
||||
return (cvtToStr(firstLineno())+"-"+cvtToStr(lastLineno())
|
||||
+":"+cvtToStr(firstColumn())+"-"+cvtToStr(lastColumn())
|
||||
+"["+(m_contentp ? m_contentp->ascii() : "ct0")
|
||||
+"+"+cvtToStr(m_contentLineno)+"]");
|
||||
}
|
||||
string FileLine::ascii() const {
|
||||
return filename()+":"+cvtToStr(lineno());
|
||||
// For most errors especially in the parser the lastLineno is more accurate than firstLineno
|
||||
return filename()+":"+cvtToStr(lastLineno());
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, FileLine* fileline) {
|
||||
os <<fileline->ascii()<<": "<<std::hex;
|
||||
@@ -235,22 +321,80 @@ void FileLine::modifyStateInherit(const FileLine* fromp) {
|
||||
}
|
||||
}
|
||||
|
||||
void FileLine::v3errorEnd(std::ostringstream& str) {
|
||||
void FileLine::v3errorEnd(std::ostringstream& str, const string& locationStr) {
|
||||
std::ostringstream nsstr;
|
||||
if (m_lineno) nsstr<<this;
|
||||
if (lastLineno()) nsstr<<this;
|
||||
nsstr<<str.str();
|
||||
nsstr<<endl;
|
||||
std::ostringstream lstr;
|
||||
if (!locationStr.empty()) {
|
||||
lstr<<std::setw(ascii().length())<<" "<<": "<<locationStr;
|
||||
}
|
||||
if (warnIsOff(V3Error::errorCode())) V3Error::suppressThisWarning();
|
||||
V3Error::v3errorEnd(nsstr);
|
||||
else if (!V3Error::errorContexted()) nsstr<<warnContextPrimary();
|
||||
V3Error::v3errorEnd(nsstr, lstr.str());
|
||||
}
|
||||
|
||||
string FileLine::warnMore() const {
|
||||
if (m_lineno) {
|
||||
if (lastLineno()) {
|
||||
return V3Error::warnMore()+string(ascii().size(), ' ')+": ";
|
||||
} else {
|
||||
return V3Error::warnMore();
|
||||
}
|
||||
}
|
||||
string FileLine::warnOther() const {
|
||||
if (lastLineno()) {
|
||||
return V3Error::warnMore()+ascii()+": ";
|
||||
} else {
|
||||
return V3Error::warnMore();
|
||||
}
|
||||
}
|
||||
|
||||
string FileLine::source() const {
|
||||
if (VL_UNCOVERABLE(!m_contentp)) {
|
||||
if (debug() || v3Global.opt.debugCheck()) return "%Error-internal-no-contents";
|
||||
else return "";
|
||||
}
|
||||
return m_contentp->getLine(m_contentLineno);
|
||||
}
|
||||
string FileLine::prettySource() const {
|
||||
string out = source();
|
||||
// Drop ignore trailing newline
|
||||
string::size_type pos = out.find('\n');
|
||||
if (pos != string::npos) out = string(out, 0, pos);
|
||||
// Column tracking counts tabs = 1, so match that when print source
|
||||
return VString::spaceUnprintable(out);
|
||||
}
|
||||
|
||||
string FileLine::warnContext(bool secondary) const {
|
||||
V3Error::errorContexted(true);
|
||||
string out = "";
|
||||
if (firstLineno()==lastLineno() && firstColumn()) {
|
||||
string sourceLine = prettySource();
|
||||
// Don't show super-long lines as can fill screen and unlikely to help user
|
||||
if (!sourceLine.empty()
|
||||
&& sourceLine.length() < SHOW_SOURCE_MAX_LENGTH
|
||||
&& sourceLine.length() >= (lastColumn()-1)) {
|
||||
out += sourceLine+"\n";
|
||||
out += string((firstColumn()-1), ' ')+'^';
|
||||
// Can't use UASSERT_OBJ used in warnings already inside the error end handler
|
||||
UASSERT_STATIC(lastColumn() >= firstColumn(), "Column numbers backwards");
|
||||
if (lastColumn() != firstColumn()) {
|
||||
out += string((lastColumn()-firstColumn()-1), '~');
|
||||
}
|
||||
out += "\n";
|
||||
}
|
||||
}
|
||||
if (!secondary) { // Avoid printing long paths on informational part of error
|
||||
for (FileLine* parentFl = parent(); parentFl; parentFl = parentFl->parent()) {
|
||||
if (parentFl->filenameIsGlobal()) break;
|
||||
out += parentFl->warnOther()+"... note: In file included from "
|
||||
+parentFl->filebasename()+"\n";
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
typedef vl_unordered_set<FileLine*> FileLineCheckSet;
|
||||
FileLineCheckSet fileLineLeakChecks;
|
||||
|
||||
+107
-37
@@ -49,12 +49,11 @@ class FileLineSingleton {
|
||||
FileNameNumMap m_namemap; // filenameno for each filename
|
||||
std::deque<string> m_names; // filename text for each filenameno
|
||||
std::deque<V3LangCode> m_languages; // language for each filenameno
|
||||
// COSNTRUCTORS
|
||||
// CONSTRUCTORS
|
||||
FileLineSingleton() { }
|
||||
~FileLineSingleton() { }
|
||||
protected:
|
||||
friend class FileLine;
|
||||
// METHODS
|
||||
int nameToNumber(const string& filename);
|
||||
const string numberToName(int filenameno) const { return m_names[filenameno]; }
|
||||
const V3LangCode numberToLang(int filenameno) const { return m_languages[filenameno]; }
|
||||
@@ -64,26 +63,43 @@ protected:
|
||||
static const string filenameLetters(int fileno);
|
||||
};
|
||||
|
||||
//! All source lines from a file/stream, to enable errors to show sources
|
||||
class VFileContent {
|
||||
// MEMBERS
|
||||
int m_id; // Content ID number
|
||||
std::deque<string> m_lines; // Source text lines
|
||||
public:
|
||||
VFileContent() { static int s_id = 0; m_id = ++s_id; }
|
||||
~VFileContent() { }
|
||||
// METHODS
|
||||
void pushText(const string& text); // Add arbitrary text (need not be line-by-line)
|
||||
string getLine(int lineno) const;
|
||||
string ascii() const { return "ct"+cvtToStr(m_id); }
|
||||
static int debug();
|
||||
};
|
||||
std::ostream& operator<<(std::ostream& os, VFileContent* contentp);
|
||||
|
||||
//! File and line number of an object, mostly for error reporting
|
||||
|
||||
//! This class is instantiated for every source code line (potentially
|
||||
//! millions). To save space, per-file information (e.g. filename, source
|
||||
//! language is held in tables in the FileLineSingleton class.
|
||||
class FileLine {
|
||||
int m_lineno;
|
||||
int m_filenameno;
|
||||
// CONSTANTS
|
||||
enum { SHOW_SOURCE_MAX_LENGTH = 400 }; // Don't show source lines > this long
|
||||
|
||||
// MEMBERS
|
||||
// Columns here means number of chars from beginning (i.e. tabs count as one)
|
||||
int m_firstLineno; // `line corrected token's first line number
|
||||
int m_firstColumn; // `line corrected token's first column number
|
||||
int m_lastLineno; // `line corrected token's last line number
|
||||
int m_lastColumn; // `line corrected token's last column number
|
||||
int m_filenameno; // `line corrected filename number
|
||||
int m_contentLineno; // Line number within source stream
|
||||
VFileContent* m_contentp; // Source text contents line is within
|
||||
FileLine* m_parent; // Parent line that included this line
|
||||
std::bitset<V3ErrorCode::_ENUM_MAX> m_warnOn;
|
||||
|
||||
private:
|
||||
struct EmptySecret {};
|
||||
inline static FileLineSingleton& singleton() {
|
||||
static FileLineSingleton s;
|
||||
return s;
|
||||
}
|
||||
inline static FileLine& defaultFileLine() {
|
||||
static FileLine* defFilelinep = new FileLine(FileLine::EmptySecret());
|
||||
return *defFilelinep;
|
||||
}
|
||||
protected:
|
||||
// User routines should never need to change line numbers
|
||||
// We are storing pointers, so we CAN'T change them after initial reading.
|
||||
@@ -92,46 +108,83 @@ protected:
|
||||
friend class V3PreLex;
|
||||
friend class V3PreProcImp;
|
||||
friend class V3PreShellImp;
|
||||
void lineno(int num) { m_lineno = num; }
|
||||
void language(V3LangCode lang) { singleton().numberToLang(m_filenameno, lang); }
|
||||
void filename(const string& name) { m_filenameno = singleton().nameToNumber(name); }
|
||||
void lineDirective(const char* textp, int& enterExitRef);
|
||||
void linenoInc() { m_lineno++; }
|
||||
void linenoIncInPlace() { m_lineno++; }
|
||||
FileLine* copyOrSameFileLine();
|
||||
private:
|
||||
// CONSTRUCTORS
|
||||
static FileLineSingleton& singleton() {
|
||||
static FileLineSingleton s;
|
||||
return s;
|
||||
}
|
||||
static FileLine& defaultFileLine() {
|
||||
static FileLine* defFilelinep = new FileLine(FileLine::EmptySecret());
|
||||
return *defFilelinep;
|
||||
}
|
||||
public:
|
||||
FileLine(const string& filename, int lineno) {
|
||||
m_lineno = lineno;
|
||||
FileLine(const string& filename) {
|
||||
m_lastLineno = m_firstLineno = 0;
|
||||
m_lastColumn = m_firstColumn = 0;
|
||||
m_filenameno = singleton().nameToNumber(filename);
|
||||
m_contentLineno = 0;
|
||||
m_contentp = NULL;
|
||||
m_parent = NULL;
|
||||
m_warnOn = defaultFileLine().m_warnOn;
|
||||
}
|
||||
explicit FileLine(FileLine* fromp) {
|
||||
m_lineno = fromp->m_lineno;
|
||||
m_firstLineno = fromp->m_firstLineno;
|
||||
m_firstColumn = fromp->m_firstColumn;
|
||||
m_lastLineno = fromp->m_lastLineno;
|
||||
m_lastColumn = fromp->m_lastColumn;
|
||||
m_filenameno = fromp->m_filenameno;
|
||||
m_contentLineno = fromp->m_contentLineno;
|
||||
m_contentp = fromp->m_contentp;
|
||||
m_parent = fromp->m_parent;
|
||||
m_warnOn = fromp->m_warnOn;
|
||||
}
|
||||
struct EmptySecret {}; // Constructor selection
|
||||
explicit FileLine(EmptySecret);
|
||||
~FileLine() { }
|
||||
FileLine* create(const string& filename, int lineno) { return new FileLine(filename, lineno); }
|
||||
FileLine* create(int lineno) { return create(filename(), lineno); }
|
||||
FileLine* copyOrSameFileLine();
|
||||
static void deleteAllRemaining();
|
||||
~FileLine() { }
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
static void* operator new(size_t size);
|
||||
static void operator delete(void* obj, size_t size);
|
||||
#endif
|
||||
|
||||
int lineno() const { return m_lineno; }
|
||||
void newContent() { m_contentp = new VFileContent; m_contentLineno = 1; }
|
||||
// METHODS
|
||||
void lineno(int num) { m_firstLineno = num; m_lastLineno = num;
|
||||
m_firstColumn = m_lastColumn = 1; }
|
||||
void language(V3LangCode lang) { singleton().numberToLang(m_filenameno, lang); }
|
||||
void filename(const string& name) { m_filenameno = singleton().nameToNumber(name); }
|
||||
void parent(FileLine* fileline) { m_parent = fileline; }
|
||||
void lineDirective(const char* textp, int& enterExitRef);
|
||||
void linenoInc() { m_lastLineno++; m_lastColumn = 1; m_contentLineno++; }
|
||||
void startToken() { m_firstLineno = m_lastLineno;
|
||||
m_firstColumn = m_lastColumn; }
|
||||
// Advance last line/column based on given text
|
||||
void forwardToken(const char* textp, size_t size, bool trackLines=true);
|
||||
int firstLineno() const { return m_firstLineno; }
|
||||
int firstColumn() const { return m_firstColumn; }
|
||||
int lastLineno() const { return m_lastLineno; }
|
||||
int lastColumn() const { return m_lastColumn; }
|
||||
VFileContent* contentp() const { return m_contentp; }
|
||||
// If not otherwise more specific, use last lineno for errors etc,
|
||||
// as the parser errors etc generally make more sense pointing at the last parse point
|
||||
int lineno() const { return m_lastLineno; }
|
||||
string source() const;
|
||||
string prettySource() const; // Source, w/stripped unprintables and newlines
|
||||
FileLine* parent() const { return m_parent; }
|
||||
V3LangCode language() const { return singleton().numberToLang(m_filenameno); }
|
||||
string ascii() const;
|
||||
string asciiLineCol() const;
|
||||
const string filename() const { return singleton().numberToName(m_filenameno); }
|
||||
bool filenameIsGlobal() const { return (filename() == commandLineFilename()
|
||||
|| filename() == internalDefineFilename()); }
|
||||
|| filename() == builtInFilename()); }
|
||||
const string filenameLetters() const { return singleton().filenameLetters(m_filenameno); }
|
||||
const string filebasename() const;
|
||||
const string filebasenameNoExt() const;
|
||||
const string profileFuncname() const;
|
||||
const string xml() const { return "fl=\""+filenameLetters()+cvtToStr(lineno())+"\""; }
|
||||
const string xml() const { return "fl=\""+filenameLetters()+cvtToStr(lastLineno())+"\""; }
|
||||
string lineDirectiveStrg(int enterExit) const;
|
||||
|
||||
// Turn on/off warning messages on this line.
|
||||
void warnOn(V3ErrorCode code, bool flag) { m_warnOn.set(code, flag); }
|
||||
void warnOff(V3ErrorCode code, bool flag) { warnOn(code, !flag); }
|
||||
@@ -149,8 +202,9 @@ public:
|
||||
void tracingOn(bool flag) { warnOn(V3ErrorCode::I_TRACING, flag); }
|
||||
|
||||
// METHODS - Global
|
||||
static string commandLineFilename() { return "COMMAND_LINE"; }
|
||||
static string internalDefineFilename() { return "INTERNAL_VERILATOR_DEFINE"; }
|
||||
// <command-line> and <built-in> match what GCC outputs
|
||||
static string commandLineFilename() { return "<command-line>"; }
|
||||
static string builtInFilename() { return "<built-in>"; }
|
||||
static void globalWarnLintOff(bool flag) {
|
||||
defaultFileLine().warnLintOff(flag); }
|
||||
static void globalWarnStyleOff(bool flag) {
|
||||
@@ -171,15 +225,31 @@ public:
|
||||
void modifyWarnOff(V3ErrorCode code, bool flag) { warnOff(code, flag); }
|
||||
|
||||
// OPERATORS
|
||||
void v3errorEnd(std::ostringstream& str);
|
||||
void v3errorEnd(std::ostringstream& str, const string& locationStr = "");
|
||||
void v3errorEndFatal(std::ostringstream& str);
|
||||
/// When building an error, prefix for printing continuation lines
|
||||
/// e.g. information referring to the same FileLine as before
|
||||
string warnMore() const;
|
||||
inline bool operator==(FileLine rhs) const {
|
||||
return (m_lineno==rhs.m_lineno && m_filenameno==rhs.m_filenameno
|
||||
&& m_warnOn==rhs.m_warnOn);
|
||||
/// When building an error, prefix for printing secondary information
|
||||
/// from a different FileLine than the original error
|
||||
string warnOther() const;
|
||||
/// When building an error, current location in include etc
|
||||
/// If not used in a given error, automatically pasted at end of error
|
||||
string warnContextPrimary() const { return warnContext(false); }
|
||||
/// When building an error, additional location for additional references
|
||||
/// Simplified information vs warnContextPrimary() to make dump clearer
|
||||
string warnContextSecondary() const { return warnContext(true); }
|
||||
bool operator==(FileLine rhs) const {
|
||||
return (m_firstLineno == rhs.m_firstLineno
|
||||
&& m_firstColumn == rhs.m_firstColumn
|
||||
&& m_lastLineno == rhs.m_lastLineno
|
||||
&& m_lastColumn == rhs.m_lastColumn
|
||||
&& m_filenameno == rhs.m_filenameno
|
||||
&& m_warnOn == rhs.m_warnOn);
|
||||
}
|
||||
private:
|
||||
void v3errorEndFatalGuts(std::ostringstream& str);
|
||||
string warnContext(bool secondary) const;
|
||||
};
|
||||
std::ostream& operator<<(std::ostream& os, FileLine* fileline);
|
||||
|
||||
|
||||
+124
-34
@@ -435,9 +435,9 @@ private:
|
||||
}
|
||||
virtual void visit(AstNodeVarRef* nodep) {
|
||||
if (m_scopep) {
|
||||
if (!m_logicVertexp) nodep->v3fatalSrc("Var ref not under a logic block");
|
||||
UASSERT_OBJ(m_logicVertexp, nodep, "Var ref not under a logic block");
|
||||
AstVarScope* varscp = nodep->varScopep();
|
||||
if (!varscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp");
|
||||
UASSERT_OBJ(varscp, nodep, "Var didn't get varscoped in V3Scope.cpp");
|
||||
GateVarVertex* vvertexp = makeVarVertex(varscp);
|
||||
UINFO(5," VARREF to "<<varscp<<endl);
|
||||
if (m_inSenItem) vvertexp->setIsClock();
|
||||
@@ -509,10 +509,9 @@ private:
|
||||
m_inSlow = lastslow;
|
||||
}
|
||||
virtual void visit(AstConcat* nodep) {
|
||||
if (VN_IS(nodep->backp(), NodeAssign)
|
||||
&& VN_CAST(nodep->backp(), NodeAssign)->lhsp()==nodep) {
|
||||
nodep->v3fatalSrc("Concat on LHS of assignment; V3Const should have deleted it");
|
||||
}
|
||||
UASSERT_OBJ(!(VN_IS(nodep->backp(), NodeAssign)
|
||||
&& VN_CAST(nodep->backp(), NodeAssign)->lhsp()==nodep),
|
||||
nodep, "Concat on LHS of assignment; V3Const should have deleted it");
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
@@ -560,9 +559,9 @@ void GateVisitor::optimizeSignals(bool allowMultiIn) {
|
||||
// reasonable logic may have disappeared. Issuing a warning would
|
||||
// thus be confusing. V3Undriven now handles this.
|
||||
vvertexp->varScp()->varp()->v3warn
|
||||
(UNDRIVEN, "Signal has no drivers "
|
||||
(UNDRIVEN, "Signal has no drivers: '"
|
||||
<<vvertexp->scopep()->prettyName()<<"."
|
||||
<<vvertexp->varScp()->varp()->prettyName());
|
||||
<<vvertexp->varScp()->varp()->prettyName()<<"'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -822,9 +821,11 @@ void GateVisitor::warnSignals() {
|
||||
) {
|
||||
vscp->varp()->user2(true); // Warn only once per signal
|
||||
vscp->v3warn(SYNCASYNCNET, "Signal flopped as both synchronous and async: "
|
||||
<<vscp->prettyName()<<endl
|
||||
<<ap->warnMore()<<"... Location of async usage"<<endl
|
||||
<<sp->warnMore()<<"... Location of sync usage"<<endl);
|
||||
<<vscp->prettyNameQ()<<endl
|
||||
<<ap->warnOther()<<"... Location of async usage"<<endl
|
||||
<<ap->warnContextPrimary()<<endl
|
||||
<<sp->warnOther()<<"... Location of sync usage"<<endl
|
||||
<<sp->warnContextSecondary());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -834,6 +835,8 @@ void GateVisitor::warnSignals() {
|
||||
//######################################################################
|
||||
// Push constant into expressions and reevaluate
|
||||
|
||||
class GateDedupeVarVisitor;
|
||||
|
||||
class GateElimVisitor : public GateBaseVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
@@ -841,21 +844,26 @@ private:
|
||||
AstVarScope* m_elimVarScp; // Variable being eliminated
|
||||
AstNode* m_replaceTreep; // What to replace the variable with
|
||||
bool m_didReplace; // Did we do any replacements
|
||||
GateDedupeVarVisitor* m_varVisp; // Callback to keep hash up to date
|
||||
|
||||
// METHODS
|
||||
void hashReplace(AstNode* oldp, AstNode* newp);
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeVarRef* nodep) {
|
||||
if (nodep->varScopep() == m_elimVarScp) {
|
||||
// Substitute in the new tree
|
||||
// It's possible we substitute into something that will be reduced more later
|
||||
// It's possible we substitute into something that will be reduced more later,
|
||||
// however, as we never delete the top Always/initial statement, all should be well.
|
||||
m_didReplace = true;
|
||||
if (nodep->lvalue()) nodep->v3fatalSrc("Can't replace lvalue assignments with const var");
|
||||
UASSERT_OBJ(!nodep->lvalue(), nodep,
|
||||
"Can't replace lvalue assignments with const var");
|
||||
AstNode* substp = m_replaceTreep->cloneTree(false);
|
||||
if (VN_IS(nodep, NodeVarRef)
|
||||
&& VN_IS(substp, NodeVarRef)
|
||||
&& nodep->same(substp)) {
|
||||
// Prevent a infinite loop...
|
||||
substp->v3fatalSrc("Replacing node with itself; perhaps circular logic?");
|
||||
}
|
||||
UASSERT_OBJ(!(VN_IS(nodep, NodeVarRef)
|
||||
&& VN_IS(substp, NodeVarRef)
|
||||
&& nodep->same(substp)),
|
||||
// Prevent an infinite loop...
|
||||
substp, "Replacing node with itself; perhaps circular logic?");
|
||||
// Which fileline() to use?
|
||||
// If replacing with logic, an error/warning is likely to want to point to the logic
|
||||
// IE what we're replacing with.
|
||||
@@ -864,6 +872,7 @@ private:
|
||||
if (VN_IS(substp, VarRef)) substp->fileline(nodep->fileline());
|
||||
// Make the substp an rvalue like nodep. This facilitate the hashing in dedupe.
|
||||
if (AstNodeVarRef* varrefp = VN_CAST(substp, NodeVarRef)) varrefp->lvalue(false);
|
||||
hashReplace(nodep, substp);
|
||||
nodep->replaceWith(substp);
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
@@ -874,10 +883,12 @@ private:
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
virtual ~GateElimVisitor() {}
|
||||
GateElimVisitor(AstNode* nodep, AstVarScope* varscp, AstNode* replaceTreep) {
|
||||
GateElimVisitor(AstNode* nodep, AstVarScope* varscp, AstNode* replaceTreep,
|
||||
GateDedupeVarVisitor* varVisp) {
|
||||
m_didReplace = false;
|
||||
m_elimVarScp = varscp;
|
||||
m_replaceTreep = replaceTreep;
|
||||
m_varVisp = varVisp;
|
||||
iterate(nodep);
|
||||
}
|
||||
bool didReplace() const { return m_didReplace; }
|
||||
@@ -885,7 +896,7 @@ public:
|
||||
|
||||
void GateVisitor::optimizeElimVar(AstVarScope* varscp, AstNode* substp, AstNode* consumerp) {
|
||||
if (debug()>=5) consumerp->dumpTree(cout, "\telimUsePre: ");
|
||||
GateElimVisitor elimVisitor (consumerp, varscp, substp);
|
||||
GateElimVisitor elimVisitor (consumerp, varscp, substp, NULL);
|
||||
if (elimVisitor.didReplace()) {
|
||||
if (debug()>=9) consumerp->dumpTree(cout, "\telimUseCns: ");
|
||||
//Caution: Can't let V3Const change our handle to consumerp, such as by
|
||||
@@ -901,16 +912,28 @@ void GateVisitor::optimizeElimVar(AstVarScope* varscp, AstNode* substp, AstNode*
|
||||
//######################################################################
|
||||
// Auxiliary hash class for GateDedupeVarVisitor
|
||||
|
||||
class GateDedupeHash : public V3HashedUserCheck {
|
||||
class GateDedupeHash : public V3HashedUserSame {
|
||||
public:
|
||||
// TYPES
|
||||
typedef std::set<AstNode*> NodeSet;
|
||||
|
||||
private:
|
||||
// NODE STATE
|
||||
// Ast*::user2p -> parent AstNodeAssign* for this rhsp
|
||||
// Ast*::user3p -> AstNode* checked in test for duplicate
|
||||
// Ast*::user5p -> AstNode* checked in test for duplicate
|
||||
// Ast*::user3p -> AstActive* of assign, for isSame() in test for duplicate
|
||||
// Set to NULL if this assign's tree was later replaced
|
||||
// Ast*::user5p -> AstNode* of assign if condition, for isSame() in test for duplicate
|
||||
// Set to NULL if this assign's tree was later replaced
|
||||
// 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)
|
||||
AstUser5InUse m_inuser5;
|
||||
|
||||
V3Hashed m_hashed; // Hash, contains rhs of assigns
|
||||
NodeSet m_nodeDeleteds; // Any node in this hash was deleted
|
||||
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void hash(AstNode* nodep) {
|
||||
// !NULL && the object is hashable
|
||||
@@ -928,13 +951,46 @@ private:
|
||||
return node1p == node2p || sameHash(node1p, node2p);
|
||||
}
|
||||
public:
|
||||
bool check(AstNode* node1p, AstNode* node2p) {
|
||||
GateDedupeHash() { }
|
||||
~GateDedupeHash() {
|
||||
if (v3Global.opt.debugCheck()) check();
|
||||
}
|
||||
|
||||
// About to replace a node; any node we're tracking refers to oldp, change it to newp.
|
||||
// This might be a variable on the lhs of the duplicate tree,
|
||||
// or could be a rhs variable in a tree we're not replacing (or not yet anyways)
|
||||
void hashReplace(AstNode* oldp, AstNode* newp) {
|
||||
UINFO(9,"replacing "<<(void*)oldp<<" with "<<(void*)newp<<endl);
|
||||
// We could update the user3p and user5p but the resulting node
|
||||
// still has incorrect hash. We really need to remove all hash on
|
||||
// the whole hash entry tree involving the replaced node and
|
||||
// rehash. That's complicated and this is rare, so just remove it
|
||||
// from consideration.
|
||||
m_nodeDeleteds.insert(oldp);
|
||||
}
|
||||
bool isReplaced(AstNode* nodep) {
|
||||
// Assignment may have been hashReplaced, if so consider non-match (effectively removed)
|
||||
UASSERT_OBJ(!VN_IS(nodep, NodeAssign), nodep, "Dedup attempt on non-assign");
|
||||
AstNode* extra1p = nodep->user3p();
|
||||
AstNode* extra2p = nodep->user5p();
|
||||
return ((extra1p && m_nodeDeleteds.find(extra1p) != m_nodeDeleteds.end())
|
||||
|| (extra2p && m_nodeDeleteds.find(extra2p) != m_nodeDeleteds.end()));
|
||||
}
|
||||
|
||||
// Callback from V3Hashed::findDuplicate
|
||||
bool isSame(AstNode* node1p, AstNode* node2p) {
|
||||
// Assignment may have been hashReplaced, if so consider non-match (effectively removed)
|
||||
if (isReplaced(node1p) || isReplaced(node2p)) {
|
||||
//UINFO(9, "isSame hit on replaced "<<(void*)node1p<<" "<<(void*)node2p<<endl);
|
||||
return false;
|
||||
}
|
||||
return same(node1p->user3p(), node2p->user3p())
|
||||
&& same(node1p->user5p(), node2p->user5p())
|
||||
&& node1p->user2p()->type() == node2p->user2p()->type();
|
||||
}
|
||||
|
||||
AstNodeAssign* hashAndFindDupe(AstNodeAssign* assignp, AstNode* extra1p, AstNode* extra2p) {
|
||||
// Legal for extra1p/2p to be NULL, we'll compare with other assigns with extras also NULL
|
||||
AstNode *rhsp = assignp->rhsp();
|
||||
rhsp->user2p(assignp);
|
||||
rhsp->user3p(extra1p);
|
||||
@@ -952,8 +1008,26 @@ public:
|
||||
m_hashed.erase(inserted);
|
||||
return VN_CAST(m_hashed.iteratorNodep(dupit)->user2p(), NodeAssign);
|
||||
}
|
||||
// Retain new inserted information
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void check() {
|
||||
m_hashed.check();
|
||||
for (V3Hashed::HashMmap::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
|
||||
AstNode* nodep = it->second;
|
||||
AstNode* activep = nodep->user3p();
|
||||
AstNode* condVarp = nodep->user5p();
|
||||
if (!isReplaced(nodep)) {
|
||||
// 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");
|
||||
UASSERT_OBJ(!condVarp || !VN_DELETED(condVarp),
|
||||
nodep, "V3Hashed check failed, lost if pointer");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -972,7 +1046,7 @@ class GateDedupeVarVisitor : public GateBaseVisitor {
|
||||
// Any other ordering or node type, except for an AstComment, makes it not dedupable
|
||||
private:
|
||||
// STATE
|
||||
GateDedupeHash m_hash; // Hash used to find dupes of rhs of assign
|
||||
GateDedupeHash m_ghash; // Hash used to find dupes of rhs of assign
|
||||
AstNodeAssign* m_assignp; // Assign found for dedupe
|
||||
AstNode* m_ifCondp; // IF condition that assign is under
|
||||
bool m_always; // Assign is under an always
|
||||
@@ -1030,6 +1104,7 @@ public:
|
||||
m_always = false;
|
||||
m_dedupable = true;
|
||||
}
|
||||
~GateDedupeVarVisitor() { }
|
||||
// PUBLIC METHODS
|
||||
AstNodeVarRef* findDupe(AstNode* nodep, AstVarScope* consumerVarScopep, AstActive* activep) {
|
||||
m_assignp = NULL;
|
||||
@@ -1041,18 +1116,29 @@ public:
|
||||
AstNode* lhsp = m_assignp->lhsp();
|
||||
// Possible todo, handle more complex lhs expressions
|
||||
if (AstNodeVarRef* lhsVarRefp = VN_CAST(lhsp, NodeVarRef)) {
|
||||
if (lhsVarRefp->varScopep() != consumerVarScopep) {
|
||||
consumerVarScopep->v3fatalSrc("Consumer doesn't match lhs of assign");
|
||||
}
|
||||
if (AstNodeAssign* dup = m_hash.hashAndFindDupe(m_assignp, activep, m_ifCondp)) {
|
||||
UASSERT_OBJ(lhsVarRefp->varScopep() == consumerVarScopep,
|
||||
consumerVarScopep, "Consumer doesn't match lhs of assign");
|
||||
if (AstNodeAssign* dup = m_ghash.hashAndFindDupe(m_assignp, activep, m_ifCondp)) {
|
||||
return static_cast<AstNodeVarRef*>(dup->lhsp());
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
void hashReplace(AstNode* oldp, AstNode* newp) {
|
||||
m_ghash.hashReplace(oldp, newp);
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
void GateElimVisitor::hashReplace(AstNode* oldp, AstNode* newp) {
|
||||
UINFO(9,"hashReplace "<<(void*)oldp<<" -> "<<(void*)newp<<endl);
|
||||
if (m_varVisp) {
|
||||
m_varVisp->hashReplace(oldp, newp);
|
||||
}
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// Recurse through the graph, looking for duplicate expressions on the rhs of an assign
|
||||
|
||||
@@ -1075,10 +1161,11 @@ private:
|
||||
if (vvertexp->inSize1()) {
|
||||
AstNodeVarRef* dupVarRefp = static_cast<AstNodeVarRef*>
|
||||
(vvertexp->iterateInEdges(*this, VNUser(vvertexp)).toNodep());
|
||||
if (dupVarRefp) {
|
||||
if (dupVarRefp) { // visit(GateLogicVertex*...) returned match
|
||||
V3GraphEdge* edgep = vvertexp->inBeginp();
|
||||
GateLogicVertex* lvertexp = static_cast<GateLogicVertex*>(edgep->fromp());
|
||||
if (!vvertexp->dedupable()) vvertexp->varScp()->v3fatalSrc("GateLogicVertex* visit should have returned NULL if consumer var vertex is not dedupable.");
|
||||
UASSERT_OBJ(vvertexp->dedupable(), vvertexp->varScp(),
|
||||
"GateLogicVertex* visit should have returned NULL if consumer var vertex is not dedupable.");
|
||||
GateOkVisitor okVisitor(lvertexp->nodep(), false, true);
|
||||
if (okVisitor.isSimple()) {
|
||||
AstVarScope* dupVarScopep = dupVarRefp->varScopep();
|
||||
@@ -1091,7 +1178,7 @@ private:
|
||||
GateLogicVertex* consumeVertexp
|
||||
= dynamic_cast<GateLogicVertex*>(outedgep->top());
|
||||
AstNode* consumerp = consumeVertexp->nodep();
|
||||
GateElimVisitor elimVisitor(consumerp, vvertexp->varScp(), dupVarRefp);
|
||||
GateElimVisitor elimVisitor(consumerp, vvertexp->varScp(), dupVarRefp, &m_varVisitor);
|
||||
outedgep = outedgep->relinkFromp(dupVvertexp);
|
||||
}
|
||||
|
||||
@@ -1150,6 +1237,7 @@ void GateVisitor::dedupe() {
|
||||
AstNode::user2ClearTree();
|
||||
GateDedupeGraphVisitor deduper;
|
||||
// Traverse starting from each of the clocks
|
||||
UINFO(9,"Gate dedupe() clocks:\n");
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
||||
if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
||||
if (vvertexp->isClock()) {
|
||||
@@ -1158,6 +1246,7 @@ void GateVisitor::dedupe() {
|
||||
}
|
||||
}
|
||||
// Traverse starting from each of the outputs
|
||||
UINFO(9,"Gate dedupe() outputs:\n");
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
||||
if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
||||
if (vvertexp->isTop() && vvertexp->varScp()->varp()->isWritable()) {
|
||||
@@ -1241,7 +1330,7 @@ private:
|
||||
oldrhsp->deleteTree(); VL_DANGLING(oldrhsp);
|
||||
m_assignp->dtypeChgWidthSigned(m_assignp->width()+assignp->width(),
|
||||
m_assignp->width()+assignp->width(),
|
||||
AstNumeric::fromBool(true));
|
||||
AstNumeric::SIGNED);
|
||||
// don't need to delete, will be handled
|
||||
//assignp->unlinkFrBack(); assignp->deleteTree(); VL_DANGLING(assignp);
|
||||
|
||||
@@ -1295,6 +1384,7 @@ public:
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void GateVisitor::mergeAssigns() {
|
||||
UINFO(6, "mergeAssigns\n");
|
||||
GateMergeAssignsGraphVisitor merger(&m_graph);
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
||||
if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
||||
|
||||
+5
-5
@@ -87,7 +87,7 @@ private:
|
||||
AstNode::user2ClearTree(); // user2p() used on entire tree
|
||||
|
||||
AstScope* scopep = nodep->scopep();
|
||||
if (!scopep) nodep->v3fatalSrc("No scope found on top level");
|
||||
UASSERT_OBJ(scopep, nodep, "No scope found on top level");
|
||||
m_scopetopp = scopep;
|
||||
|
||||
iterateChildren(nodep);
|
||||
@@ -96,7 +96,7 @@ private:
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
// Consumption/generation of a variable,
|
||||
AstVarScope* vscp = nodep->varScopep();
|
||||
if (!vscp) nodep->v3fatalSrc("Scope not assigned");
|
||||
UASSERT_OBJ(vscp, nodep, "Scope not assigned");
|
||||
if (m_activep && !nodep->user3()) {
|
||||
nodep->user3(true);
|
||||
if (vscp->isCircular()) {
|
||||
@@ -111,7 +111,7 @@ private:
|
||||
}
|
||||
virtual void visit(AstActive* nodep) {
|
||||
m_activep = nodep;
|
||||
if (!nodep->sensesp()) nodep->v3fatalSrc("Unlinked");
|
||||
UASSERT_OBJ(nodep->sensesp(), nodep, "Unlinked");
|
||||
iterateChildren(nodep->sensesp()); // iterateAndNext?
|
||||
m_activep = NULL;
|
||||
iterateChildren(nodep);
|
||||
@@ -191,7 +191,7 @@ private:
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
// Consumption/generation of a variable,
|
||||
AstVarScope* vscp = nodep->varScopep();
|
||||
if (!vscp) nodep->v3fatalSrc("Scope not assigned");
|
||||
UASSERT_OBJ(vscp, nodep, "Scope not assigned");
|
||||
if (m_activep) {
|
||||
UINFO(8," VarAct "<<nodep<<endl);
|
||||
vscp->user1(true);
|
||||
@@ -212,7 +212,7 @@ private:
|
||||
virtual void visit(AstActive* nodep) {
|
||||
UINFO(8,"ACTIVE "<<nodep<<endl);
|
||||
m_activep = nodep;
|
||||
if (!nodep->sensesp()) nodep->v3fatalSrc("Unlinked");
|
||||
UASSERT_OBJ(nodep->sensesp(), nodep, "Unlinked");
|
||||
iterateChildren(nodep->sensesp()); // iterateAndNext?
|
||||
m_activep = NULL;
|
||||
iterateChildren(nodep);
|
||||
|
||||
@@ -118,11 +118,6 @@ public:
|
||||
/// (I.E. all loops will occur within each color, not between them.)
|
||||
void stronglyConnected(V3EdgeFuncP edgeFuncp);
|
||||
|
||||
/// Assign same color to all destination vertices that have same
|
||||
/// subgraph feeding into them
|
||||
/// (I.E. all "from" nodes are common within each color)
|
||||
/// See V3ClkGater if this is needed again; it got specialized
|
||||
|
||||
/// Assign a ordering number to all vertexes in a tree.
|
||||
/// All nodes with no inputs will get rank 1
|
||||
void rank(V3EdgeFuncP edgeFuncp);
|
||||
|
||||
+1
-1
@@ -579,7 +579,7 @@ double V3Graph::orderDFSIterate(V3GraphVertex* vertexp) {
|
||||
// Compute fanouts of each node
|
||||
// If forward edge, don't double count that fanout
|
||||
if (vertexp->user() == 2) return vertexp->fanout(); // Already processed it
|
||||
if (vertexp->user() == 1) vertexp->v3fatalSrc("Loop found, backward edges should be dead");
|
||||
UASSERT_OBJ(vertexp->user() != 1, vertexp, "Loop found, backward edges should be dead");
|
||||
vertexp->user(1);
|
||||
double fanout = 0;
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
|
||||
+20
-17
@@ -40,7 +40,7 @@ DfaVertex* DfaGraph::findStart() {
|
||||
vertexp; vertexp=vertexp->verticesNextp()) {
|
||||
if (DfaVertex* vvertexp = dynamic_cast<DfaVertex*>(vertexp)) {
|
||||
if (vvertexp->start()) {
|
||||
if (startp) vertexp->v3fatalSrc("Multiple start points in NFA graph");
|
||||
UASSERT_OBJ(!startp, vertexp, "Multiple start points in NFA graph");
|
||||
startp = vvertexp;
|
||||
}
|
||||
} else {
|
||||
@@ -118,9 +118,8 @@ private:
|
||||
DfaVertex* nfaStatep = static_cast<DfaVertex*>(dfaEdgep->top());
|
||||
hash ^= hashVertex(nfaStatep);
|
||||
if (debug()) {
|
||||
if (nfaStatep->user()==m_step) {
|
||||
nfaStatep->v3fatalSrc("DFA state points to duplicate NFA state.");
|
||||
}
|
||||
UASSERT_OBJ(nfaStatep->user() != m_step, nfaStatep,
|
||||
"DFA state points to duplicate NFA state.");
|
||||
nfaStatep->user(m_step);
|
||||
}
|
||||
}
|
||||
@@ -220,19 +219,23 @@ private:
|
||||
|
||||
// Expand the nfasWithInput list to include epsilon states
|
||||
// reachable by those on nfasWithInput
|
||||
for (DfaStates::iterator nfaIt=nfasWithInput.begin();
|
||||
nfaIt!=nfasWithInput.end(); ++nfaIt) {
|
||||
DfaVertex* nfaStatep = *nfaIt;
|
||||
// Foreach epsilon-reachable (on this nfaStatep)
|
||||
for (V3GraphEdge* nfaEdgep = nfaStatep->outBeginp();
|
||||
nfaEdgep; nfaEdgep=nfaEdgep->outNextp()) {
|
||||
DfaEdge* cNfaEdgep = static_cast<DfaEdge*>(nfaEdgep);
|
||||
if (cNfaEdgep->epsilon()) {
|
||||
DfaVertex* nextStatep = static_cast<DfaVertex*>(cNfaEdgep->top());
|
||||
if (unseenNfaThisStep(nextStatep)) { // Not processed?
|
||||
nfasWithInput.push_back(nextStatep);
|
||||
nextStatep->user(m_step);
|
||||
UINFO(9," Epsilon Reachable "<<nextStatep<<endl);
|
||||
{
|
||||
DfaStates nfasTodo = nfasWithInput;
|
||||
nfasWithInput.clear(); // Now the completed list
|
||||
while (!nfasTodo.empty()) {
|
||||
DfaVertex* nfaStatep = nfasTodo.front(); nfasTodo.pop_front();
|
||||
nfasWithInput.push_back(nfaStatep);
|
||||
// Foreach epsilon-reachable (on this nfaStatep)
|
||||
for (V3GraphEdge* nfaEdgep = nfaStatep->outBeginp();
|
||||
nfaEdgep; nfaEdgep=nfaEdgep->outNextp()) {
|
||||
DfaEdge* cNfaEdgep = static_cast<DfaEdge*>(nfaEdgep);
|
||||
if (cNfaEdgep->epsilon()) {
|
||||
DfaVertex* nextStatep = static_cast<DfaVertex*>(cNfaEdgep->top());
|
||||
if (unseenNfaThisStep(nextStatep)) { // Not processed?
|
||||
nfasTodo.push_back(nextStatep);
|
||||
nextStatep->user(m_step);
|
||||
UINFO(9," Epsilon Reachable "<<nextStatep<<endl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,9 +94,8 @@ void GraphPathChecker::initHalfCriticalPaths(GraphWay way, bool checkOnly) {
|
||||
|
||||
GraphPCNode* ourUserp = static_cast<GraphPCNode*>(vertexp->userp());
|
||||
if (checkOnly) {
|
||||
if (ourUserp->m_cp[way] != critPathCost) {
|
||||
vertexp->v3fatalSrc("Validation of critical paths failed");
|
||||
}
|
||||
UASSERT_OBJ(ourUserp->m_cp[way] == critPathCost,
|
||||
vertexp, "Validation of critical paths failed");
|
||||
} else {
|
||||
ourUserp->m_cp[way] = critPathCost;
|
||||
}
|
||||
|
||||
+5
-7
@@ -60,7 +60,7 @@ private:
|
||||
// Decrement blocking edges count, return true if the vertex is
|
||||
// newly unblocked
|
||||
bool unblock() {
|
||||
if (m_numBlockingEdges <= 0) vertexp()->v3fatalSrc("Underflow of blocking edges");
|
||||
UASSERT_OBJ(m_numBlockingEdges > 0, vertexp(), "Underflow of blocking edges");
|
||||
m_numBlockingEdges--;
|
||||
return (m_numBlockingEdges == 0);
|
||||
}
|
||||
@@ -208,9 +208,8 @@ private:
|
||||
|
||||
typename WaitingVertices::iterator it =
|
||||
m_waitingVertices.find(toVertexp);
|
||||
if (it == m_waitingVertices.end()) {
|
||||
toVertexp->v3fatalSrc("Found edge into vertex not in waiting list.");
|
||||
}
|
||||
UASSERT_OBJ(it != m_waitingVertices.end(), toVertexp,
|
||||
"Found edge into vertex not in waiting list.");
|
||||
if (it->second.unblock()) {
|
||||
m_readyVertices.insert(it->second);
|
||||
m_waitingVertices.erase(it);
|
||||
@@ -223,9 +222,8 @@ private:
|
||||
|
||||
typename WaitingVertices::iterator it =
|
||||
m_waitingVertices.find(fromVertexp);
|
||||
if (it == m_waitingVertices.end()) {
|
||||
fromVertexp->v3fatalSrc("Found edge into vertex not in waiting list.");
|
||||
}
|
||||
UASSERT_OBJ(it != m_waitingVertices.end(), fromVertexp,
|
||||
"Found edge into vertex not in waiting list.");
|
||||
if (it->second.unblock()) {
|
||||
m_readyVertices.insert(it->second);
|
||||
m_waitingVertices.erase(it);
|
||||
|
||||
+3
-14
@@ -32,15 +32,6 @@
|
||||
// Test class
|
||||
|
||||
class V3GraphTest {
|
||||
public:
|
||||
// ***These tests only run with DEBUG ON***
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
// Note setting just --debug will not enable this, as we exit when we run the test
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__, 0);
|
||||
return level;
|
||||
}
|
||||
|
||||
protected:
|
||||
// MEMBERS
|
||||
DfaGraph m_graph;
|
||||
@@ -59,9 +50,8 @@ protected:
|
||||
public:
|
||||
V3GraphTest() {}
|
||||
virtual ~V3GraphTest() {}
|
||||
void run() {
|
||||
if (debug()) runTest();
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
void run() { runTest(); }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -69,7 +59,7 @@ public:
|
||||
// Vertices and nodes
|
||||
|
||||
class V3GraphTestVertex : public V3GraphVertex {
|
||||
string m_name;
|
||||
string m_name;
|
||||
public:
|
||||
V3GraphTestVertex(V3Graph* graphp, const string& name)
|
||||
: V3GraphVertex(graphp), m_name(name) {}
|
||||
@@ -363,5 +353,4 @@ void V3Graph::selfTest() {
|
||||
{ V3GraphTestVars test; test.run(); }
|
||||
{ V3GraphTestDfa test; test.run(); }
|
||||
{ V3GraphTestImport test; test.run(); }
|
||||
if (V3GraphTest::debug()) v3fatalSrc("Exiting due to graph testing enabled");
|
||||
}
|
||||
|
||||
+22
-27
@@ -59,16 +59,15 @@ private:
|
||||
void nodeHashIterate(AstNode* nodep) {
|
||||
V3Hash thisHash;
|
||||
if (!m_cacheInUser4 || !nodep->user4()) {
|
||||
if (VN_IS(nodep->backp(), CFunc)
|
||||
&& !(VN_IS(nodep, NodeStmt) || VN_IS(nodep, CFunc))) {
|
||||
nodep->v3fatalSrc("Node "<<nodep->prettyTypeName()<<" in statement position but not marked stmt (node under function)");
|
||||
}
|
||||
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)");
|
||||
V3Hash oldHash = m_lowerHash;
|
||||
{
|
||||
m_lowerHash = nodep->sameHash();
|
||||
if (m_lowerHash.isIllegal()) {
|
||||
nodep->v3fatalSrc("sameHash function undefined (returns 0) for node under CFunc.");
|
||||
}
|
||||
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,
|
||||
@@ -132,8 +131,8 @@ void V3Hashed::hash(AstNode* nodep) {
|
||||
}
|
||||
|
||||
bool V3Hashed::sameNodes(AstNode* node1p, AstNode* node2p) {
|
||||
if (!node1p->user4p()) node1p->v3fatalSrc("Called isIdentical on non-hashed nodes");
|
||||
if (!node2p->user4p()) node2p->v3fatalSrc("Called isIdentical on non-hashed nodes");
|
||||
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));
|
||||
}
|
||||
@@ -141,11 +140,18 @@ bool V3Hashed::sameNodes(AstNode* node1p, AstNode* node2p) {
|
||||
void V3Hashed::erase(iterator it) {
|
||||
AstNode* nodep = iteratorNodep(it);
|
||||
UINFO(8," erase "<<nodep<<endl);
|
||||
if (!nodep->user4p()) nodep->v3fatalSrc("Called removeNode on non-hashed node");
|
||||
UASSERT_OBJ(nodep->user4p(), nodep, "Called removeNode on non-hashed node");
|
||||
m_hashMmap.erase(it);
|
||||
nodep->user4p(NULL); // So we don't allow removeNode again
|
||||
}
|
||||
|
||||
void V3Hashed::check() {
|
||||
for (HashMmap::iterator it = begin(); it != end(); ++it) {
|
||||
AstNode* nodep = it->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);
|
||||
@@ -161,7 +167,7 @@ void V3Hashed::dumpFile(const string& filename, bool tree) {
|
||||
V3Hash lasthash;
|
||||
int num_in_bucket = 0;
|
||||
for (HashMmap::iterator it=begin(); 1; ++it) {
|
||||
if (lasthash != it->first || it==end()) {
|
||||
if (it==end() || lasthash != it->first) {
|
||||
if (it!=end()) lasthash = it->first;
|
||||
if (num_in_bucket) {
|
||||
if (dist.find(num_in_bucket)==dist.end()) {
|
||||
@@ -194,27 +200,16 @@ void V3Hashed::dumpFile(const string& filename, bool tree) {
|
||||
}
|
||||
}
|
||||
|
||||
V3Hashed::iterator V3Hashed::findDuplicate(AstNode* nodep) {
|
||||
V3Hashed::iterator V3Hashed::findDuplicate(AstNode* nodep, V3HashedUserSame* checkp) {
|
||||
UINFO(8," findD "<<nodep<<endl);
|
||||
if (!nodep->user4p()) nodep->v3fatalSrc("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 && sameNodes(nodep, node2p)) {
|
||||
return eqit;
|
||||
}
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
V3Hashed::iterator V3Hashed::findDuplicate(AstNode* nodep, V3HashedUserCheck* checkp) {
|
||||
UINFO(8," findD "<<nodep<<endl);
|
||||
if (!nodep->user4p()) nodep->v3fatalSrc("Called findDuplicate on non-hashed node");
|
||||
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->check(nodep, node2p) && sameNodes(nodep, node2p)) {
|
||||
if (nodep != node2p
|
||||
&& (!checkp || checkp->isSame(nodep, node2p))
|
||||
&& sameNodes(nodep, node2p)) {
|
||||
return eqit;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-7
@@ -40,11 +40,11 @@ public:
|
||||
|
||||
//============================================================================
|
||||
|
||||
struct V3HashedUserCheck {
|
||||
struct V3HashedUserSame {
|
||||
// Functor for V3Hashed::findDuplicate
|
||||
virtual bool check(AstNode*, AstNode*) = 0;
|
||||
V3HashedUserCheck() {}
|
||||
virtual ~V3HashedUserCheck() {}
|
||||
virtual bool isSame(AstNode*, AstNode*) = 0;
|
||||
V3HashedUserSame() {}
|
||||
virtual ~V3HashedUserSame() {}
|
||||
};
|
||||
|
||||
class V3Hashed : public VHashedBase {
|
||||
@@ -53,8 +53,8 @@ class V3Hashed : public VHashedBase {
|
||||
AstUser4InUse m_inuser4;
|
||||
|
||||
// TYPES
|
||||
typedef std::multimap<V3Hash,AstNode*> HashMmap;
|
||||
public:
|
||||
typedef std::multimap<V3Hash,AstNode*> HashMmap;
|
||||
typedef HashMmap::iterator iterator;
|
||||
private:
|
||||
// MEMBERS
|
||||
@@ -72,12 +72,13 @@ public:
|
||||
|
||||
// METHODS
|
||||
void clear() { m_hashMmap.clear(); AstNode::user4ClearTree(); }
|
||||
void check(); // Check assertions on structure
|
||||
iterator hashAndInsert(AstNode* nodep); // Hash the node, and insert into map. Return iterator to inserted
|
||||
void hash(AstNode* nodep); // Only hash the node
|
||||
bool sameNodes(AstNode* node1p, AstNode* node2p); // After hashing, and tell if identical
|
||||
void erase(iterator it); // Remove node from structures
|
||||
iterator findDuplicate(AstNode* nodep); // Return duplicate in hash, if any
|
||||
iterator findDuplicate(AstNode* nodep, V3HashedUserCheck* checkp); // Extra user checks for sameness
|
||||
// Return duplicate in hash, if any, with optional user check for sameness
|
||||
iterator findDuplicate(AstNode* nodep, V3HashedUserSame* checkp=NULL);
|
||||
AstNode* iteratorNodep(iterator it) { return it->second; }
|
||||
void dumpFile(const string& filename, bool tree);
|
||||
void dumpFilePrefixed(const string& nameComment, bool tree=false);
|
||||
|
||||
+8
-10
@@ -124,7 +124,7 @@ private:
|
||||
if (nodep->pragType() == AstPragmaType::INLINE_MODULE) {
|
||||
//UINFO(0,"PRAG MARK "<<m_modp<<endl);
|
||||
if (!m_modp) {
|
||||
nodep->v3error("Inline pragma not under a module");
|
||||
nodep->v3error("Inline pragma not under a module"); // LCOV_EXCL_LINE
|
||||
} else if (m_modp->user2() == CIL_MAYBE
|
||||
|| m_modp->user2() == CIL_NOTSOFT) {
|
||||
m_modp->user2(CIL_USER);
|
||||
@@ -132,7 +132,7 @@ private:
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); // Remove so don't propagate to upper cell...
|
||||
} else if (nodep->pragType() == AstPragmaType::NO_INLINE_MODULE) {
|
||||
if (!m_modp) {
|
||||
nodep->v3error("Inline pragma not under a module");
|
||||
nodep->v3error("Inline pragma not under a module"); // LCOV_EXCL_LINE
|
||||
} else {
|
||||
cantInline("Pragma NO_INLINE_MODULE", false);
|
||||
}
|
||||
@@ -306,9 +306,8 @@ private:
|
||||
AstConst* exprconstp = VN_CAST(nodep->user2p(), Const);
|
||||
AstVarRef* exprvarrefp = VN_CAST(nodep->user2p(), VarRef);
|
||||
UINFO(8,"connectto: "<<nodep->user2p()<<endl);
|
||||
if (!exprconstp && !exprvarrefp) {
|
||||
nodep->v3fatalSrc("Unknown interconnect type; pinReconnectSimple should have cleared up");
|
||||
}
|
||||
UASSERT_OBJ(exprconstp || exprvarrefp, nodep,
|
||||
"Unknown interconnect type; pinReconnectSimple should have cleared up");
|
||||
if (exprconstp) {
|
||||
m_modp->addStmtp(new AstAssignW(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), nodep, true),
|
||||
@@ -318,7 +317,7 @@ private:
|
||||
// the logic changes up and down; if we aliased, we might
|
||||
// remove the change detection on the output variable.
|
||||
UINFO(9,"public pin assign: "<<exprvarrefp<<endl);
|
||||
if (nodep->isNonOutput()) nodep->v3fatalSrc("Outputs only - inputs use AssignAlias");
|
||||
UASSERT_OBJ(!nodep->isNonOutput(), nodep, "Outputs only - inputs use AssignAlias");
|
||||
m_modp->addStmtp(
|
||||
new AstAssignW(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), exprvarrefp->varp(), true),
|
||||
@@ -555,12 +554,11 @@ private:
|
||||
// delete it in later optimizations.
|
||||
AstVar* pinOldVarp = pinp->modVarp();
|
||||
AstVar* pinNewVarp = pinOldVarp->clonep();
|
||||
if (!pinNewVarp) pinOldVarp->v3fatalSrc("Cloning failed");
|
||||
UASSERT_OBJ(pinNewVarp, pinOldVarp, "Cloning failed");
|
||||
|
||||
AstNode* connectRefp = pinp->exprp();
|
||||
if (!VN_IS(connectRefp, Const) && !VN_IS(connectRefp, VarRef)) {
|
||||
pinp->v3fatalSrc("Unknown interconnect type; pinReconnectSimple should have cleared up");
|
||||
}
|
||||
UASSERT_OBJ(VN_IS(connectRefp, Const) || VN_IS(connectRefp, VarRef), pinp,
|
||||
"Unknown interconnect type; pinReconnectSimple should have cleared up");
|
||||
V3Inst::checkOutputShort(pinp);
|
||||
|
||||
// Propagate any attributes across the interconnect
|
||||
|
||||
+10
-8
@@ -75,9 +75,8 @@ private:
|
||||
V3Inst::pinReconnectSimple(nodep, m_cellp, false);
|
||||
// Make an ASSIGNW (expr, pin)
|
||||
AstNode* exprp = nodep->exprp()->cloneTree(false);
|
||||
if (exprp->width() != nodep->modVarp()->width()) {
|
||||
nodep->v3fatalSrc("Width mismatch, should have been handled in pinReconnectSimple");
|
||||
}
|
||||
UASSERT_OBJ(exprp->width() == nodep->modVarp()->width(), nodep,
|
||||
"Width mismatch, should have been handled in pinReconnectSimple");
|
||||
if (nodep->modVarp()->isInoutish()) {
|
||||
nodep->v3fatalSrc("Unsupported: Verilator is a 2-state simulator");
|
||||
} else if (nodep->modVarp()->isWritable()) {
|
||||
@@ -104,7 +103,8 @@ private:
|
||||
nodep->modVarp(), m_cellp->name(), false);
|
||||
const AstVarRef* refp = VN_CAST(exprp, VarRef);
|
||||
const AstVarXRef* xrefp = VN_CAST(exprp, VarXRef);
|
||||
if (!refp && !xrefp) exprp->v3fatalSrc("Interfaces: Pin is not connected to a VarRef or VarXRef");
|
||||
UASSERT_OBJ(refp || xrefp, exprp,
|
||||
"Interfaces: Pin is not connected to a VarRef or VarXRef");
|
||||
AstAssignVarScope* assp = new AstAssignVarScope(exprp->fileline(), lhsp, exprp);
|
||||
m_cellp->addNextHere(assp);
|
||||
} else {
|
||||
@@ -249,7 +249,7 @@ private:
|
||||
virtual void visit(AstCell* nodep) {
|
||||
UINFO(4," CELL "<<nodep<<endl);
|
||||
// Find submodule vars
|
||||
if (!nodep->modp()) nodep->v3fatalSrc("Unlinked");
|
||||
UASSERT_OBJ(nodep->modp(), nodep, "Unlinked");
|
||||
m_deModVars.main(nodep->modp());
|
||||
//
|
||||
if (nodep->rangep()) {
|
||||
@@ -337,8 +337,10 @@ private:
|
||||
// Arrayed instants: one bit for each of the instants (each
|
||||
// assign is 1 pinwidth wide)
|
||||
if (m_cellRangep->littleEndian()) {
|
||||
nodep->v3warn(LITENDIAN, "Little endian cell range connecting to vector: MSB < LSB of cell range: "
|
||||
<<m_cellRangep->lsbConst()<<":"<<m_cellRangep->msbConst());
|
||||
nodep->exprp()->v3warn(
|
||||
LITENDIAN,
|
||||
"Little endian cell range connecting to vector: MSB < LSB of cell range: "
|
||||
<<m_cellRangep->lsbConst()<<":"<<m_cellRangep->msbConst());
|
||||
}
|
||||
AstNode* exprp = nodep->exprp()->unlinkFrBack();
|
||||
bool inputPin = nodep->modVarp()->isNonOutput();
|
||||
@@ -409,7 +411,7 @@ private:
|
||||
if (!varNewp) {
|
||||
if (debug()>=9) m_deModVars.dump();
|
||||
nodep->v3fatalSrc("Module dearray failed for "
|
||||
<<AstNode::prettyName(varNewName));
|
||||
<<AstNode::prettyNameQ(varNewName));
|
||||
}
|
||||
|
||||
// But clone the pin for each module instance
|
||||
|
||||
+8
-14
@@ -100,10 +100,9 @@ private:
|
||||
// (which at the V3Order stage represent verilog tasks, not to
|
||||
// the CFuncs that V3Order will generate.) So don't check for
|
||||
// collisions in CFuncs.
|
||||
if (nodep->user5p()) {
|
||||
nodep->v3fatalSrc("Node originally inserted below logic vertex "
|
||||
<<static_cast<AstNode*>(nodep->user5p()));
|
||||
}
|
||||
UASSERT_OBJ(!nodep->user5p(), nodep,
|
||||
"Node originally inserted below logic vertex "
|
||||
<<static_cast<AstNode*>(nodep->user5p()));
|
||||
nodep->user5p(const_cast<void*>(reinterpret_cast<const void*>(m_startNodep)));
|
||||
}
|
||||
|
||||
@@ -235,25 +234,20 @@ private:
|
||||
// are no actives-under-actives. In any case, check that we're at
|
||||
// root:
|
||||
markCost(nodep);
|
||||
if (nodep != m_startNodep) {
|
||||
nodep->v3fatalSrc("Multiple actives, or not start node");
|
||||
}
|
||||
UASSERT_OBJ(nodep == m_startNodep, nodep, "Multiple actives, or not start node");
|
||||
}
|
||||
virtual void visit(AstCCall* nodep) {
|
||||
VisitBase vb(this, nodep);
|
||||
iterateChildren(nodep);
|
||||
m_tracingCall = true;
|
||||
iterate(nodep->funcp());
|
||||
if (m_tracingCall) {
|
||||
nodep->v3fatalSrc("visit(AstCFunc) should have cleared m_tracingCall.");
|
||||
}
|
||||
UASSERT_OBJ(!m_tracingCall, nodep, "visit(AstCFunc) should have cleared m_tracingCall.");
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
// Don't count a CFunc other than by tracing a call or counting it
|
||||
// from the root
|
||||
if (!m_tracingCall && (nodep != m_startNodep)) {
|
||||
nodep->v3fatalSrc("AstCFunc not under AstCCall, or not start node");
|
||||
}
|
||||
UASSERT_OBJ(m_tracingCall || nodep == m_startNodep, nodep,
|
||||
"AstCFunc not under AstCCall, or not start node");
|
||||
m_tracingCall = false;
|
||||
bool saved_inCFunc = m_inCFunc;
|
||||
m_inCFunc = true;
|
||||
@@ -287,7 +281,7 @@ public:
|
||||
InstrCountDumpVisitor(AstNode* nodep, std::ostream* osp)
|
||||
: m_osp(osp), m_depth(0) {
|
||||
// No check for NULL output, so...
|
||||
if (!osp) nodep->v3fatalSrc("Don't call if not dumping");
|
||||
UASSERT_OBJ(osp, nodep, "Don't call if not dumping");
|
||||
if (nodep) iterate(nodep);
|
||||
}
|
||||
virtual ~InstrCountDumpVisitor() {}
|
||||
|
||||
+211
-149
@@ -30,160 +30,222 @@
|
||||
|
||||
class V3LanguageWords {
|
||||
// List of common reserved keywords
|
||||
|
||||
private:
|
||||
typedef std::map<string,string> KeywordMap;
|
||||
KeywordMap m_kwdMap; // List of keywords, and what language applies
|
||||
|
||||
void addKwd(const string& kwd, const string& why) {
|
||||
m_kwdMap.insert(make_pair(kwd, why));
|
||||
}
|
||||
struct Singleton {
|
||||
KeywordMap s_kwdMap; // List of keywords, and what language applies
|
||||
Singleton() { init(); }
|
||||
void addKwd(const string& kwd, const string& why) {
|
||||
s_kwdMap.insert(make_pair(kwd, why));
|
||||
}
|
||||
void init();
|
||||
};
|
||||
public:
|
||||
string isKeyword(const string& kwd) {
|
||||
KeywordMap::iterator it = m_kwdMap.find(kwd);
|
||||
if (it == m_kwdMap.end()) return "";
|
||||
typedef KeywordMap::const_iterator const_iterator;
|
||||
// METHODS
|
||||
static const_iterator begin() { return s().s_kwdMap.begin(); }
|
||||
static const_iterator end() { return s().s_kwdMap.end(); }
|
||||
static string isKeyword(const string& kwd) {
|
||||
KeywordMap::iterator it = s().s_kwdMap.find(kwd);
|
||||
if (it == s().s_kwdMap.end()) return "";
|
||||
return it->second;
|
||||
}
|
||||
|
||||
public:
|
||||
V3LanguageWords() {
|
||||
// C++ keywords
|
||||
// C++
|
||||
addKwd("NULL", "C++ common word");
|
||||
addKwd("abort", "C++ common word");
|
||||
addKwd("alignas", "C++11 keyword");
|
||||
addKwd("alignof", "C++11 keyword");
|
||||
addKwd("and", "C++11 keyword");
|
||||
addKwd("and_eq", "C++11 keyword");
|
||||
addKwd("asm", "C++ common word");
|
||||
addKwd("atomic_cancel", "C++ TM TS keyword");
|
||||
addKwd("atomic_commit", "C++ TM TS keyword");
|
||||
addKwd("atomic_noexcept", "C++ TM TS keyword");
|
||||
addKwd("auto", "C++ keyword");
|
||||
addKwd("bit_vector", "C++ common word");
|
||||
addKwd("bitand", "C++ keyword");
|
||||
addKwd("bitor", "C++ keyword");
|
||||
addKwd("bool", "C++ keyword");
|
||||
addKwd("break", "C++ keyword");
|
||||
addKwd("case", "C++ keyword");
|
||||
addKwd("catch", "C++ keyword");
|
||||
addKwd("cdecl", "C++ common word");
|
||||
addKwd("char", "C++ keyword");
|
||||
addKwd("char16_t", "C++11 keyword");
|
||||
addKwd("char32_t", "C++11 keyword");
|
||||
addKwd("class", "C++11 keyword");
|
||||
addKwd("compl", "C++11 keyword");
|
||||
addKwd("complex", "C++ common word");
|
||||
addKwd("concept", "C++20 keyword");
|
||||
addKwd("const", "C++ keyword");
|
||||
addKwd("const_cast", "C++ common word");
|
||||
addKwd("const_iterator", "C++ common word");
|
||||
addKwd("const_reference ", "C++ common word");
|
||||
addKwd("constexpr", "C++11 keyword");
|
||||
addKwd("continue", "C++ keyword");
|
||||
addKwd("decltype", "C++11 keyword");
|
||||
addKwd("default", "C++ keyword");
|
||||
addKwd("delete", "C++ keyword");
|
||||
addKwd("deque", "C++ common word");
|
||||
addKwd("do", "C++ keyword");
|
||||
addKwd("double", "C++ keyword");
|
||||
addKwd("dynamic_cast", "C++ keyword");
|
||||
addKwd("else", "C++ keyword");
|
||||
addKwd("enum", "C++ keyword");
|
||||
addKwd("explicit", "C++ keyword");
|
||||
addKwd("export", "C++ keyword");
|
||||
addKwd("extern", "C++ keyword");
|
||||
addKwd("false", "C++ keyword");
|
||||
addKwd("far", "C++ common word");
|
||||
addKwd("float", "C++ keyword");
|
||||
addKwd("for", "C++ keyword");
|
||||
addKwd("friend", "C++ keyword");
|
||||
addKwd("goto", "C++ keyword");
|
||||
addKwd("huge", "C++ keyword");
|
||||
addKwd("if", "C++ keyword");
|
||||
addKwd("import", "C++ modules TS keyword");
|
||||
addKwd("inline", "C++ keyword");
|
||||
addKwd("int", "C++ keyword");
|
||||
addKwd("interrupt", "C++ common word");
|
||||
addKwd("iterator", "C++ common word");
|
||||
addKwd("list", "C++ common word");
|
||||
addKwd("long", "C++ keyword");
|
||||
addKwd("map", "C++ common word");
|
||||
addKwd("module", "C++ modules TS keyword");
|
||||
addKwd("std::multimap", "C++ common word");
|
||||
addKwd("std::multiset", "C++ common word");
|
||||
addKwd("mutable", "C++ keyword");
|
||||
addKwd("namespace", "C++ keyword");
|
||||
addKwd("near", "C++ common word");
|
||||
addKwd("new", "C++ keyword");
|
||||
addKwd("noexcept", "C++11 keyword");
|
||||
addKwd("not", "C++ keyword");
|
||||
addKwd("not_eq", "C++ keyword");
|
||||
addKwd("nullptr", "C++11 keyword");
|
||||
addKwd("operator", "C++ keyword");
|
||||
addKwd("or", "C++ keyword");
|
||||
addKwd("or_eq", "C++ keyword");
|
||||
addKwd("override", "C++ common word");
|
||||
addKwd("pascal", "C++ keyword");
|
||||
addKwd("private", "C++ keyword");
|
||||
addKwd("protected", "C++ keyword");
|
||||
addKwd("public", "C++ keyword");
|
||||
addKwd("queue", "C++ common word");
|
||||
addKwd("reference", "C++ common word");
|
||||
addKwd("register", "C++ keyword");
|
||||
addKwd("reinterpret_cast ", "C++ keyword");
|
||||
addKwd("requires", "C++20 keyword");
|
||||
addKwd("restrict", "C++ keyword");
|
||||
addKwd("return", "C++ keyword");
|
||||
addKwd("set", "C++ common word");
|
||||
addKwd("short", "C++ keyword");
|
||||
addKwd("signed", "C++ keyword");
|
||||
addKwd("sizeof", "C++ keyword");
|
||||
addKwd("stack", "C++ common word");
|
||||
addKwd("static", "C++ keyword");
|
||||
addKwd("static_assert", "C++11 keyword");
|
||||
addKwd("static_cast", "C++ keyword");
|
||||
addKwd("struct", "C++ keyword");
|
||||
addKwd("switch", "C++ keyword");
|
||||
addKwd("synchronized", "C++ TM TS keyword");
|
||||
addKwd("template", "C++ keyword");
|
||||
addKwd("this", "C++ keyword");
|
||||
addKwd("thread_local", "C++11 keyword");
|
||||
addKwd("throw", "C++ keyword");
|
||||
addKwd("transaction_safe", "C++ common word");
|
||||
addKwd("transaction_safe_dynamic", "C++ common word");
|
||||
addKwd("true", "C++ keyword");
|
||||
addKwd("try", "C++ keyword");
|
||||
addKwd("type_info", "C++ common word");
|
||||
addKwd("typedef", "C++ keyword");
|
||||
addKwd("typeid", "C++ keyword");
|
||||
addKwd("typename", "C++ keyword");
|
||||
addKwd("uint16_t", "C++ common word");
|
||||
addKwd("uint32_t", "C++ common word");
|
||||
addKwd("uint8_t", "C++ common word");
|
||||
addKwd("union", "C++ keyword");
|
||||
addKwd("unsigned", "C++ keyword");
|
||||
addKwd("using", "C++ keyword");
|
||||
addKwd("vector", "C++ common word");
|
||||
addKwd("virtual", "C++ keyword");
|
||||
addKwd("void", "C++ keyword");
|
||||
addKwd("volatile", "C++ keyword");
|
||||
addKwd("wchar_t", "C++ keyword");
|
||||
addKwd("while", "C++ keyword");
|
||||
addKwd("xor", "C++ keyword");
|
||||
addKwd("xor_eq", "C++ keyword");
|
||||
// This conflicts with header functions, so is ignored
|
||||
//dKwd("final", "C++11 keyword");
|
||||
// SystemC
|
||||
addKwd("sc_clock", "SystemC common word");
|
||||
addKwd("sc_in", "SystemC common word");
|
||||
addKwd("sc_inout", "SystemC common word");
|
||||
addKwd("sc_out", "SystemC common word");
|
||||
addKwd("sc_signal", "SystemC common word");
|
||||
addKwd("sensitive", "SystemC common word");
|
||||
addKwd("sensitive_neg", "SystemC common word");
|
||||
addKwd("sensitive_pos", "SystemC common word");
|
||||
}
|
||||
private:
|
||||
static Singleton& s() { static Singleton s_s; return s_s; }
|
||||
};
|
||||
|
||||
inline void V3LanguageWords::Singleton::init() {
|
||||
// C++ keywords
|
||||
addKwd("NULL", "C++ common word");
|
||||
addKwd("abort", "C++ common word");
|
||||
addKwd("alignas", "C++11 keyword");
|
||||
addKwd("alignof", "C++11 keyword");
|
||||
addKwd("and", "C++11 keyword");
|
||||
addKwd("and_eq", "C++11 keyword");
|
||||
addKwd("asm", "C++ common word");
|
||||
addKwd("atomic_cancel", "C++ TM TS keyword");
|
||||
addKwd("atomic_commit", "C++ TM TS keyword");
|
||||
addKwd("atomic_noexcept", "C++ TM TS keyword");
|
||||
addKwd("auto", "C++ keyword");
|
||||
addKwd("bit_vector", "C++ common word");
|
||||
addKwd("bitand", "C++ keyword");
|
||||
addKwd("bitor", "C++ keyword");
|
||||
addKwd("bool", "C++ keyword");
|
||||
addKwd("break", "C++ keyword");
|
||||
addKwd("case", "C++ keyword");
|
||||
addKwd("catch", "C++ keyword");
|
||||
addKwd("cdecl", "C++ common word");
|
||||
addKwd("char", "C++ keyword");
|
||||
addKwd("char16_t", "C++11 keyword");
|
||||
addKwd("char32_t", "C++11 keyword");
|
||||
addKwd("class", "C++11 keyword");
|
||||
addKwd("compl", "C++11 keyword");
|
||||
addKwd("complex", "C++ common word");
|
||||
addKwd("concept", "C++20 keyword");
|
||||
addKwd("const", "C++ keyword");
|
||||
addKwd("const_cast", "C++ common word");
|
||||
addKwd("const_iterator", "C++ common word");
|
||||
addKwd("const_reference ", "C++ common word");
|
||||
addKwd("constexpr", "C++11 keyword");
|
||||
addKwd("continue", "C++ keyword");
|
||||
addKwd("decltype", "C++11 keyword");
|
||||
addKwd("default", "C++ keyword");
|
||||
addKwd("delete", "C++ keyword");
|
||||
addKwd("deque", "C++ common word");
|
||||
addKwd("do", "C++ keyword");
|
||||
addKwd("double", "C++ keyword");
|
||||
addKwd("dynamic_cast", "C++ keyword");
|
||||
addKwd("else", "C++ keyword");
|
||||
addKwd("enum", "C++ keyword");
|
||||
addKwd("explicit", "C++ keyword");
|
||||
addKwd("export", "C++ keyword");
|
||||
addKwd("extern", "C++ keyword");
|
||||
addKwd("false", "C++ keyword");
|
||||
addKwd("far", "C++ common word");
|
||||
addKwd("float", "C++ keyword");
|
||||
addKwd("for", "C++ keyword");
|
||||
addKwd("friend", "C++ keyword");
|
||||
addKwd("goto", "C++ keyword");
|
||||
addKwd("huge", "C++ keyword");
|
||||
addKwd("if", "C++ keyword");
|
||||
addKwd("import", "C++ modules TS keyword");
|
||||
addKwd("inline", "C++ keyword");
|
||||
addKwd("int", "C++ keyword");
|
||||
addKwd("interrupt", "C++ common word");
|
||||
addKwd("iterator", "C++ common word");
|
||||
addKwd("list", "C++ common word");
|
||||
addKwd("long", "C++ keyword");
|
||||
addKwd("map", "C++ common word");
|
||||
addKwd("module", "C++ modules TS keyword");
|
||||
addKwd("std::multimap", "C++ common word");
|
||||
addKwd("std::multiset", "C++ common word");
|
||||
addKwd("mutable", "C++ keyword");
|
||||
addKwd("namespace", "C++ keyword");
|
||||
addKwd("near", "C++ common word");
|
||||
addKwd("new", "C++ keyword");
|
||||
addKwd("noexcept", "C++11 keyword");
|
||||
addKwd("not", "C++ keyword");
|
||||
addKwd("not_eq", "C++ keyword");
|
||||
addKwd("nullptr", "C++11 keyword");
|
||||
addKwd("operator", "C++ keyword");
|
||||
addKwd("or", "C++ keyword");
|
||||
addKwd("or_eq", "C++ keyword");
|
||||
addKwd("override", "C++ common word");
|
||||
addKwd("pascal", "C++ keyword");
|
||||
addKwd("private", "C++ keyword");
|
||||
addKwd("protected", "C++ keyword");
|
||||
addKwd("public", "C++ keyword");
|
||||
addKwd("queue", "C++ common word");
|
||||
addKwd("reference", "C++ common word");
|
||||
addKwd("register", "C++ keyword");
|
||||
addKwd("reinterpret_cast ", "C++ keyword");
|
||||
addKwd("requires", "C++20 keyword");
|
||||
addKwd("restrict", "C++ keyword");
|
||||
addKwd("return", "C++ keyword");
|
||||
addKwd("set", "C++ common word");
|
||||
addKwd("short", "C++ keyword");
|
||||
addKwd("signed", "C++ keyword");
|
||||
addKwd("sizeof", "C++ keyword");
|
||||
addKwd("stack", "C++ common word");
|
||||
addKwd("static", "C++ keyword");
|
||||
addKwd("static_assert", "C++11 keyword");
|
||||
addKwd("static_cast", "C++ keyword");
|
||||
addKwd("struct", "C++ keyword");
|
||||
addKwd("switch", "C++ keyword");
|
||||
addKwd("synchronized", "C++ TM TS keyword");
|
||||
addKwd("template", "C++ keyword");
|
||||
addKwd("this", "C++ keyword");
|
||||
addKwd("thread_local", "C++11 keyword");
|
||||
addKwd("throw", "C++ keyword");
|
||||
addKwd("transaction_safe", "C++ common word");
|
||||
addKwd("transaction_safe_dynamic", "C++ common word");
|
||||
addKwd("true", "C++ keyword");
|
||||
addKwd("try", "C++ keyword");
|
||||
addKwd("type_info", "C++ common word");
|
||||
addKwd("typedef", "C++ keyword");
|
||||
addKwd("typeid", "C++ keyword");
|
||||
addKwd("typename", "C++ keyword");
|
||||
addKwd("uint16_t", "C++ common word");
|
||||
addKwd("uint32_t", "C++ common word");
|
||||
addKwd("uint8_t", "C++ common word");
|
||||
addKwd("union", "C++ keyword");
|
||||
addKwd("unsigned", "C++ keyword");
|
||||
addKwd("using", "C++ keyword");
|
||||
addKwd("vector", "C++ common word");
|
||||
addKwd("virtual", "C++ keyword");
|
||||
addKwd("void", "C++ keyword");
|
||||
addKwd("volatile", "C++ keyword");
|
||||
addKwd("wchar_t", "C++ keyword");
|
||||
addKwd("while", "C++ keyword");
|
||||
addKwd("xor", "C++ keyword");
|
||||
addKwd("xor_eq", "C++ keyword");
|
||||
// This conflicts with header functions, so is ignored
|
||||
//dKwd("final", "C++11 keyword");
|
||||
// SystemC
|
||||
addKwd("sc_clock", "SystemC common word");
|
||||
addKwd("sc_in", "SystemC common word");
|
||||
addKwd("sc_inout", "SystemC common word");
|
||||
addKwd("sc_out", "SystemC common word");
|
||||
addKwd("sc_signal", "SystemC common word");
|
||||
addKwd("sensitive", "SystemC common word");
|
||||
addKwd("sensitive_neg", "SystemC common word");
|
||||
addKwd("sensitive_pos", "SystemC common word");
|
||||
// Preprocessor defined words
|
||||
addKwd("`__FILE__", "Verilog preprocessor directive");
|
||||
addKwd("`__LINE__", "Verilog preprocessor directive");
|
||||
addKwd("`accelerate", "Verilog-XL preprocessor directive");
|
||||
addKwd("`autoexpand_vectornets", "Verilog-XL preprocessor directive");
|
||||
addKwd("`begin_keywords", "Verilog preprocessor directive");
|
||||
addKwd("`celldefine", "Verilog preprocessor directive");
|
||||
addKwd("`default_decay_time", "Verilog preprocessor directive");
|
||||
addKwd("`default_nettype", "Verilog preprocessor directive");
|
||||
addKwd("`default_trireg_strength", "Verilog preprocessor directive");
|
||||
addKwd("`define", "Verilog preprocessor directive");
|
||||
addKwd("`delay_mode_distributed", "Verilog preprocessor directive");
|
||||
addKwd("`delay_mode_path", "Verilog preprocessor directive");
|
||||
addKwd("`delay_mode_unit", "Verilog preprocessor directive");
|
||||
addKwd("`delay_mode_zero", "Verilog preprocessor directive");
|
||||
addKwd("`disable_portfaults", "Verilog-XL preprocessor directive");
|
||||
addKwd("`else", "Verilog preprocessor directive");
|
||||
addKwd("`elsif", "Verilog preprocessor directive");
|
||||
addKwd("`enable_portfaults", "Verilog-XL preprocessor directive");
|
||||
addKwd("`end_keywords", "Verilog preprocessor directive");
|
||||
addKwd("`endcelldefine", "Verilog preprocessor directive");
|
||||
addKwd("`endif", "Verilog preprocessor directive");
|
||||
addKwd("`endprotect", "Verilog preprocessor directive");
|
||||
addKwd("`endprotected", "Verilog preprocessor directive");
|
||||
addKwd("`error", "Verilog preprocessor directive");
|
||||
addKwd("`expand_vectornets", "Verilog-XL preprocessor directive");
|
||||
addKwd("`ifdef", "Verilog preprocessor directive");
|
||||
addKwd("`ifndef", "Verilog preprocessor directive");
|
||||
addKwd("`include", "Verilog preprocessor directive");
|
||||
addKwd("`inline", "Verilog preprocessor directive");
|
||||
addKwd("`line", "Verilog preprocessor directive");
|
||||
addKwd("`noaccelerate", "Verilog-XL preprocessor directive");
|
||||
addKwd("`noexpand_vectornets", "Verilog-XL preprocessor directive");
|
||||
addKwd("`noremove_gatenames", "Verilog-XL preprocessor directive");
|
||||
addKwd("`noremove_netnames", "Verilog-XL preprocessor directive");
|
||||
addKwd("`nosuppress_faults", "Verilog-XL preprocessor directive");
|
||||
addKwd("`nounconnected_drive", "Verilog-XL preprocessor directive");
|
||||
addKwd("`portcoerce", "Verilog preprocessor directive");
|
||||
addKwd("`pragma", "Verilog preprocessor directive");
|
||||
addKwd("`protect", "Verilog preprocessor directive");
|
||||
addKwd("`protected", "Verilog preprocessor directive");
|
||||
addKwd("`remove_gatenames", "Verilog-XL preprocessor directive");
|
||||
addKwd("`remove_netnames", "Verilog-XL preprocessor directive");
|
||||
addKwd("`resetall", "Verilog preprocessor directive");
|
||||
addKwd("`suppress_faults", "Verilog-XL preprocessor directive");
|
||||
addKwd("`systemc_ctor", "Verilator preprocessor directive");
|
||||
addKwd("`systemc_dtor", "Verilator preprocessor directive");
|
||||
addKwd("`systemc_header", "Verilator preprocessor directive");
|
||||
addKwd("`systemc_imp_header", "Verilator preprocessor directive");
|
||||
addKwd("`systemc_implementation", "Verilator preprocessor directive");
|
||||
addKwd("`systemc_interface", "Verilator preprocessor directive");
|
||||
addKwd("`timescale", "Verilog preprocessor directive");
|
||||
addKwd("`undef", "Verilog preprocessor directive");
|
||||
addKwd("`undefineall", "Verilog preprocessor directive");
|
||||
addKwd("`verilator_config", "Verilator preprocessor directive");
|
||||
addKwd("`verilog", "Verilator preprocessor directive");
|
||||
}
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+4
-3
@@ -299,11 +299,12 @@ private:
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
// Consumption/generation of a variable,
|
||||
// it's used so can't elim assignment before this use.
|
||||
if (!nodep->varScopep()) nodep->v3fatalSrc("NULL");
|
||||
UASSERT_OBJ(nodep->varScopep(), nodep, "NULL");
|
||||
//
|
||||
AstVarScope* vscp = nodep->varScopep();
|
||||
if (!vscp) nodep->v3fatalSrc("Scope not assigned");
|
||||
UASSERT_OBJ(vscp, nodep, "Scope not assigned");
|
||||
if (nodep->lvalue()) {
|
||||
m_sideEffect = true; // $sscanf etc may have RHS vars that are lvalues
|
||||
m_lifep->complexAssign(vscp);
|
||||
} else {
|
||||
m_lifep->varUsageReplace(vscp, nodep); VL_DANGLING(nodep);
|
||||
@@ -323,7 +324,7 @@ private:
|
||||
// Has to be direct assignment without any EXTRACTing.
|
||||
if (VN_IS(nodep->lhsp(), VarRef) && !m_sideEffect && !m_noopt) {
|
||||
AstVarScope* vscp = VN_CAST(nodep->lhsp(), VarRef)->varScopep();
|
||||
if (!vscp) nodep->v3fatalSrc("Scope lost on variable");
|
||||
UASSERT_OBJ(vscp, nodep, "Scope lost on variable");
|
||||
m_lifep->simpleAssign(vscp, nodep);
|
||||
} else {
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
|
||||
+8
-11
@@ -60,7 +60,7 @@ private:
|
||||
// VISITORS
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
AstVarScope* vscp = nodep->varScopep();
|
||||
if (!vscp) nodep->v3fatalSrc("Scope not assigned");
|
||||
UASSERT_OBJ(vscp, nodep, "Scope not assigned");
|
||||
if (AstVarScope* newvscp = reinterpret_cast<AstVarScope*>(vscp->user4p())) {
|
||||
UINFO(9, " Replace "<<nodep<<" to "<<newvscp<<endl);
|
||||
AstVarRef* newrefp = new AstVarRef(nodep->fileline(), newvscp, nodep->lvalue());
|
||||
@@ -267,14 +267,12 @@ private:
|
||||
iterateChildren(nodep);
|
||||
|
||||
if (v3Global.opt.mtasks()) {
|
||||
if (!m_mtasksGraphp) {
|
||||
nodep->v3fatalSrc("Should have initted m_mtasksGraphp by now");
|
||||
}
|
||||
UASSERT_OBJ(m_mtasksGraphp, nodep,
|
||||
"Should have initted m_mtasksGraphp by now");
|
||||
m_checker.reset(new GraphPathChecker(m_mtasksGraphp));
|
||||
} else {
|
||||
if (m_mtasksGraphp) {
|
||||
nodep->v3fatalSrc("Did not expect any m_mtasksGraphp in serial mode");
|
||||
}
|
||||
UASSERT_OBJ(!m_mtasksGraphp, nodep,
|
||||
"Did not expect any m_mtasksGraphp in serial mode");
|
||||
}
|
||||
|
||||
// Find all assignposts. Determine which ones can be
|
||||
@@ -289,7 +287,7 @@ private:
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
// Consumption/generation of a variable,
|
||||
AstVarScope* vscp = nodep->varScopep();
|
||||
if (!vscp) nodep->v3fatalSrc("Scope not assigned");
|
||||
UASSERT_OBJ(vscp, nodep, "Scope not assigned");
|
||||
|
||||
LifeLocation loc(m_execMTaskp, ++m_sequence);
|
||||
if (nodep->lvalue()) {
|
||||
@@ -311,9 +309,8 @@ private:
|
||||
if (AstVarRef* rhsp = VN_CAST(nodep->rhsp(), VarRef)) {
|
||||
// rhsp is the dly var
|
||||
AstVarScope* dlyVarp = rhsp->varScopep();
|
||||
if (m_assignposts.find(dlyVarp) != m_assignposts.end()) {
|
||||
nodep->v3fatalSrc("LifePostLocation attempted duplicate dlyvar map addition");
|
||||
}
|
||||
UASSERT_OBJ(m_assignposts.find(dlyVarp) == m_assignposts.end(), nodep,
|
||||
"LifePostLocation attempted duplicate dlyvar map addition");
|
||||
LifeLocation loc(m_execMTaskp, ++m_sequence);
|
||||
m_assignposts[dlyVarp] = LifePostLocation(loc, nodep);
|
||||
}
|
||||
|
||||
+15
-12
@@ -79,9 +79,9 @@ public:
|
||||
void LinkCellsGraph::loopsMessageCb(V3GraphVertex* vertexp) {
|
||||
if (LinkCellsVertex* vvertexp = dynamic_cast<LinkCellsVertex*>(vertexp)) {
|
||||
vvertexp->modp()->v3error("Unsupported: Recursive multiple modules (module instantiates something leading back to itself): "
|
||||
<<vvertexp->modp()->prettyName()<<endl
|
||||
<<vvertexp->modp()->prettyNameQ()<<endl
|
||||
<<V3Error::warnMore()
|
||||
<<"... Note self-recursion (module instantiating itself directly) is supported.");
|
||||
<<"... note: self-recursion (module instantiating itself directly) is supported.");
|
||||
V3Error::abortIfErrors();
|
||||
} else { // Everything should match above, but...
|
||||
v3fatalSrc("Recursive instantiations");
|
||||
@@ -141,7 +141,8 @@ private:
|
||||
// We'll throw the error when we know the module will really be needed.
|
||||
string prettyName = AstNode::prettyName(modName);
|
||||
V3Parse parser (v3Global.rootp(), m_filterp, m_parseSymp);
|
||||
parser.parseFile(nodep->fileline(), prettyName, false, "");
|
||||
// true below -> other simulators treat modules in link-found files as library cells
|
||||
parser.parseFile(nodep->fileline(), prettyName, true, "");
|
||||
V3Error::abortIfErrors();
|
||||
// We've read new modules, grab new pointers to their names
|
||||
readModNames();
|
||||
@@ -150,7 +151,7 @@ private:
|
||||
if (!modp) {
|
||||
// This shouldn't throw a message as parseFile will create
|
||||
// a AstNotFoundModule for us
|
||||
nodep->v3error("Can't resolve module reference: "<<prettyName);
|
||||
nodep->v3error("Can't resolve module reference: '"<<prettyName<<"'");
|
||||
}
|
||||
}
|
||||
return modp;
|
||||
@@ -203,7 +204,7 @@ private:
|
||||
m_declfnWarned.insert(nodep->fileline()->filename());
|
||||
nodep->v3warn(DECLFILENAME, "Filename '"<<nodep->fileline()->filebasenameNoExt()
|
||||
<<"' does not match "<<nodep->typeName()
|
||||
<<" name: "<<nodep->prettyName());
|
||||
<<" name: "<<nodep->prettyNameQ());
|
||||
}
|
||||
}
|
||||
if (VN_IS(nodep, Iface) || VN_IS(nodep, Package)) nodep->inLibrary(true); // Interfaces can't be at top, unless asked
|
||||
@@ -240,7 +241,7 @@ private:
|
||||
if (!nodep->cellp()) nodep->ifacep(VN_CAST(modp, Iface));
|
||||
} else if (VN_IS(modp, NotFoundModule)) { // Will error out later
|
||||
} else {
|
||||
nodep->v3error("Non-interface used as an interface: "<<nodep->prettyName());
|
||||
nodep->v3error("Non-interface used as an interface: "<<nodep->prettyNameQ());
|
||||
}
|
||||
}
|
||||
// Note cannot do modport resolution here; modports are allowed underneath generates
|
||||
@@ -249,7 +250,7 @@ private:
|
||||
virtual void visit(AstPackageImport* nodep) {
|
||||
// Package Import: We need to do the package before the use of a package
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->packagep()) nodep->v3fatalSrc("Unlinked package"); // Parser should set packagep
|
||||
UASSERT_OBJ(nodep->packagep(), nodep, "Unlinked package"); // Parser should set packagep
|
||||
new V3GraphEdge(&m_graph, vertex(m_modp), vertex(nodep->packagep()), 1, false);
|
||||
}
|
||||
|
||||
@@ -373,11 +374,11 @@ private:
|
||||
if (!pinp->exprp()) {
|
||||
if (pinp->name().substr(0, 11) == "__pinNumber") {
|
||||
pinp->v3warn(PINNOCONNECT, "Cell pin is not connected: "
|
||||
<<pinp->prettyName());
|
||||
<<pinp->prettyNameQ());
|
||||
} else {
|
||||
pinp->v3warn(PINCONNECTEMPTY,
|
||||
"Cell pin connected by name with empty reference: "
|
||||
<<pinp->prettyName());
|
||||
<<pinp->prettyNameQ());
|
||||
}
|
||||
}
|
||||
if (ports.find(pinp->name()) == ports.end()) {
|
||||
@@ -402,7 +403,7 @@ private:
|
||||
nodep->addPinsp(newp);
|
||||
} else { // warn on the CELL that needs it, not the port
|
||||
nodep->v3warn(PINMISSING, "Cell has missing pin: "
|
||||
<<portp->prettyName());
|
||||
<<portp->prettyNameQ());
|
||||
AstPin* newp = new AstPin(nodep->fileline(), 0, portp->name(), NULL);
|
||||
nodep->addPinsp(newp);
|
||||
}
|
||||
@@ -463,8 +464,10 @@ private:
|
||||
if (!(foundp->fileline()->warnIsOff(V3ErrorCode::MODDUP)
|
||||
|| nodep->fileline()->warnIsOff(V3ErrorCode::MODDUP))) {
|
||||
nodep->v3warn(MODDUP, "Duplicate declaration of module: "
|
||||
<<nodep->prettyName()<<endl
|
||||
<<foundp->warnMore()<<"... Location of original declaration");
|
||||
<<nodep->prettyNameQ()<<endl
|
||||
<<nodep->warnContextPrimary()<<endl
|
||||
<<foundp->warnOther()<<"... Location of original declaration"<<endl
|
||||
<<foundp->warnContextSecondary());
|
||||
}
|
||||
nodep->unlinkFrBack();
|
||||
pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
|
||||
+272
-154
@@ -79,6 +79,50 @@
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
//######################################################################
|
||||
// Matcher classes (for suggestion matching)
|
||||
|
||||
class LinkNodeMatcherFTask : public VNodeMatcher {
|
||||
public:
|
||||
virtual bool nodeMatch(const AstNode* nodep) const {
|
||||
return VN_IS(nodep, NodeFTask);
|
||||
}
|
||||
};
|
||||
class LinkNodeMatcherModport : public VNodeMatcher {
|
||||
public:
|
||||
virtual bool nodeMatch(const AstNode* nodep) const {
|
||||
return VN_IS(nodep, Modport);
|
||||
}
|
||||
};
|
||||
class LinkNodeMatcherVar : public VNodeMatcher {
|
||||
public:
|
||||
virtual bool nodeMatch(const AstNode* nodep) const {
|
||||
return VN_IS(nodep, Var);
|
||||
}
|
||||
};
|
||||
class LinkNodeMatcherVarIO : public VNodeMatcher {
|
||||
public:
|
||||
virtual bool nodeMatch(const AstNode* nodep) const {
|
||||
const AstVar* varp = VN_CAST_CONST(nodep, Var);
|
||||
if (!varp) return false;
|
||||
return varp->isIO();
|
||||
}
|
||||
};
|
||||
class LinkNodeMatcherVarParam : public VNodeMatcher {
|
||||
public:
|
||||
virtual bool nodeMatch(const AstNode* nodep) const {
|
||||
const AstVar* varp = VN_CAST_CONST(nodep, Var);
|
||||
if (!varp) return false;
|
||||
return varp->isParam();
|
||||
}
|
||||
};
|
||||
class LinkNodeMatcherVarOrScope : public VNodeMatcher {
|
||||
public:
|
||||
virtual bool nodeMatch(const AstNode* nodep) const {
|
||||
return VN_IS(nodep, Var) || VN_IS(nodep, Scope);
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// LinkDot state, as a visitor of each AstNode
|
||||
|
||||
@@ -226,13 +270,17 @@ public:
|
||||
UINFO(4,"Var2 "<<fnodep<<endl);
|
||||
if (nodep->type() == fnodep->type()) {
|
||||
nodep->v3error("Duplicate declaration of "<<nodeTextType(fnodep)
|
||||
<<": "<<nodep->prettyName()<<endl
|
||||
<<fnodep->warnMore()<<"... Location of original declaration");
|
||||
<<": "<<nodep->prettyNameQ()<<endl
|
||||
<<nodep->warnContextPrimary()<<endl
|
||||
<<fnodep->warnOther()<<"... Location of original declaration\n"
|
||||
<<fnodep->warnContextSecondary());
|
||||
} else {
|
||||
nodep->v3error("Unsupported in C: "<<ucfirst(nodeTextType(nodep))
|
||||
<<" has the same name as "
|
||||
<<nodeTextType(fnodep)<<": "<<nodep->prettyName()<<endl
|
||||
<<fnodep->warnMore()<<"... Location of original declaration");
|
||||
<<nodeTextType(fnodep)<<": "<<nodep->prettyNameQ()<<endl
|
||||
<<nodep->warnContextPrimary()<<endl
|
||||
<<fnodep->warnOther()<<"... Location of original declaration\n"
|
||||
<<fnodep->warnContextSecondary());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -244,7 +292,7 @@ public:
|
||||
symp->fallbackp(NULL);
|
||||
rootEntp()->insert("$unit ", symp); // Space so can never name conflict with user code
|
||||
//
|
||||
if (m_dunitEntp) nodep->v3fatalSrc("Call insertDUnit only once");
|
||||
UASSERT_OBJ(!m_dunitEntp, nodep, "Call insertDUnit only once");
|
||||
m_dunitEntp = symp;
|
||||
}
|
||||
VSymEnt* insertTopCell(AstNodeModule* nodep, const string& scopename) {
|
||||
@@ -261,7 +309,7 @@ public:
|
||||
}
|
||||
VSymEnt* insertCell(VSymEnt* abovep, VSymEnt* modSymp,
|
||||
AstCell* nodep, const string& scopename) {
|
||||
if (!abovep) nodep->v3fatalSrc("Null symbol table inserting node");
|
||||
UASSERT_OBJ(abovep, nodep, "Null symbol table inserting node");
|
||||
VSymEnt* symp = new VSymEnt(&m_syms, nodep);
|
||||
UINFO(9," INSERTcel se"<<cvtToHex(symp)<<" "<<scopename
|
||||
<<" above=se"<<cvtToHex(abovep)
|
||||
@@ -285,7 +333,7 @@ public:
|
||||
AstCellInline* nodep, const string& basename) {
|
||||
// A fake point in the hierarchy, corresponding to an inlined module
|
||||
// This refrences to another Sym, and eventually resolves to a module with a prefix
|
||||
if (!abovep) nodep->v3fatalSrc("Null symbol table inserting node");
|
||||
UASSERT_OBJ(abovep, nodep, "Null symbol table inserting node");
|
||||
VSymEnt* symp = new VSymEnt(&m_syms, nodep);
|
||||
UINFO(9," INSERTinl se"<<cvtToHex(symp)
|
||||
<<" "<<basename<<" above=se"<<cvtToHex(abovep)
|
||||
@@ -308,7 +356,7 @@ public:
|
||||
// After we remove begins these will go away
|
||||
// Note we fallback to the symbol table of the parent, as we want to find variables there
|
||||
// However, cells walk the graph, so cells will appear under the begin/ftask itself
|
||||
if (!abovep) nodep->v3fatalSrc("Null symbol table inserting node");
|
||||
UASSERT_OBJ(abovep, nodep, "Null symbol table inserting node");
|
||||
VSymEnt* symp = new VSymEnt(&m_syms, nodep);
|
||||
UINFO(9," INSERTblk se"<<cvtToHex(symp)
|
||||
<<" above=se"<<cvtToHex(abovep)<<" node="<<nodep<<endl);
|
||||
@@ -325,7 +373,7 @@ public:
|
||||
}
|
||||
VSymEnt* insertSym(VSymEnt* abovep, const string& name,
|
||||
AstNode* nodep, AstPackage* packagep) {
|
||||
if (!abovep) nodep->v3fatalSrc("Null symbol table inserting node");
|
||||
UASSERT_OBJ(abovep, nodep, "Null symbol table inserting node");
|
||||
VSymEnt* symp = new VSymEnt(&m_syms, nodep);
|
||||
UINFO(9," INSERTsym se"<<cvtToHex(symp)<<" name='"<<name
|
||||
<<"' above=se"<<cvtToHex(abovep)<<" node="<<nodep<<endl);
|
||||
@@ -346,14 +394,13 @@ public:
|
||||
// Don't use this in ResolveVisitor, as we need to pick up the proper
|
||||
// reference under each SCOPE
|
||||
VSymEnt* symp = nodep->user1u().toSymEnt();
|
||||
if (!symp) nodep->v3fatalSrc("Module/etc never assigned a symbol entry?");
|
||||
UASSERT_OBJ(symp, nodep, "Module/etc never assigned a symbol entry?");
|
||||
return symp;
|
||||
}
|
||||
VSymEnt* getScopeSym(AstScope* nodep) {
|
||||
NameScopeSymMap::iterator it = m_nameScopeSymMap.find(nodep->name());
|
||||
if (it == m_nameScopeSymMap.end()) {
|
||||
nodep->v3fatalSrc("Scope never assigned a symbol entry?");
|
||||
}
|
||||
UASSERT_OBJ(it != m_nameScopeSymMap.end(), nodep,
|
||||
"Scope never assigned a symbol entry?");
|
||||
return it->second;
|
||||
}
|
||||
void implicitOkAdd(AstNodeModule* nodep, const string& varname) {
|
||||
@@ -397,18 +444,18 @@ public:
|
||||
AstVar* varp = varSymp ? VN_CAST(varSymp->nodep(), Var) : NULL;
|
||||
UINFO(9, " insAllIface se"<<cvtToHex(varSymp)<<" "<<varp<<endl);
|
||||
AstIfaceRefDType* ifacerefp = ifaceRefFromArray(varp->subDTypep());
|
||||
if (!ifacerefp) varp->v3fatalSrc("Non-ifacerefs on list!");
|
||||
UASSERT_OBJ(ifacerefp, varp, "Non-ifacerefs on list!");
|
||||
if (!ifacerefp->ifaceViaCellp()) {
|
||||
if (!ifacerefp->cellp()) { // Probably a NotFoundModule, or a normal module if made mistake
|
||||
ifacerefp->v3error("Cannot find file containing interface: "
|
||||
<<AstNode::prettyName(ifacerefp->ifaceName()));
|
||||
<<AstNode::prettyNameQ(ifacerefp->ifaceName()));
|
||||
continue;
|
||||
} else {
|
||||
ifacerefp->v3fatalSrc("Unlinked interface");
|
||||
}
|
||||
} else if (ifacerefp->ifaceViaCellp()->dead()) {
|
||||
ifacerefp->v3error("Parent cell's interface is not found: "
|
||||
<<AstNode::prettyName(ifacerefp->ifaceName()));
|
||||
<<AstNode::prettyNameQ(ifacerefp->ifaceName()));
|
||||
continue;
|
||||
}
|
||||
VSymEnt* ifaceSymp = getNodeSym(ifacerefp->ifaceViaCellp());
|
||||
@@ -425,9 +472,15 @@ public:
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
if (!ok) ifacerefp->v3error("Modport not found under interface '"
|
||||
<<ifacerefp->prettyName(ifacerefp->ifaceName())
|
||||
<<"': "<<ifacerefp->prettyName(ifacerefp->modportName()));
|
||||
if (!ok) {
|
||||
string suggest = suggestSymFallback(
|
||||
ifaceSymp, ifacerefp->modportName(), LinkNodeMatcherModport());
|
||||
ifacerefp->modportFileline()
|
||||
->v3error("Modport not found under interface "
|
||||
<<ifacerefp->prettyNameQ(ifacerefp->ifaceName())<<": "
|
||||
<<ifacerefp->prettyNameQ(ifacerefp->modportName())<<endl
|
||||
<<(suggest.empty() ? "" : ifacerefp->warnMore()+suggest));
|
||||
}
|
||||
}
|
||||
// Alias won't expand until interfaces and modport names are known; see notes at top
|
||||
insertScopeAlias(SAMN_IFTOP, varSymp, ifOrPortSymp);
|
||||
@@ -440,10 +493,9 @@ public:
|
||||
// a child cell connecting to that interface
|
||||
// Typically lhsp=VAR w/dtype IFACEREF, rhsp=IFACE cell
|
||||
UINFO(9," insertScopeAlias se"<<cvtToHex(lhsp)<<" se"<<cvtToHex(rhsp)<<endl);
|
||||
if (VN_IS(rhsp->nodep(), Cell)
|
||||
&& !VN_IS(VN_CAST(rhsp->nodep(), Cell)->modp(), Iface)) {
|
||||
rhsp->nodep()->v3fatalSrc("Got a non-IFACE alias RHS");
|
||||
}
|
||||
UASSERT_OBJ(!(VN_IS(rhsp->nodep(), Cell)
|
||||
&& !VN_IS(VN_CAST(rhsp->nodep(), Cell)->modp(), Iface)),
|
||||
rhsp->nodep(), "Got a non-IFACE alias RHS");
|
||||
m_scopeAliasMap[samn].insert(make_pair(lhsp, rhsp));
|
||||
}
|
||||
void computeScopeAliases() {
|
||||
@@ -599,6 +651,23 @@ public:
|
||||
if (!foundp) baddot = dotname;
|
||||
return foundp;
|
||||
}
|
||||
string suggestSymFallback(VSymEnt* lookupSymp, const string& name,
|
||||
const VNodeMatcher& matcher) {
|
||||
// Suggest alternative symbol in given point in hierarchy
|
||||
// Does not support inline, as we find user-level errors before inlining
|
||||
// For simplicity lookupSymp may be passed NULL result from findDotted
|
||||
if (!lookupSymp) return "";
|
||||
VSpellCheck speller;
|
||||
lookupSymp->candidateIdFallback(&speller, &matcher);
|
||||
return speller.bestCandidateMsg(name);
|
||||
}
|
||||
string suggestSymFlat(VSymEnt* lookupSymp, const string& name,
|
||||
const VNodeMatcher& matcher) {
|
||||
if (!lookupSymp) return "";
|
||||
VSpellCheck speller;
|
||||
lookupSymp->candidateIdFlat(&speller, &matcher);
|
||||
return speller.bestCandidateMsg(name);
|
||||
}
|
||||
};
|
||||
|
||||
LinkDotState* LinkDotState::s_errorThisp = NULL;
|
||||
@@ -670,18 +739,20 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
// packages before using packages
|
||||
iterateChildrenBackwards(nodep);
|
||||
|
||||
// The first module in the list is always the top module (sorted before this is called).
|
||||
// The first modules in the list are always the top modules
|
||||
// (sorted before this is called).
|
||||
// This may not be the module with isTop() set, as early in the steps,
|
||||
// wrapTop may have not been created yet.
|
||||
AstNodeModule* topmodp = nodep->modulesp();
|
||||
if (!topmodp) {
|
||||
if (!nodep->modulesp()) {
|
||||
nodep->v3error("No top level module found");
|
||||
} else {
|
||||
UINFO(8,"Top Module: "<<topmodp<<endl);
|
||||
}
|
||||
for (AstNodeModule* modp = nodep->modulesp(); modp && modp->level() <= 2;
|
||||
modp = VN_CAST(modp->nextp(), NodeModule)) {
|
||||
UINFO(8,"Top Module: "<<modp<<endl);
|
||||
m_scope = "TOP";
|
||||
m_curSymp = m_modSymp = m_statep->insertTopCell(topmodp, m_scope);
|
||||
m_curSymp = m_modSymp = m_statep->insertTopCell(modp, m_scope);
|
||||
{
|
||||
iterate(topmodp);
|
||||
iterate(modp);
|
||||
}
|
||||
m_scope = "";
|
||||
m_curSymp = m_modSymp = NULL;
|
||||
@@ -705,10 +776,10 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
int oldModBeginNum = m_modBeginNum;
|
||||
if (doit && nodep->user2()) {
|
||||
nodep->v3error("Unsupported: Identically recursive module (module instantiates itself, without changing parameters): "
|
||||
<<AstNode::prettyName(nodep->origName()));
|
||||
<<AstNode::prettyNameQ(nodep->origName()));
|
||||
} else if (doit) {
|
||||
UINFO(4," Link Module: "<<nodep<<endl);
|
||||
if (nodep->dead()) nodep->v3fatalSrc("Module in cell tree mislabeled as dead?");
|
||||
UASSERT_OBJ(!nodep->dead(), nodep, "Module in cell tree mislabeled as dead?");
|
||||
VSymEnt* upperSymp = m_curSymp ? m_curSymp : m_statep->rootEntp();
|
||||
m_packagep = VN_CAST(nodep, Package);
|
||||
if (standalonePkg) {
|
||||
@@ -751,7 +822,8 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
m_packagep = NULL;
|
||||
}
|
||||
virtual void visit(AstScope* nodep) {
|
||||
if (!m_statep->forScopeCreation()) v3fatalSrc("Scopes should only exist right after V3Scope");
|
||||
UASSERT_OBJ(m_statep->forScopeCreation(), nodep,
|
||||
"Scopes should only exist right after V3Scope");
|
||||
// Ignored. Processed in next step
|
||||
}
|
||||
virtual void visit(AstCell* nodep) {
|
||||
@@ -776,10 +848,9 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
string baddot;
|
||||
VSymEnt* okSymp;
|
||||
aboveSymp = m_statep->findDotted(aboveSymp, scope, baddot, okSymp);
|
||||
if (!aboveSymp) {
|
||||
nodep->v3fatalSrc("Can't find cell insertion point at '"
|
||||
<<baddot<<"' in: "<<nodep->prettyName());
|
||||
}
|
||||
UASSERT_OBJ(aboveSymp, nodep,
|
||||
"Can't find cell insertion point at '"
|
||||
<<baddot<<"' in: "<<nodep->prettyNameQ());
|
||||
}
|
||||
{
|
||||
m_scope = m_scope+"."+nodep->name();
|
||||
@@ -808,10 +879,9 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
string baddot;
|
||||
VSymEnt* okSymp;
|
||||
aboveSymp = m_statep->findDotted(aboveSymp, dotted, baddot, okSymp);
|
||||
if (!aboveSymp) {
|
||||
nodep->v3fatalSrc("Can't find cellinline insertion point at '"
|
||||
<<baddot<<"' in: "<<nodep->prettyName());
|
||||
}
|
||||
UASSERT_OBJ(aboveSymp, nodep,
|
||||
"Can't find cellinline insertion point at '"
|
||||
<<baddot<<"' in: "<<nodep->prettyNameQ());
|
||||
m_statep->insertInline(aboveSymp, m_modSymp, nodep, ident);
|
||||
} else { // No __DOT__, just directly underneath
|
||||
m_statep->insertInline(aboveSymp, m_modSymp, nodep, nodep->name());
|
||||
@@ -876,7 +946,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
virtual void visit(AstNodeFTask* nodep) {
|
||||
// NodeTask: Remember its name for later resolution
|
||||
UINFO(5," "<<nodep<<endl);
|
||||
if (!m_curSymp || !m_modSymp) nodep->v3fatalSrc("Function/Task not under module?");
|
||||
UASSERT_OBJ(m_curSymp && m_modSymp, nodep, "Function/Task not under module?");
|
||||
// Remember the existing symbol table scope
|
||||
VSymEnt* oldCurSymp = m_curSymp;
|
||||
{
|
||||
@@ -911,7 +981,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
}
|
||||
virtual void visit(AstVar* nodep) {
|
||||
// Var: Remember its name for later resolution
|
||||
if (!m_curSymp || !m_modSymp) nodep->v3fatalSrc("Var not under module?");
|
||||
UASSERT_OBJ(m_curSymp && m_modSymp, nodep, "Var not under module?");
|
||||
iterateChildren(nodep);
|
||||
if (!m_statep->forScopeCreation()) {
|
||||
// Find under either a task or the module's vars
|
||||
@@ -926,17 +996,32 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
} else if (!findvarp && foundp && m_curSymp->findIdFlat(nodep->name())) {
|
||||
nodep->v3error("Unsupported in C: Variable has same name as "
|
||||
<<LinkDotState::nodeTextType(foundp->nodep())
|
||||
<<": "<<nodep->prettyName());
|
||||
<<": "<<nodep->prettyNameQ());
|
||||
} else if (findvarp != nodep) {
|
||||
UINFO(4,"DupVar: "<<nodep<<" ;; "<<foundp->nodep()<<endl);
|
||||
UINFO(4," found cur=se"<<cvtToHex(m_curSymp)
|
||||
<<" ;; parent=se"<<cvtToHex(foundp->parentp())<<endl);
|
||||
if (foundp && foundp->parentp() == m_curSymp // Only when on same level
|
||||
&& !foundp->imported()) { // and not from package
|
||||
if (!(findvarp->isIO() && nodep->isIO()) // e.g. !(output && output)
|
||||
&& ((findvarp->isIO() && nodep->isSignal()) // e.g. output && reg
|
||||
|| (findvarp->isSignal() && nodep->isIO())) // e.g. reg && output
|
||||
&& !(findvarp->isSignal() && !nodep->isSignal())) { // e.g. !(reg && reg)
|
||||
bool nansiBad = ((findvarp->isDeclTyped() && nodep->isDeclTyped())
|
||||
|| (findvarp->isIO() && nodep->isIO())); // e.g. !(output && output)
|
||||
bool ansiBad = findvarp->isAnsi() || nodep->isAnsi(); // dup illegal with ANSI
|
||||
if (ansiBad || nansiBad) {
|
||||
static int didAnsiWarn = false;
|
||||
bool ansiWarn = ansiBad && !nansiBad;
|
||||
if (ansiWarn) { if (didAnsiWarn++) ansiWarn = false; }
|
||||
nodep->v3error("Duplicate declaration of signal: "
|
||||
<<nodep->prettyNameQ()<<endl
|
||||
<<(ansiWarn
|
||||
? nodep->warnMore()+"... note: ANSI ports must have type declared with the I/O (IEEE 2017 23.2.2.2)\n"
|
||||
: "")
|
||||
<<nodep->warnContextPrimary()<<endl
|
||||
<<findvarp->warnOther()<<"... Location of original declaration"<<endl
|
||||
<<findvarp->warnContextSecondary());
|
||||
// Combining most likely reduce other errors
|
||||
findvarp->combineType(nodep);
|
||||
findvarp->fileline()->modifyStateInherit(nodep->fileline());
|
||||
} else {
|
||||
findvarp->combineType(nodep);
|
||||
findvarp->fileline()->modifyStateInherit(nodep->fileline());
|
||||
AstBasicDType* bdtypep = VN_CAST(findvarp->childDTypep(), BasicDType);
|
||||
@@ -944,18 +1029,12 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
// Then have "input foo" and "real foo" so the
|
||||
// dtype comes from the other side.
|
||||
AstNodeDType* newdtypep = nodep->subDTypep();
|
||||
if (!newdtypep || !nodep->childDTypep()) findvarp->v3fatalSrc("No child type?");
|
||||
UASSERT_OBJ(newdtypep && nodep->childDTypep(), findvarp,
|
||||
"No child type?");
|
||||
bdtypep->unlinkFrBack()->deleteTree();
|
||||
newdtypep->unlinkFrBack();
|
||||
findvarp->childDTypep(newdtypep);
|
||||
}
|
||||
} else {
|
||||
nodep->v3error("Duplicate declaration of signal: "
|
||||
<<nodep->prettyName()<<endl
|
||||
<<findvarp->warnMore()<<"... Location of original declaration");
|
||||
// Combining most likely reduce other errors
|
||||
findvarp->combineType(nodep);
|
||||
findvarp->fileline()->modifyStateInherit(nodep->fileline());
|
||||
}
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
} else {
|
||||
@@ -965,9 +1044,11 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
&& !nodep->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN)
|
||||
&& !foundp->nodep()->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN)) {
|
||||
nodep->v3warn(VARHIDDEN, "Declaration of signal hides declaration in upper scope: "
|
||||
<<nodep->prettyName()<<endl
|
||||
<<foundp->nodep()->warnMore()
|
||||
<<"... Location of original declaration");
|
||||
<<nodep->prettyNameQ()<<endl
|
||||
<<nodep->warnContextPrimary()<<endl
|
||||
<<foundp->nodep()->warnOther()
|
||||
<<"... Location of original declaration\n"
|
||||
<<foundp->nodep()->warnContextSecondary());
|
||||
}
|
||||
ins = true;
|
||||
}
|
||||
@@ -1011,18 +1092,18 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
}
|
||||
virtual void visit(AstTypedef* nodep) {
|
||||
// Remember its name for later resolution
|
||||
if (!m_curSymp) nodep->v3fatalSrc("Typedef not under module?");
|
||||
UASSERT_OBJ(m_curSymp, nodep, "Typedef not under module?");
|
||||
iterateChildren(nodep);
|
||||
m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep);
|
||||
}
|
||||
virtual void visit(AstParamTypeDType* nodep) {
|
||||
if (!m_curSymp) nodep->v3fatalSrc("Parameter type not under module?");
|
||||
UASSERT_OBJ(m_curSymp, nodep, "Parameter type not under module?");
|
||||
iterateChildren(nodep);
|
||||
m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep);
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
// For dotted resolution, ignore all AstVars under functions, otherwise shouldn't exist
|
||||
if (m_statep->forScopeCreation()) nodep->v3fatalSrc("No CFuncs expected in tree yet");
|
||||
UASSERT_OBJ(!m_statep->forScopeCreation(), nodep, "No CFuncs expected in tree yet");
|
||||
}
|
||||
virtual void visit(AstEnumItem* nodep) {
|
||||
// EnumItem: Remember its name for later resolution
|
||||
@@ -1041,15 +1122,19 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
if (foundp && foundp->parentp() == m_curSymp // Only when on same level
|
||||
&& !foundp->imported()) { // and not from package
|
||||
nodep->v3error("Duplicate declaration of enum value: "<<nodep->prettyName()<<endl
|
||||
<<findvarp->warnMore()<<"... Location of original declaration");
|
||||
<<nodep->warnContextPrimary()<<endl
|
||||
<<findvarp->warnOther()<<"... Location of original declaration\n"
|
||||
<<findvarp->warnContextSecondary());
|
||||
} else {
|
||||
// User can disable the message at either point
|
||||
if (!nodep->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN)
|
||||
&& !foundp->nodep()->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN)) {
|
||||
nodep->v3warn(VARHIDDEN, "Declaration of enum value hides declaration in upper scope: "
|
||||
<<nodep->prettyName()<<endl
|
||||
<<foundp->nodep()->warnMore()
|
||||
<<"... Location of original declaration");
|
||||
<<nodep->warnContextPrimary()<<endl
|
||||
<<foundp->nodep()->warnOther()
|
||||
<<"... Location of original declaration\n"
|
||||
<<nodep->warnContextSecondary());
|
||||
}
|
||||
ins = true;
|
||||
}
|
||||
@@ -1068,8 +1153,8 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
} else {
|
||||
VSymEnt* impp = srcp->findIdFlat(nodep->name());
|
||||
if (!impp) {
|
||||
nodep->v3error("Import object not found: "
|
||||
<<nodep->packagep()->prettyName()<<"::"<<nodep->prettyName());
|
||||
nodep->v3error("Import object not found: '"
|
||||
<<nodep->packagep()->prettyName()<<"::"<<nodep->prettyName()<<"'");
|
||||
}
|
||||
}
|
||||
m_curSymp->importFromPackage(m_statep->symsp(), srcp, nodep->name());
|
||||
@@ -1082,8 +1167,8 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
if (nodep->name()!="*") {
|
||||
VSymEnt* impp = srcp->findIdFlat(nodep->name());
|
||||
if (!impp) {
|
||||
nodep->v3error("Export object not found: "
|
||||
<<nodep->packagep()->prettyName()<<"::"<<nodep->prettyName());
|
||||
nodep->v3error("Export object not found: '"
|
||||
<<nodep->packagep()->prettyName()<<"::"<<nodep->prettyName()<<"'");
|
||||
}
|
||||
}
|
||||
m_curSymp->exportFromPackage(m_statep->symsp(), srcp, nodep->name());
|
||||
@@ -1166,7 +1251,8 @@ private:
|
||||
UINFO(5," "<<nodep<<endl);
|
||||
if (nodep->dead() || !nodep->user4()) {
|
||||
UINFO(4,"Mark dead module "<<nodep<<endl);
|
||||
if (!m_statep->forPrearray()) nodep->v3fatalSrc("Dead module persisted past where should have removed");
|
||||
UASSERT_OBJ(m_statep->forPrearray(), nodep,
|
||||
"Dead module persisted past where should have removed");
|
||||
// Don't remove now, because we may have a tree of
|
||||
// parameterized modules with VARXREFs into the deleted module
|
||||
// region. V3Dead should cleanup.
|
||||
@@ -1189,7 +1275,7 @@ private:
|
||||
}
|
||||
virtual void visit(AstDefParam* nodep) {
|
||||
iterateChildren(nodep);
|
||||
nodep->v3warn(DEFPARAM, "Suggest replace defparam with Verilog 2001 #(."
|
||||
nodep->v3warn(DEFPARAM, "Suggest replace defparam assignment with Verilog 2001 #(."
|
||||
<<nodep->prettyName()<<"(...etc...))");
|
||||
VSymEnt* foundp = m_statep->getNodeSym(nodep)->findIdFallback(nodep->path());
|
||||
AstCell* cellp = foundp ? VN_CAST(foundp->nodep(), Cell) : NULL;
|
||||
@@ -1217,10 +1303,16 @@ private:
|
||||
AstVar* refp = foundp ? VN_CAST(foundp->nodep(), Var) : NULL;
|
||||
if (!refp) {
|
||||
nodep->v3error("Input/output/inout declaration not found for port: "
|
||||
<<nodep->prettyName());
|
||||
<<nodep->prettyNameQ());
|
||||
} else if (!refp->isIO() && !refp->isIfaceRef()) {
|
||||
nodep->v3error("Pin is not an in/out/inout/interface: "<<nodep->prettyName());
|
||||
nodep->v3error("Pin is not an in/out/inout/interface: "<<nodep->prettyNameQ());
|
||||
} else {
|
||||
if (refp->user4()) {
|
||||
nodep->v3error("Duplicate declaration of port: "<<nodep->prettyNameQ()<<endl
|
||||
<<nodep->warnContextPrimary()<<endl
|
||||
<<refp->warnOther()<<"... Location of original declaration\n"
|
||||
<<refp->warnContextSecondary());
|
||||
}
|
||||
refp->user4(true);
|
||||
VSymEnt* symp = m_statep->insertSym(m_statep->getNodeSym(m_modp),
|
||||
"__pinNumber"+cvtToStr(nodep->pinNum()),
|
||||
@@ -1240,7 +1332,7 @@ private:
|
||||
virtual void visit(AstAssignAlias* nodep) {
|
||||
// tran gates need implicit creation
|
||||
// As VarRefs don't exist in forPrimary, sanity check
|
||||
if (m_statep->forPrimary()) nodep->v3fatalSrc("Assign aliases unexpected pre-dot");
|
||||
UASSERT_OBJ(!m_statep->forPrimary(), nodep, "Assign aliases unexpected pre-dot");
|
||||
if (AstVarRef* forrefp = VN_CAST(nodep->lhsp(), VarRef)) {
|
||||
pinImplicitExprRecurse(forrefp);
|
||||
}
|
||||
@@ -1291,7 +1383,8 @@ class LinkDotScopeVisitor : public AstNVisitor {
|
||||
}
|
||||
virtual void visit(AstScope* nodep) {
|
||||
UINFO(8," SCOPE "<<nodep<<endl);
|
||||
if (!m_statep->forScopeCreation()) v3fatalSrc("Scopes should only exist right after V3Scope");
|
||||
UASSERT_OBJ(m_statep->forScopeCreation(), nodep,
|
||||
"Scopes should only exist right after V3Scope");
|
||||
// Using the CELL names, we created all hierarchy. We now need to match this Scope
|
||||
// up with the hierarchy created by the CELL names.
|
||||
m_modSymp = m_statep->getScopeSym(nodep);
|
||||
@@ -1309,20 +1402,21 @@ class LinkDotScopeVisitor : public AstNVisitor {
|
||||
// Find the interface cell the var references
|
||||
AstIfaceRefDType* dtypep
|
||||
= LinkDotState::ifaceRefFromArray(nodep->varp()->dtypep());
|
||||
if (!dtypep) nodep->v3fatalSrc("Non AstIfaceRefDType on isIfaceRef() var");
|
||||
UASSERT_OBJ(dtypep, nodep, "Non AstIfaceRefDType on isIfaceRef() var");
|
||||
UINFO(9,"Iface parent dtype "<<dtypep<<endl);
|
||||
string ifcellname = dtypep->cellName();
|
||||
string baddot; VSymEnt* okSymp;
|
||||
VSymEnt* cellSymp = m_statep->findDotted(m_modSymp, ifcellname, baddot, okSymp);
|
||||
if (!cellSymp) nodep->v3fatalSrc("No symbol for interface cell: "
|
||||
<<nodep->prettyName(ifcellname));
|
||||
UASSERT_OBJ(cellSymp, nodep,
|
||||
"No symbol for interface cell: "<<nodep->prettyNameQ(ifcellname));
|
||||
UINFO(5, " Found interface cell: se"<<cvtToHex(cellSymp)
|
||||
<<" "<<cellSymp->nodep()<<endl);
|
||||
if (dtypep->modportName()!="") {
|
||||
VSymEnt* mpSymp = m_statep->findDotted(m_modSymp, ifcellname, baddot, okSymp);
|
||||
if (!mpSymp) { nodep->v3fatalSrc("No symbol for interface modport: "
|
||||
<<nodep->prettyName(dtypep->modportName())); }
|
||||
else cellSymp = mpSymp;
|
||||
UASSERT_OBJ(mpSymp, nodep,
|
||||
"No symbol for interface modport: "
|
||||
<<nodep->prettyNameQ(dtypep->modportName()));
|
||||
cellSymp = mpSymp;
|
||||
UINFO(5, " Found modport cell: se"
|
||||
<<cvtToHex(cellSymp)<<" "<<mpSymp->nodep()<<endl);
|
||||
}
|
||||
@@ -1344,7 +1438,7 @@ class LinkDotScopeVisitor : public AstNVisitor {
|
||||
if (debug()>=9) nodep->dumpTree(cout, "-\t\t\t\talias: ");
|
||||
AstVarScope* fromVscp = VN_CAST(nodep->lhsp(), VarRef)->varScopep();
|
||||
AstVarScope* toVscp = VN_CAST(nodep->rhsp(), VarRef)->varScopep();
|
||||
if (!fromVscp || !toVscp) nodep->v3fatalSrc("Bad alias scopes");
|
||||
UASSERT_OBJ(fromVscp && toVscp, nodep, "Bad alias scopes");
|
||||
fromVscp->user2p(toVscp);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
@@ -1355,7 +1449,8 @@ class LinkDotScopeVisitor : public AstNVisitor {
|
||||
{
|
||||
AstVarRef* refp = VN_CAST(nodep->rhsp(), VarRef);
|
||||
AstVarXRef* xrefp = VN_CAST(nodep->rhsp(), VarXRef);
|
||||
if (!refp && !xrefp) nodep->v3fatalSrc("Unsupported: Non Var(X)Ref attached to interface pin");
|
||||
UASSERT_OBJ(refp || xrefp, nodep,
|
||||
"Unsupported: Non Var(X)Ref attached to interface pin");
|
||||
string inl = ((xrefp && xrefp->inlinedDots().size())
|
||||
? (xrefp->inlinedDots() + "__DOT__") : "");
|
||||
VSymEnt* symp = NULL;
|
||||
@@ -1371,7 +1466,7 @@ class LinkDotScopeVisitor : public AstNVisitor {
|
||||
}
|
||||
if (!symp) UINFO(9,"No symbol for interface alias rhs ("
|
||||
<<string(refp?"VARREF ":"VARXREF ")<<scopename<<")"<<endl);
|
||||
if (!symp) nodep->v3fatalSrc("No symbol for interface alias rhs");
|
||||
UASSERT_OBJ(symp, nodep, "No symbol for interface alias rhs");
|
||||
UINFO(5, " Found a linked scope RHS: "<<scopename<<" se"
|
||||
<<cvtToHex(symp)<<" "<<symp->nodep()<<endl);
|
||||
rhsSymp = symp;
|
||||
@@ -1381,11 +1476,12 @@ class LinkDotScopeVisitor : public AstNVisitor {
|
||||
const AstVarXRef* xrefp = VN_CAST(nodep->lhsp(), VarXRef);
|
||||
const AstVarRef* refp = VN_CAST(nodep->lhsp(), VarRef);
|
||||
|
||||
if (!refp && !xrefp) nodep->v3fatalSrc("Unsupported: Non Var(X)Ref attached to interface pin");
|
||||
UASSERT_OBJ(refp || xrefp, nodep,
|
||||
"Unsupported: Non Var(X)Ref attached to interface pin");
|
||||
string scopename = refp ? refp->varp()->name() : xrefp->dotted()+"."+xrefp->name();
|
||||
string baddot; VSymEnt* okSymp;
|
||||
VSymEnt* symp = m_statep->findDotted(m_modSymp, scopename, baddot, okSymp);
|
||||
if (!symp) nodep->v3fatalSrc("No symbol for interface alias lhs");
|
||||
UASSERT_OBJ(symp, nodep, "No symbol for interface alias lhs");
|
||||
UINFO(5, " Found a linked scope LHS: "<<scopename
|
||||
<<" se"<<cvtToHex(symp)<<" "<<symp->nodep()<<endl);
|
||||
lhsSymp = symp;
|
||||
@@ -1449,7 +1545,7 @@ class LinkDotIfaceVisitor : public AstNVisitor {
|
||||
if (nodep->isExport()) nodep->v3error("Unsupported: modport export");
|
||||
VSymEnt* symp = m_curSymp->findIdFallback(nodep->name());
|
||||
if (!symp) {
|
||||
nodep->v3error("Modport item not found: "<<nodep->prettyName());
|
||||
nodep->v3error("Modport item not found: "<<nodep->prettyNameQ());
|
||||
} else if (AstNodeFTask* ftaskp = VN_CAST(symp->nodep(), NodeFTask)) {
|
||||
// Make symbol under modport that points at the _interface_'s var, not the modport.
|
||||
nodep->ftaskp(ftaskp);
|
||||
@@ -1457,7 +1553,7 @@ class LinkDotIfaceVisitor : public AstNVisitor {
|
||||
ftaskp, NULL/*package*/);
|
||||
m_statep->insertScopeAlias(LinkDotState::SAMN_MODPORT, subSymp, symp);
|
||||
} else {
|
||||
nodep->v3error("Modport item is not a function/task: "<<nodep->prettyName());
|
||||
nodep->v3error("Modport item is not a function/task: "<<nodep->prettyNameQ());
|
||||
}
|
||||
if (m_statep->forScopeCreation()) {
|
||||
// Done with AstModportFTaskRef.
|
||||
@@ -1470,7 +1566,7 @@ class LinkDotIfaceVisitor : public AstNVisitor {
|
||||
iterateChildren(nodep);
|
||||
VSymEnt* symp = m_curSymp->findIdFallback(nodep->name());
|
||||
if (!symp) {
|
||||
nodep->v3error("Modport item not found: "<<nodep->prettyName());
|
||||
nodep->v3error("Modport item not found: "<<nodep->prettyNameQ());
|
||||
} else if (AstVar* varp = VN_CAST(symp->nodep(), Var)) {
|
||||
// Make symbol under modport that points at the _interface_'s var via the modport.
|
||||
// (Need modport still to test input/output markings)
|
||||
@@ -1481,7 +1577,7 @@ class LinkDotIfaceVisitor : public AstNVisitor {
|
||||
nodep->varp(vscp->varp());
|
||||
m_statep->insertSym(m_curSymp, nodep->name(), vscp, NULL/*package*/);
|
||||
} else {
|
||||
nodep->v3error("Modport item is not a variable: "<<nodep->prettyName());
|
||||
nodep->v3error("Modport item is not a variable: "<<nodep->prettyNameQ());
|
||||
}
|
||||
if (m_statep->forScopeCreation()) {
|
||||
// Done with AstModportVarRef.
|
||||
@@ -1582,11 +1678,21 @@ private:
|
||||
if (!nodep->varp()) {
|
||||
if (!noWarn) {
|
||||
if (nodep->fileline()->warnIsOff(V3ErrorCode::I_DEF_NETTYPE_WIRE)) {
|
||||
string suggest = m_statep->suggestSymFallback(
|
||||
moduleSymp, nodep->name(), LinkNodeMatcherVar());
|
||||
nodep->v3error("Signal definition not found, and implicit disabled with `default_nettype: "
|
||||
<<nodep->prettyName());
|
||||
} else {
|
||||
<<nodep->prettyNameQ()<<endl
|
||||
<<(suggest.empty() ? "" : nodep->warnMore()+suggest));
|
||||
|
||||
}
|
||||
// Bypass looking for suggestions if IMPLICIT is turned off
|
||||
// as there could be thousands of these suppressed in large netlists
|
||||
else if (!nodep->fileline()->warnIsOff(V3ErrorCode::IMPLICIT)) {
|
||||
string suggest = m_statep->suggestSymFallback(
|
||||
moduleSymp, nodep->name(), LinkNodeMatcherVar());
|
||||
nodep->v3warn(IMPLICIT, "Signal definition not found, creating implicitly: "
|
||||
<<nodep->prettyName());
|
||||
<<nodep->prettyNameQ()<<endl
|
||||
<<(suggest.empty() ? "" : nodep->warnMore()+suggest));
|
||||
}
|
||||
}
|
||||
AstVar* newp = new AstVar(nodep->fileline(), AstVarType::WIRE,
|
||||
@@ -1609,7 +1715,7 @@ private:
|
||||
AstModportVarRef* snodep = VN_CAST(symp->nodep(), ModportVarRef);
|
||||
AstVar* varp = snodep->varp();
|
||||
if (lvalue && snodep->direction().isReadOnly()) {
|
||||
nodep->v3error("Attempt to drive input-only modport: "<<nodep->prettyName());
|
||||
nodep->v3error("Attempt to drive input-only modport: "<<nodep->prettyNameQ());
|
||||
} // else other simulators don't warn about reading, and IEEE doesn't say illegal
|
||||
return varp;
|
||||
} else {
|
||||
@@ -1619,7 +1725,7 @@ private:
|
||||
void taskFuncSwapCheck(AstNodeFTaskRef* nodep) {
|
||||
if (nodep->taskp() && VN_IS(nodep->taskp(), Task)
|
||||
&& VN_IS(nodep, FuncRef)) nodep->v3error("Illegal call of a task as a function: "
|
||||
<<nodep->prettyName());
|
||||
<<nodep->prettyNameQ());
|
||||
}
|
||||
inline void checkNoDot(AstNode* nodep) {
|
||||
if (VL_UNLIKELY(m_ds.m_dotPos != DP_NONE)) {
|
||||
@@ -1635,7 +1741,8 @@ private:
|
||||
string varName
|
||||
= ifacep->name()+"__Vmp__"+modportp->name()+"__Viftop"+cvtToStr(++m_modportNum);
|
||||
AstIfaceRefDType* idtypep
|
||||
= new AstIfaceRefDType(fl, cellp->name(), ifacep->name(), modportp->name());
|
||||
= new AstIfaceRefDType(fl, modportp->fileline(),
|
||||
cellp->name(), ifacep->name(), modportp->name());
|
||||
idtypep->cellp(cellp);
|
||||
AstVar* varp = new AstVar(fl, AstVarType::IFACEREF, varName, VFlagChildDType(), idtypep);
|
||||
varp->isIfaceParent(true);
|
||||
@@ -1644,9 +1751,11 @@ private:
|
||||
}
|
||||
void markAndCheckPinDup(AstNode* nodep, AstNode* refp, const char* whatp) {
|
||||
if (refp->user5p() && refp->user5p()!=nodep) {
|
||||
nodep->v3error("Duplicate "<<whatp<<" connection: "<<nodep->prettyName()<<endl
|
||||
<<refp->user5p()->warnMore()
|
||||
<<"... Location of original "<<whatp<<" connection");
|
||||
nodep->v3error("Duplicate "<<whatp<<" connection: "<<nodep->prettyNameQ()<<endl
|
||||
<<nodep->warnContextPrimary()<<endl
|
||||
<<refp->user5p()->warnOther()
|
||||
<<"... Location of original "<<whatp<<" connection\n"
|
||||
<<refp->user5p()->warnContextSecondary());
|
||||
} else {
|
||||
refp->user5p(nodep);
|
||||
}
|
||||
@@ -1693,10 +1802,9 @@ private:
|
||||
checkNoDot(nodep);
|
||||
m_cellp = nodep;
|
||||
AstNode::user5ClearTree();
|
||||
if (!nodep->modp()) {
|
||||
nodep->v3fatalSrc("Cell has unlinked module"); // V3LinkCell should have errored out
|
||||
}
|
||||
else {
|
||||
UASSERT_OBJ(nodep->modp(), nodep,
|
||||
"Cell has unlinked module"); // V3LinkCell should have errored out
|
||||
{
|
||||
if (VN_IS(nodep->modp(), NotFoundModule)) {
|
||||
// Prevent warnings about missing pin connects
|
||||
if (nodep->pinsp()) nodep->pinsp()->unlinkFrBackWithNext()->deleteTree();
|
||||
@@ -1723,7 +1831,7 @@ private:
|
||||
checkNoDot(nodep);
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->modVarp()) {
|
||||
if (!m_pinSymp) nodep->v3fatalSrc("Pin not under cell?");
|
||||
UASSERT_OBJ(m_pinSymp, nodep, "Pin not under cell?");
|
||||
VSymEnt* foundp = m_pinSymp->findIdFlat(nodep->name());
|
||||
const char* whatp = nodep->param() ? "parameter pin" : "pin";
|
||||
if (!foundp) {
|
||||
@@ -1732,12 +1840,19 @@ private:
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
return;
|
||||
}
|
||||
nodep->v3error(ucfirst(whatp)<<" not found: "<<nodep->prettyName());
|
||||
string suggest
|
||||
= (nodep->param()
|
||||
? m_statep->suggestSymFlat(
|
||||
m_pinSymp, nodep->name(), LinkNodeMatcherVarParam())
|
||||
: m_statep->suggestSymFlat(
|
||||
m_pinSymp, nodep->name(), LinkNodeMatcherVarIO()));
|
||||
nodep->v3error(ucfirst(whatp)<<" not found: "<<nodep->prettyNameQ()<<endl
|
||||
<<(suggest.empty() ? "" : nodep->warnMore()+suggest));
|
||||
}
|
||||
else if (AstVar* refp = VN_CAST(foundp->nodep(), Var)) {
|
||||
if (!refp->isIO() && !refp->isParam() && !refp->isIfaceRef()) {
|
||||
nodep->v3error(ucfirst(whatp)<<" is not an in/out/inout/param/interface: "
|
||||
<<nodep->prettyName());
|
||||
<<nodep->prettyNameQ());
|
||||
} else {
|
||||
nodep->modVarp(refp);
|
||||
markAndCheckPinDup(nodep, refp, whatp);
|
||||
@@ -1748,7 +1863,7 @@ private:
|
||||
markAndCheckPinDup(nodep, refp, whatp);
|
||||
}
|
||||
else {
|
||||
nodep->v3error(ucfirst(whatp)<<" not found: "<<nodep->prettyName());
|
||||
nodep->v3error(ucfirst(whatp)<<" not found: "<<nodep->prettyNameQ());
|
||||
}
|
||||
}
|
||||
// Early return() above when deleted
|
||||
@@ -1824,8 +1939,8 @@ private:
|
||||
UINFO(9," linkPARSEREF "<<m_ds.ascii()<<" n="<<nodep<<endl);
|
||||
// m_curSymp is symbol table of outer expression
|
||||
// m_ds.m_dotSymp is symbol table relative to "."'s above now
|
||||
if (!m_ds.m_dotSymp) nodep->v3fatalSrc("NULL lookup symbol table");
|
||||
if (!m_statep->forPrimary()) nodep->v3fatalSrc("ParseRefs should no longer exist");
|
||||
UASSERT_OBJ(m_ds.m_dotSymp, nodep, "NULL lookup symbol table");
|
||||
UASSERT_OBJ(m_statep->forPrimary(), nodep, "ParseRefs should no longer exist");
|
||||
DotStates lastStates = m_ds;
|
||||
bool start = (m_ds.m_dotPos == DP_NONE); // Save, as m_dotp will be changed
|
||||
if (start) {
|
||||
@@ -1851,11 +1966,10 @@ private:
|
||||
expectWhat = "scope/variable";
|
||||
allowScope = true;
|
||||
allowVar = true;
|
||||
if (!VN_IS(m_ds.m_dotp->lhsp(), PackageRef)) {
|
||||
m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
|
||||
}
|
||||
UASSERT_OBJ(VN_IS(m_ds.m_dotp->lhsp(), PackageRef),
|
||||
m_ds.m_dotp->lhsp(), "Bad package link");
|
||||
packagep = VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep();
|
||||
if (!packagep) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
|
||||
UASSERT_OBJ(packagep, m_ds.m_dotp->lhsp(), "Bad package link");
|
||||
m_ds.m_dotSymp = m_statep->getNodeSym(packagep);
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
} else if (m_ds.m_dotPos == DP_SCOPE) {
|
||||
@@ -1903,13 +2017,13 @@ private:
|
||||
if (VN_IS(cellp->modp(), Iface)) {
|
||||
// Interfaces can be referenced like a variable for interconnect
|
||||
VSymEnt* cellEntp = m_statep->getNodeSym(cellp);
|
||||
if (!cellEntp) nodep->v3fatalSrc("No interface sym entry");
|
||||
UASSERT_OBJ(cellEntp, nodep, "No interface sym entry");
|
||||
VSymEnt* parentEntp = cellEntp->parentp(); // Container of the var; probably a module or generate begin
|
||||
string findName = nodep->name()+"__Viftop";
|
||||
VSymEnt* ifaceSymp = parentEntp->findIdFallback(findName);
|
||||
AstVar* ifaceRefVarp = ifaceSymp ? VN_CAST(ifaceSymp->nodep(), Var) : NULL;
|
||||
if (!ifaceRefVarp) nodep->v3fatalSrc("Can't find interface var ref: "
|
||||
<<findName);
|
||||
UASSERT_OBJ(ifaceRefVarp, nodep,
|
||||
"Can't find interface var ref: "<<findName);
|
||||
//
|
||||
ok = true;
|
||||
m_ds.m_dotText = VString::dot(m_ds.m_dotText, ".", nodep->name());
|
||||
@@ -1920,15 +2034,15 @@ private:
|
||||
ifaceRefVarp, false);
|
||||
nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
} else if (VN_IS(cellp->modp(), NotFoundModule)) {
|
||||
cellp->v3error("Cannot find file containing interface: "
|
||||
<<AstNode::prettyName(cellp->modp()->name()));
|
||||
cellp->modNameFileline()->v3error("Cannot find file containing interface: "
|
||||
<<cellp->modp()->prettyNameQ());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (AstVar* varp = foundToVarp(foundp, nodep, false)) {
|
||||
AstIfaceRefDType* ifacerefp = LinkDotState::ifaceRefFromArray(varp->subDTypep());
|
||||
if (ifacerefp) {
|
||||
if (!ifacerefp->ifaceViaCellp()) ifacerefp->v3fatalSrc("Unlinked interface");
|
||||
UASSERT_OBJ(ifacerefp->ifaceViaCellp(), ifacerefp, "Unlinked interface");
|
||||
// Really this is a scope reference into an interface
|
||||
UINFO(9,"varref-ifaceref "<<m_ds.m_dotText<<" "<<nodep<<endl);
|
||||
m_ds.m_dotText = VString::dot(m_ds.m_dotText, ".", nodep->name());
|
||||
@@ -1976,14 +2090,14 @@ private:
|
||||
|| !VN_CAST(m_ds.m_dotSymp->nodep(), Cell)->modp()
|
||||
|| !VN_IS(VN_CAST(m_ds.m_dotSymp->nodep(), Cell)->modp(), Iface)) {
|
||||
nodep->v3error("Modport not referenced as <interface>."
|
||||
<<modportp->prettyName());
|
||||
<<modportp->prettyNameQ());
|
||||
} else if (!VN_CAST(m_ds.m_dotSymp->nodep(), Cell)->modp()
|
||||
|| !VN_IS(VN_CAST(m_ds.m_dotSymp->nodep(), Cell)->modp(), Iface)) {
|
||||
nodep->v3error("Modport not referenced from underneath an interface: "
|
||||
<<modportp->prettyName());
|
||||
<<modportp->prettyNameQ());
|
||||
} else {
|
||||
AstCell* cellp = VN_CAST(m_ds.m_dotSymp->nodep(), Cell);
|
||||
if (!cellp) nodep->v3fatalSrc("Modport not referenced from a cell");
|
||||
UASSERT_OBJ(cellp, nodep, "Modport not referenced from a cell");
|
||||
AstIface* ifacep = VN_CAST(cellp->modp(), Iface);
|
||||
//string cellName = m_ds.m_dotText; // Use cellp->name
|
||||
m_ds.m_dotText = VString::dot(m_ds.m_dotText, ".", nodep->name());
|
||||
@@ -2021,13 +2135,16 @@ private:
|
||||
} else if (m_ds.m_dotText=="") {
|
||||
UINFO(7," ErrParseRef curSymp=se"<<cvtToHex(m_curSymp)
|
||||
<<" ds="<<m_ds.ascii()<<endl);
|
||||
string suggest = m_statep->suggestSymFallback(
|
||||
m_ds.m_dotSymp, nodep->name(), VNodeMatcher());
|
||||
nodep->v3error("Can't find definition of "<<expectWhat
|
||||
<<": "<<nodep->prettyName());
|
||||
<<": "<<nodep->prettyNameQ()<<endl
|
||||
<<(suggest.empty() ? "" : nodep->warnMore()+suggest));
|
||||
} else {
|
||||
nodep->v3error("Can't find definition of '"
|
||||
<<(baddot!="" ? baddot : nodep->prettyName())
|
||||
<<"' in dotted "
|
||||
<<expectWhat<<": "<<m_ds.m_dotText+"."+nodep->prettyName());
|
||||
<<"' in dotted "<<expectWhat
|
||||
<<": '"<<m_ds.m_dotText+"."+nodep->prettyName()<<"'");
|
||||
okSymp->cellErrorScopes(nodep, AstNode::prettyName(m_ds.m_dotText));
|
||||
}
|
||||
m_ds.m_dotErr = true;
|
||||
@@ -2053,14 +2170,14 @@ private:
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->varp()) {
|
||||
UINFO(9," linkVarRef se"<<cvtToHex(m_curSymp)<<" n="<<nodep<<endl);
|
||||
if (!m_curSymp) nodep->v3fatalSrc("NULL lookup symbol table");
|
||||
UASSERT_OBJ(m_curSymp, nodep, "NULL lookup symbol table");
|
||||
VSymEnt* foundp = m_curSymp->findIdFallback(nodep->name());
|
||||
if (AstVar* varp = foundp ? foundToVarp(foundp, nodep, nodep->lvalue()) : NULL) {
|
||||
nodep->varp(varp);
|
||||
nodep->packagep(foundp->packagep()); // Generally set by parse, but might be an import
|
||||
}
|
||||
if (!nodep->varp()) {
|
||||
nodep->v3error("Can't find definition of signal, again: "<<nodep->prettyName());
|
||||
nodep->v3error("Can't find definition of signal, again: "<<nodep->prettyNameQ());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2082,10 +2199,9 @@ private:
|
||||
dotSymp = m_modSymp; // Dotted lookup is always relative to module, as maybe variable name lower down with same scope name we want to ignore (t_math_divw)
|
||||
string inl = AstNode::dedotName(nodep->inlinedDots());
|
||||
dotSymp = m_statep->findDotted(dotSymp, inl, baddot, okSymp);
|
||||
if (!dotSymp) {
|
||||
nodep->v3fatalSrc("Couldn't resolve inlined scope '"
|
||||
<<baddot<<"' in: "<<nodep->inlinedDots());
|
||||
}
|
||||
UASSERT_OBJ(dotSymp, nodep,
|
||||
"Couldn't resolve inlined scope '"
|
||||
<<baddot<<"' in: "<<nodep->inlinedDots());
|
||||
}
|
||||
dotSymp = m_statep->findDotted(dotSymp, nodep->dotted(), baddot, okSymp); // Maybe NULL
|
||||
if (!m_statep->forScopeCreation()) {
|
||||
@@ -2095,8 +2211,8 @@ private:
|
||||
UINFO(7," Resolved "<<nodep<<endl); // Also prints varp
|
||||
if (!nodep->varp()) {
|
||||
nodep->v3error("Can't find definition of '"
|
||||
<<baddot<<"' in dotted signal: "
|
||||
<<nodep->dotted()+"."+nodep->prettyName());
|
||||
<<baddot<<"' in dotted signal: '"
|
||||
<<nodep->dotted()+"."+nodep->prettyName()<<"'");
|
||||
okSymp->cellErrorScopes(nodep);
|
||||
}
|
||||
// V3Inst may have expanded arrays of interfaces to
|
||||
@@ -2116,8 +2232,8 @@ private:
|
||||
AstVarScope* vscp = foundp ? VN_CAST(foundp->nodep(), VarScope) : NULL;
|
||||
if (!vscp) {
|
||||
nodep->v3error("Can't find varpin scope of '"<<baddot
|
||||
<<"' in dotted signal: "
|
||||
<<nodep->dotted()+"."+nodep->prettyName());
|
||||
<<"' in dotted signal: '"
|
||||
<<nodep->dotted()+"."+nodep->prettyName()<<"'");
|
||||
okSymp->cellErrorScopes(nodep);
|
||||
} else {
|
||||
while (vscp->user2p()) { // If V3Inline aliased it, pick up the new signal
|
||||
@@ -2155,19 +2271,17 @@ private:
|
||||
iterateChildren(nodep);
|
||||
if (m_statep->forPrimary() && nodep->isIO() && !m_ftaskp && !nodep->user4()) {
|
||||
nodep->v3error("Input/output/inout does not appear in port list: "
|
||||
<<nodep->prettyName());
|
||||
<<nodep->prettyNameQ());
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeFTaskRef* nodep) {
|
||||
if (nodep->user3SetOnce()) return;
|
||||
UINFO(8," "<<nodep<<endl);
|
||||
if (m_ds.m_dotp && m_ds.m_dotPos == DP_PACKAGE) {
|
||||
if (!VN_IS(m_ds.m_dotp->lhsp(), PackageRef)) {
|
||||
m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
|
||||
}
|
||||
if (!VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep()) {
|
||||
m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
|
||||
}
|
||||
UASSERT_OBJ(VN_IS(m_ds.m_dotp->lhsp(), PackageRef),
|
||||
m_ds.m_dotp->lhsp(), "Bad package link");
|
||||
UASSERT_OBJ(VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep(),
|
||||
m_ds.m_dotp->lhsp(), "Bad package link");
|
||||
nodep->packagep(VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep());
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
m_ds.m_dotp = NULL;
|
||||
@@ -2248,12 +2362,18 @@ private:
|
||||
<<"'"<<" as a "<<foundp->nodep()->typeName()
|
||||
<<" but expected a task/function");
|
||||
} else if (nodep->dotted() == "") {
|
||||
string suggest = m_statep->suggestSymFallback(
|
||||
dotSymp, nodep->name(), LinkNodeMatcherFTask());
|
||||
nodep->v3error("Can't find definition of task/function: "
|
||||
<<nodep->prettyName());
|
||||
<<nodep->prettyNameQ()<<endl
|
||||
<<(suggest.empty() ? "" : nodep->warnMore()+suggest));
|
||||
} else {
|
||||
string suggest = m_statep->suggestSymFallback(
|
||||
dotSymp, nodep->name(), LinkNodeMatcherFTask());
|
||||
nodep->v3error("Can't find definition of '"<<baddot
|
||||
<<"' in dotted task/function: "
|
||||
<<nodep->dotted()+"."+nodep->prettyName());
|
||||
<<"' in dotted task/function: '"
|
||||
<<nodep->dotted()+"."+nodep->prettyName()<<"'\n"
|
||||
<<(suggest.empty() ? "" : nodep->warnMore()+suggest));
|
||||
okSymp->cellErrorScopes(nodep);
|
||||
}
|
||||
}
|
||||
@@ -2341,12 +2461,10 @@ private:
|
||||
// Resolve its reference
|
||||
if (nodep->user3SetOnce()) return;
|
||||
if (m_ds.m_dotp && m_ds.m_dotPos == DP_PACKAGE) {
|
||||
if (!VN_IS(m_ds.m_dotp->lhsp(), PackageRef)) {
|
||||
m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
|
||||
}
|
||||
if (!VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep()) {
|
||||
m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
|
||||
}
|
||||
UASSERT_OBJ(VN_IS(m_ds.m_dotp->lhsp(), PackageRef),
|
||||
m_ds.m_dotp->lhsp(), "Bad package link");
|
||||
UASSERT_OBJ(VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep(),
|
||||
m_ds.m_dotp->lhsp(), "Bad package link");
|
||||
nodep->packagep(VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep());
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
m_ds.m_dotp = NULL;
|
||||
@@ -2370,7 +2488,7 @@ private:
|
||||
nodep->packagep(foundp->packagep());
|
||||
}
|
||||
else {
|
||||
nodep->v3error("Can't find typedef: "<<nodep->prettyName());
|
||||
nodep->v3error("Can't find typedef: "<<nodep->prettyNameQ());
|
||||
}
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
@@ -2382,10 +2500,10 @@ private:
|
||||
VSymEnt* foundp = m_curSymp->findIdFallback(nodep->name());
|
||||
AstNodeFTask* taskp = foundp ? VN_CAST(foundp->nodep(), NodeFTask) : NULL;
|
||||
if (!taskp) { nodep->v3error("Can't find definition of exported task/function: "
|
||||
<<nodep->prettyName()); }
|
||||
<<nodep->prettyNameQ()); }
|
||||
else if (taskp->dpiExport()) {
|
||||
nodep->v3error("Function was already DPI Exported, duplicate not allowed: "
|
||||
<<nodep->prettyName());
|
||||
<<nodep->prettyNameQ());
|
||||
} else {
|
||||
taskp->dpiExport(true);
|
||||
if (nodep->cname()!="") taskp->cname(nodep->cname());
|
||||
|
||||
+4
-6
@@ -81,12 +81,10 @@ private:
|
||||
// Also this would otherwise prevent us from using a label twice
|
||||
// see t_func_return test.
|
||||
while (underp && VN_IS(underp, Var)) underp = underp->nextp();
|
||||
if (underp) UINFO(5," Underpoint is "<<underp<<endl);
|
||||
UASSERT_OBJ(underp, nodep, "Break/disable/continue not under expected statement");
|
||||
UINFO(5," Underpoint is "<<underp<<endl);
|
||||
|
||||
if (!underp) {
|
||||
nodep->v3fatalSrc("Break/disable/continue not under expected statement");
|
||||
return NULL;
|
||||
} else if (VN_IS(underp, JumpLabel)) {
|
||||
if (VN_IS(underp, JumpLabel)) {
|
||||
return VN_CAST(underp, JumpLabel);
|
||||
} else { // Move underp stuff to be under a new label
|
||||
AstJumpLabel* labelp = new AstJumpLabel(nodep->fileline(), NULL);
|
||||
@@ -232,7 +230,7 @@ private:
|
||||
}
|
||||
//if (debug()>=9) { UINFO(0,"\n"); beginp->dumpTree(cout, " labeli: "); }
|
||||
if (!beginp) { nodep->v3error("disable isn't underneath a begin with name: "
|
||||
<<nodep->prettyName()); }
|
||||
<<nodep->prettyNameQ()); }
|
||||
else {
|
||||
// Jump to the end of the named begin
|
||||
AstJumpLabel* labelp = findAddLabel(beginp, false);
|
||||
|
||||
@@ -59,7 +59,7 @@ private:
|
||||
if (nodep->lvalue() && !m_ftaskp
|
||||
&& nodep->varp()->isReadOnly()) {
|
||||
nodep->v3warn(ASSIGNIN, "Assigning to input/const variable: "
|
||||
<<nodep->prettyName());
|
||||
<<nodep->prettyNameQ());
|
||||
}
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
|
||||
+117
-72
@@ -53,34 +53,41 @@ void V3LinkLevel::modSortByLevel() {
|
||||
|
||||
typedef std::vector<AstNodeModule*> ModVec;
|
||||
|
||||
ModVec vec;
|
||||
AstNodeModule* topp = NULL;
|
||||
ModVec mods; // Modules
|
||||
ModVec tops; // Top level modules
|
||||
for (AstNodeModule* nodep = v3Global.rootp()->modulesp();
|
||||
nodep; nodep=VN_CAST(nodep->nextp(), NodeModule)) {
|
||||
if (nodep->level()<=2) {
|
||||
if (topp) {
|
||||
static int warnedOnce = 0;
|
||||
nodep->v3warn(E_MULTITOP, "Unsupported: Multiple top level modules: "
|
||||
<<nodep->prettyName()<<" and "<<topp->prettyName()<<endl
|
||||
<<(!warnedOnce++
|
||||
? (nodep->warnMore()
|
||||
+"... Fix, or use --top-module option to select which you want.")
|
||||
: ""));
|
||||
}
|
||||
topp = nodep;
|
||||
tops.push_back(nodep);
|
||||
}
|
||||
vec.push_back(nodep);
|
||||
mods.push_back(nodep);
|
||||
}
|
||||
stable_sort(vec.begin(), vec.end(), CmpLevel()); // Sort the vector
|
||||
if (tops.size() >= 2) {
|
||||
AstNode* secp = tops[1]; // Complain about second one, as first often intended
|
||||
if (!secp->fileline()->warnIsOff(V3ErrorCode::MULTITOP)) {
|
||||
secp->v3warn(MULTITOP, "Multiple top level modules\n"
|
||||
<<secp->warnMore()
|
||||
<<"... Suggest see manual; fix the duplicates, or use --top-module to select top."
|
||||
<<V3Error::warnContextNone());
|
||||
for (ModVec::iterator it = tops.begin(); it != tops.end(); ++it) {
|
||||
AstNode* alsop = *it;
|
||||
std::cout<<secp->warnMore()<<"... Top module "<<alsop->prettyNameQ()<<endl
|
||||
<<alsop->warnContextSecondary();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reorder the netlist's modules to have modules in level sorted order
|
||||
stable_sort(mods.begin(), mods.end(), CmpLevel()); // Sort the vector
|
||||
UINFO(9,"modSortByLevel() sorted\n"); // Comment required for gcc4.6.3 / bug666
|
||||
for (ModVec::iterator it = vec.begin(); it != vec.end(); ++it) {
|
||||
for (ModVec::iterator it = mods.begin(); it != mods.end(); ++it) {
|
||||
AstNodeModule* nodep = *it;
|
||||
nodep->clearIter(); // Because we didn't iterate to find the node
|
||||
// pointers, may have a stale m_iterp() needing cleanup
|
||||
nodep->unlinkFrBack();
|
||||
}
|
||||
if (v3Global.rootp()->modulesp()) v3Global.rootp()->v3fatalSrc("Unlink didn't work");
|
||||
for (ModVec::iterator it = vec.begin(); it != vec.end(); ++it) {
|
||||
UASSERT_OBJ(!v3Global.rootp()->modulesp(), v3Global.rootp(), "Unlink didn't work");
|
||||
for (ModVec::iterator it = mods.begin(); it != mods.end(); ++it) {
|
||||
AstNodeModule* nodep = *it;
|
||||
v3Global.rootp()->addModulep(nodep);
|
||||
}
|
||||
@@ -95,8 +102,11 @@ void V3LinkLevel::wrapTop(AstNetlist* rootp) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
// We do ONLY the top module
|
||||
AstNodeModule* oldmodp = rootp->modulesp();
|
||||
if (!oldmodp) rootp->v3fatalSrc("No module found to process");
|
||||
AstNodeModule* newmodp = new AstModule(oldmodp->fileline(), string("TOP_")+oldmodp->name());
|
||||
if (!oldmodp) { // Later V3LinkDot will warn
|
||||
UINFO(1,"No module found to wrap\n");
|
||||
return;
|
||||
}
|
||||
AstNodeModule* newmodp = new AstModule(oldmodp->fileline(), string("TOP"));
|
||||
// Make the new module first in the list
|
||||
oldmodp->unlinkFrBackWithNext();
|
||||
newmodp->addNext(oldmodp);
|
||||
@@ -108,63 +118,12 @@ void V3LinkLevel::wrapTop(AstNetlist* rootp) {
|
||||
// the rest must be done after data type resolution
|
||||
wrapTopCell(rootp);
|
||||
|
||||
V3Global::dumpCheckGlobalTree("wraptop", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
|
||||
}
|
||||
|
||||
void V3LinkLevel::wrapTopCell(AstNetlist* rootp) {
|
||||
AstNodeModule* newmodp = rootp->modulesp();
|
||||
if (!newmodp || !newmodp->isTop()) rootp->v3fatalSrc("No TOP module found to process");
|
||||
AstNodeModule* oldmodp = VN_CAST(newmodp->nextp(), NodeModule);
|
||||
if (!oldmodp) rootp->v3fatalSrc("No module found to process");
|
||||
|
||||
// Add instance
|
||||
AstCell* cellp = new AstCell(newmodp->fileline(),
|
||||
(!v3Global.opt.l2Name().empty()
|
||||
? v3Global.opt.l2Name() : oldmodp->name()),
|
||||
oldmodp->name(),
|
||||
NULL, NULL, NULL);
|
||||
cellp->modp(oldmodp);
|
||||
newmodp->addStmtp(cellp);
|
||||
|
||||
// Add pins
|
||||
for (AstNode* subnodep=oldmodp->stmtsp(); subnodep; subnodep = subnodep->nextp()) {
|
||||
if (AstVar* oldvarp = VN_CAST(subnodep, Var)) {
|
||||
UINFO(8,"VARWRAP "<<oldvarp<<endl);
|
||||
if (oldvarp->isIO()) {
|
||||
AstVar* varp = oldvarp->cloneTree(false);
|
||||
newmodp->addStmtp(varp);
|
||||
varp->sigPublic(true); // User needs to be able to get to it...
|
||||
if (oldvarp->isIO()) {
|
||||
oldvarp->primaryIO(false);
|
||||
varp->primaryIO(true);
|
||||
}
|
||||
if (varp->direction().isRefOrConstRef()) {
|
||||
varp->v3error("Unsupported: ref/const ref as primary input/output: "
|
||||
<<varp->prettyName());
|
||||
}
|
||||
if (varp->isIO() && v3Global.opt.systemC()) {
|
||||
varp->sc(true);
|
||||
// User can see trace one level down from the wrapper
|
||||
// Avoids packing & unpacking SC signals a second time
|
||||
varp->trace(false);
|
||||
}
|
||||
|
||||
AstPin* pinp = new AstPin(oldvarp->fileline(), 0, oldvarp->name(),
|
||||
new AstVarRef(varp->fileline(),
|
||||
varp, oldvarp->isWritable()));
|
||||
// Skip length and width comp; we know it's a direct assignment
|
||||
pinp->modVarp(oldvarp);
|
||||
cellp->addPinsp(pinp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Instantiate all packages under the top wrapper
|
||||
// This way all later SCOPE based optimizations can ignore packages
|
||||
for (AstNodeModule* modp = rootp->modulesp(); modp; modp=VN_CAST(modp->nextp(), NodeModule)) {
|
||||
if (VN_IS(modp, Package)
|
||||
&& modp != oldmodp) { // Don't duplicate if didn't find a top module
|
||||
if (VN_IS(modp, Package)) {
|
||||
AstCell* cellp = new AstCell(modp->fileline(),
|
||||
modp->fileline(),
|
||||
// Could add __03a__03a="::" to prevent conflict
|
||||
// with module names/"v"
|
||||
modp->name(),
|
||||
@@ -174,4 +133,90 @@ void V3LinkLevel::wrapTopCell(AstNetlist* rootp) {
|
||||
newmodp->addStmtp(cellp);
|
||||
}
|
||||
}
|
||||
|
||||
V3Global::dumpCheckGlobalTree("wraptop", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
|
||||
}
|
||||
|
||||
void V3LinkLevel::wrapTopCell(AstNetlist* rootp) {
|
||||
AstNodeModule* newmodp = rootp->modulesp();
|
||||
UASSERT_OBJ(newmodp && newmodp->isTop(), rootp, "No TOP module found to insert under");
|
||||
|
||||
// Find all duplicate signal names (if multitop)
|
||||
typedef vl_unordered_set<std::string> NameSet;
|
||||
NameSet ioNames;
|
||||
NameSet dupNames;
|
||||
// For all modulues, skipping over new top
|
||||
for (AstNodeModule* oldmodp = VN_CAST(rootp->modulesp()->nextp(), NodeModule);
|
||||
oldmodp && oldmodp->level() <= 2;
|
||||
oldmodp = VN_CAST(oldmodp->nextp(), NodeModule)) {
|
||||
for (AstNode* subnodep = oldmodp->stmtsp(); subnodep; subnodep = subnodep->nextp()) {
|
||||
if (AstVar* oldvarp = VN_CAST(subnodep, Var)) {
|
||||
if (oldvarp->isIO()) {
|
||||
if (ioNames.find(oldvarp->name()) != ioNames.end()) {
|
||||
//UINFO(8, "Multitop dup I/O found: "<<oldvarp<<endl);
|
||||
dupNames.insert(oldvarp->name());
|
||||
} else {
|
||||
ioNames.insert(oldvarp->name());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For all modulues, skipping over new top
|
||||
for (AstNodeModule* oldmodp = VN_CAST(rootp->modulesp()->nextp(), NodeModule);
|
||||
oldmodp && oldmodp->level() <= 2;
|
||||
oldmodp = VN_CAST(oldmodp->nextp(), NodeModule)) {
|
||||
if (VN_IS(oldmodp, Package)) continue;
|
||||
// Add instance
|
||||
UINFO(5,"LOOP "<<oldmodp<<endl);
|
||||
AstCell* cellp = new AstCell(newmodp->fileline(),
|
||||
newmodp->fileline(),
|
||||
(!v3Global.opt.l2Name().empty()
|
||||
? v3Global.opt.l2Name() : oldmodp->name()),
|
||||
oldmodp->name(),
|
||||
NULL, NULL, NULL);
|
||||
cellp->modp(oldmodp);
|
||||
newmodp->addStmtp(cellp);
|
||||
|
||||
// Add pins
|
||||
for (AstNode* subnodep=oldmodp->stmtsp(); subnodep; subnodep = subnodep->nextp()) {
|
||||
if (AstVar* oldvarp = VN_CAST(subnodep, Var)) {
|
||||
UINFO(8,"VARWRAP "<<oldvarp<<endl);
|
||||
if (oldvarp->isIO()) {
|
||||
string name = oldvarp->name();
|
||||
if (dupNames.find(name) != dupNames.end()) {
|
||||
// __02E=. while __DOT__ looks nicer but will break V3LinkDot
|
||||
name = oldmodp->name()+"__02E"+name;
|
||||
}
|
||||
|
||||
AstVar* varp = oldvarp->cloneTree(false);
|
||||
varp->name(name);
|
||||
newmodp->addStmtp(varp);
|
||||
varp->sigPublic(true); // User needs to be able to get to it...
|
||||
if (oldvarp->isIO()) {
|
||||
oldvarp->primaryIO(false);
|
||||
varp->primaryIO(true);
|
||||
}
|
||||
if (varp->direction().isRefOrConstRef()) {
|
||||
varp->v3error("Unsupported: ref/const ref as primary input/output: "
|
||||
<<varp->prettyNameQ());
|
||||
}
|
||||
if (varp->isIO() && v3Global.opt.systemC()) {
|
||||
varp->sc(true);
|
||||
// User can see trace one level down from the wrapper
|
||||
// Avoids packing & unpacking SC signals a second time
|
||||
varp->trace(false);
|
||||
}
|
||||
|
||||
AstPin* pinp = new AstPin(oldvarp->fileline(), 0, varp->name(),
|
||||
new AstVarRef(varp->fileline(),
|
||||
varp, oldvarp->isWritable()));
|
||||
// Skip length and width comp; we know it's a direct assignment
|
||||
pinp->modVarp(oldvarp);
|
||||
cellp->addPinsp(pinp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+17
-15
@@ -177,7 +177,7 @@ private:
|
||||
AstNodeDType* dtypep = VN_CAST(nodep->valuep(), NodeDType);
|
||||
if (!dtypep) {
|
||||
nodep->v3error("Parameter type's initial value isn't a type: "
|
||||
<<nodep->prettyName());
|
||||
<<nodep->prettyNameQ());
|
||||
nodep->unlinkFrBack();
|
||||
} else {
|
||||
dtypep->unlinkFrBack();
|
||||
@@ -209,7 +209,8 @@ private:
|
||||
if (nodep->isParam() || (m_ftaskp && nodep->isNonOutput())) {
|
||||
}
|
||||
else if (!m_ftaskp && nodep->isNonOutput()) {
|
||||
nodep->v3error("Unsupported: Default value on module input: "<<nodep->prettyName());
|
||||
nodep->v3error("Unsupported: Default value on module input: "
|
||||
<<nodep->prettyNameQ());
|
||||
nodep->valuep()->unlinkFrBack()->deleteTree();
|
||||
} // 2. Under modules, it's an initial value to be loaded at time 0 via an AstInitial
|
||||
else if (m_valueModp) {
|
||||
@@ -241,62 +242,62 @@ private:
|
||||
iterateChildren(nodep);
|
||||
if (nodep->attrType() == AstAttrType::DT_PUBLIC) {
|
||||
AstTypedef* typep = VN_CAST(nodep->backp(), Typedef);
|
||||
if (!typep) nodep->v3fatalSrc("Attribute not attached to typedef");
|
||||
UASSERT_OBJ(typep, nodep, "Attribute not attached to typedef");
|
||||
typep->attrPublic(true);
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
else if (nodep->attrType() == AstAttrType::VAR_CLOCK) {
|
||||
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
||||
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
||||
m_varp->attrScClocked(true);
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
else if (nodep->attrType() == AstAttrType::VAR_CLOCK_ENABLE) {
|
||||
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
||||
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
||||
m_varp->attrClockEn(true);
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
else if (nodep->attrType() == AstAttrType::VAR_PUBLIC) {
|
||||
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
||||
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
||||
m_varp->sigUserRWPublic(true); m_varp->sigModPublic(true);
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT) {
|
||||
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
||||
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
||||
m_varp->sigUserRWPublic(true);
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT_RD) {
|
||||
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
||||
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
||||
m_varp->sigUserRdPublic(true);
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT_RW) {
|
||||
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
||||
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
||||
m_varp->sigUserRWPublic(true);
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
else if (nodep->attrType() == AstAttrType::VAR_ISOLATE_ASSIGNMENTS) {
|
||||
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
||||
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
||||
m_varp->attrIsolateAssign(true);
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
else if (nodep->attrType() == AstAttrType::VAR_SFORMAT) {
|
||||
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
||||
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
||||
m_varp->attrSFormat(true);
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
else if (nodep->attrType() == AstAttrType::VAR_SC_BV) {
|
||||
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
||||
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
||||
m_varp->attrScBv(true);
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
else if (nodep->attrType() == AstAttrType::VAR_CLOCKER) {
|
||||
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
||||
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
||||
m_varp->attrClocker(AstVarAttrClocker::CLOCKER_YES);
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
else if (nodep->attrType() == AstAttrType::VAR_NO_CLOCKER) {
|
||||
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
||||
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
||||
m_varp->attrClocker(AstVarAttrClocker::CLOCKER_NO);
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
@@ -337,7 +338,8 @@ private:
|
||||
else if (VN_IS(backp, Typedef)) break;
|
||||
else if (VN_IS(backp, NodeFTask)) break;
|
||||
}
|
||||
if (!backp) nodep->v3fatalSrc("Implicit enum/struct type created under unexpected node type");
|
||||
UASSERT_OBJ(backp, nodep,
|
||||
"Implicit enum/struct type created under unexpected node type");
|
||||
AstNodeDType* dtypep = nodep->childDTypep(); dtypep->unlinkFrBack();
|
||||
if (VN_IS(backp, Typedef)) { // A typedef doesn't need us to make yet another level of typedefing
|
||||
// For typedefs just remove the AstRefDType level of abstraction
|
||||
|
||||
@@ -240,12 +240,12 @@ private:
|
||||
|
||||
virtual void visit(AstPragma* nodep) {
|
||||
if (nodep->pragType() == AstPragmaType::PUBLIC_MODULE) {
|
||||
if (!m_modp) nodep->v3fatalSrc("PUBLIC_MODULE not under a module");
|
||||
UASSERT_OBJ(m_modp, nodep, "PUBLIC_MODULE not under a module");
|
||||
m_modp->modPublic(true);
|
||||
nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
}
|
||||
else if (nodep->pragType() == AstPragmaType::PUBLIC_TASK) {
|
||||
if (!m_ftaskp) nodep->v3fatalSrc("PUBLIC_TASK not under a task");
|
||||
UASSERT_OBJ(m_ftaskp, nodep, "PUBLIC_TASK not under a task");
|
||||
m_ftaskp->taskPublic(true);
|
||||
m_modp->modPublic(true); // Need to get to the task...
|
||||
nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
@@ -287,7 +287,7 @@ private:
|
||||
break;
|
||||
default: // Most operators, just move to next argument
|
||||
if (!V3Number::displayedFmtLegal(ch)) {
|
||||
nodep->v3error("Unknown $display-like format code: %"<<ch);
|
||||
nodep->v3error("Unknown $display-like format code: '%"<<ch<<"'");
|
||||
} else {
|
||||
if (!argp) {
|
||||
nodep->v3error("Missing arguments for $display-like format");
|
||||
@@ -387,7 +387,7 @@ private:
|
||||
iterateChildren(nodep);
|
||||
// Cleanup old-school displays without format arguments
|
||||
if (!nodep->hasFormat()) {
|
||||
if (nodep->text()!="") nodep->v3fatalSrc("Non-format $sformatf should have \"\" format");
|
||||
UASSERT_OBJ(nodep->text()=="", nodep, "Non-format $sformatf should have \"\" format");
|
||||
if (VN_IS(nodep->exprsp(), Const)
|
||||
&& VN_CAST(nodep->exprsp(), Const)->num().isFromString()) {
|
||||
AstConst* fmtp = VN_CAST(nodep->exprsp()->unlinkFrBack(), Const);
|
||||
@@ -404,9 +404,6 @@ private:
|
||||
nodep->scopeNamep(new AstScopeName(nodep->fileline()));
|
||||
}
|
||||
}
|
||||
virtual void visit(AstDisplay* nodep) {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstUdpTable* nodep) {
|
||||
UINFO(5,"UDPTABLE "<<nodep<<endl);
|
||||
|
||||
+1
-1
@@ -173,7 +173,7 @@ private:
|
||||
for (; nodep; nodep=nodep->nextp()) {
|
||||
if (VN_IS(nodep, NodeAssign)) {
|
||||
if (AstVarRef* varrefp = VN_CAST(VN_CAST(nodep, NodeAssign)->lhsp(), VarRef)) {
|
||||
if (!varrefp->lvalue()) varrefp->v3fatalSrc("LHS assignment not lvalue");
|
||||
UASSERT_OBJ(varrefp->lvalue(), varrefp, "LHS assignment not lvalue");
|
||||
if (!varrefp->varp()->user4p()) {
|
||||
UINFO(4," FuncAsn "<<varrefp<<endl);
|
||||
varrefp->varp()->user4p(varrefp);
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ private:
|
||||
string rsvd = m_words.isKeyword(nodep->name());
|
||||
if (rsvd != "") {
|
||||
nodep->v3warn(SYMRSVDWORD, "Symbol matches "+rsvd
|
||||
+": '"<<nodep->prettyName()<<"'");
|
||||
+": "<<nodep->prettyNameQ());
|
||||
string newname = string("__SYM__")+nodep->name();
|
||||
nodep->name(newname);
|
||||
}
|
||||
|
||||
+9
-7
@@ -49,7 +49,11 @@
|
||||
void V3Number::v3errorEnd(std::ostringstream& str) const {
|
||||
std::ostringstream nsstr;
|
||||
nsstr<<str.str();
|
||||
m_fileline->v3errorEnd(nsstr);
|
||||
if (m_nodep) {
|
||||
m_nodep->v3errorEnd(nsstr);
|
||||
} else {
|
||||
m_fileline->v3errorEnd(nsstr);
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
@@ -289,6 +293,7 @@ void V3Number::V3NumberCreate(AstNode* nodep, const char* sourcep, FileLine* fl)
|
||||
}
|
||||
|
||||
void V3Number::setNames(AstNode* nodep) {
|
||||
m_nodep = nodep;
|
||||
if (!nodep) return;
|
||||
m_fileline = nodep->fileline();
|
||||
}
|
||||
@@ -331,7 +336,7 @@ V3Number& V3Number::setLongS(vlsint32_t value) {
|
||||
return *this;
|
||||
}
|
||||
V3Number& V3Number::setDouble(double value) {
|
||||
if (VL_UNLIKELY(width()!=64)) {
|
||||
if (VL_UNCOVERABLE(width()!=64)) {
|
||||
v3fatalSrc("Real operation on wrong sized number");
|
||||
}
|
||||
m_double = true;
|
||||
@@ -685,11 +690,8 @@ uint32_t V3Number::toUInt() const {
|
||||
}
|
||||
|
||||
double V3Number::toDouble() const {
|
||||
if (VL_UNLIKELY(!isDouble())) {
|
||||
v3fatalSrc("Real conversion on non-real number");
|
||||
}
|
||||
if (VL_UNLIKELY(width()!=64)) {
|
||||
v3fatalSrc("Real operation on wrong sized number");
|
||||
if (VL_UNCOVERABLE(!isDouble() || width()!=64)) {
|
||||
v3fatalSrc("Real operation on wrong sized/non-real number");
|
||||
}
|
||||
union { double d; uint32_t u[2]; } u;
|
||||
u.u[0] = m_value[0]; u.u[1] = m_value[1];
|
||||
|
||||
+5
-4
@@ -42,6 +42,7 @@ class V3Number {
|
||||
bool m_fromString:1; // True if from string literal
|
||||
bool m_autoExtend:1; // True if SystemVerilog extend-to-any-width
|
||||
FileLine* m_fileline;
|
||||
AstNode* m_nodep; // Parent node
|
||||
std::vector<uint32_t> m_value; // The Value, with bit 0 being in bit 0 of this vector (unless X/Z)
|
||||
std::vector<uint32_t> m_valueX; // Each bit is true if it's X or Z, 10=z, 11=x
|
||||
string m_stringVal; // If isString, the value of the string
|
||||
@@ -139,8 +140,8 @@ public:
|
||||
// CONSTRUCTORS
|
||||
explicit V3Number(AstNode* nodep) { init(nodep, 1); }
|
||||
V3Number(AstNode* nodep, int width) { init(nodep, width); } // 0=unsized
|
||||
V3Number(AstNode* nodep, int width, uint32_t value) {
|
||||
init(nodep, width); m_value[0] = value; opCleanThis();
|
||||
V3Number(AstNode* nodep, int width, uint32_t value, bool sized=true) {
|
||||
init(nodep, width, sized); m_value[0] = value; opCleanThis();
|
||||
}
|
||||
// Create from a verilog 32'hxxxx number.
|
||||
V3Number(AstNode* nodep, const char* sourcep) { V3NumberCreate(nodep, sourcep, NULL); }
|
||||
@@ -163,14 +164,14 @@ public:
|
||||
|
||||
private:
|
||||
void V3NumberCreate(AstNode* nodep, const char* sourcep, FileLine* fl);
|
||||
void init(AstNode* nodep, int swidth) {
|
||||
void init(AstNode* nodep, int swidth, bool sized=true) {
|
||||
setNames(nodep);
|
||||
m_signed = false;
|
||||
m_double = false;
|
||||
m_isString = false;
|
||||
m_autoExtend = false;
|
||||
m_fromString = false;
|
||||
width(swidth);
|
||||
width(swidth, sized);
|
||||
for (int i=0; i<words(); i++) m_value[i] = m_valueX[i] = 0;
|
||||
}
|
||||
void setNames(AstNode* nodep);
|
||||
|
||||
@@ -38,11 +38,12 @@ void test(const string& lhss, const string& op, const string& rhss, const string
|
||||
char* r1 = strdup(rhss.c_str());
|
||||
char* e1 = strdup(exps.c_str());
|
||||
|
||||
V3Number lhnum (new FileLine("ck", __LINE__), l1);
|
||||
V3Number rhnum (new FileLine("ck", __LINE__), r1);
|
||||
V3Number expnum (new FileLine("ck", __LINE__), e1);
|
||||
FileLine fl = new FileLine(FileLine::builtInFinename());
|
||||
|
||||
V3Number gotnum (new FileLine("ck", __LINE__), expnum.width());
|
||||
V3Number lhnum (fl, l1);
|
||||
V3Number rhnum (fl, r1);
|
||||
V3Number expnum (fl, e1);
|
||||
V3Number gotnum (fl, expnum.width());
|
||||
|
||||
if (op=="redOr") gotnum.opRedOr (lhnum);
|
||||
else if (op=="redAnd") gotnum.opRedAnd (lhnum);
|
||||
@@ -82,7 +83,7 @@ void test(const string& lhss, const string& op, const string& rhss, const string
|
||||
<<" = "<<expnum<<endl
|
||||
<<" =? "<<gotnum<<endl);
|
||||
|
||||
V3Number ok (new FileLine("ck", __LINE__), 1);
|
||||
V3Number ok (fl, 1);
|
||||
ok.opCaseEq(expnum, gotnum);
|
||||
if (ok.toUInt()!=1) {
|
||||
v3fatalSrc("%Error:Test FAILED");
|
||||
|
||||
+8
-5
@@ -173,7 +173,7 @@ void V3Options::checkParameters() {
|
||||
it != m_parameters.end(); ++it) {
|
||||
msg << " " << it->first;
|
||||
}
|
||||
v3fatal(msg.str()<<endl);
|
||||
v3error(msg.str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -669,7 +669,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
||||
else if ( onoff (sw, "-covsp", flag/*ref*/)) { } // TBD
|
||||
else if (!strcmp(sw, "-debug-abort")) { abort(); } // Undocumented, see also --debug-sigsegv
|
||||
else if ( onoff (sw, "-debug-check", flag/*ref*/)) { m_debugCheck = flag; }
|
||||
else if ( onoff (sw, "-debug-collision", flag/*ref*/) ){ m_debugCollision = flag; } // Undocumented
|
||||
else if ( onoff (sw, "-debug-collision", flag/*ref*/)) { m_debugCollision = flag; } // Undocumented
|
||||
else if ( onoff (sw, "-debug-leak", flag/*ref*/)) { m_debugLeak = flag; }
|
||||
else if ( onoff (sw, "-debug-nondeterminism", flag/*ref*/)){ m_debugNondeterminism = flag; }
|
||||
else if ( onoff (sw, "-debug-partition", flag/*ref*/)){ m_debugPartition = flag; } // Undocumented
|
||||
@@ -677,6 +677,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
||||
else if (!strcmp(sw, "-debug-sigsegv")) { throwSigsegv(); } // Undocumented, see also --debug-abort
|
||||
else if (!strcmp(sw, "-debug-fatalsrc")) { v3fatalSrc("--debug-fatal-src"); } // Undocumented, see also --debug-abort
|
||||
else if ( onoff (sw, "-decoration", flag/*ref*/)) { m_decoration = flag; }
|
||||
else if ( onoff (sw, "-dpi-hdr-only", flag/*ref*/)) { m_dpiHdrOnly = flag; }
|
||||
else if ( onoff (sw, "-dump-defines", flag/*ref*/)) { m_dumpDefines = flag; }
|
||||
else if ( onoff (sw, "-dump-tree", flag/*ref*/)) { m_dumpTree = flag ? 3 : 0; } // Also see --dump-treei
|
||||
else if ( onoff (sw, "-exe", flag/*ref*/)) { m_exe = flag; }
|
||||
@@ -730,7 +731,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
||||
case 'd': m_oDedupe = flag; break;
|
||||
case 'm': m_oAssemble = flag; break;
|
||||
case 'e': m_oCase = flag; break;
|
||||
case 'f': m_oFlopGater = flag; break;
|
||||
case 'g': m_oGate = flag; break;
|
||||
case 'i': m_oInline = flag; break;
|
||||
case 'k': m_oSubstConst = flag; break;
|
||||
@@ -1018,6 +1018,9 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
||||
else if (!strcmp(sw, "-gdb")) {
|
||||
// Used only in perl shell
|
||||
}
|
||||
else if (!strcmp(sw, "-rr")) {
|
||||
// Used only in perl shell
|
||||
}
|
||||
else if (!strcmp(sw, "-gdbbt")) {
|
||||
// Used only in perl shell
|
||||
}
|
||||
@@ -1157,7 +1160,7 @@ void V3Options::parseOptsFile(FileLine* fl, const string& filename, bool rel) {
|
||||
whole_file += "\n"; // So string match below is simplified
|
||||
if (inCmt) fl->v3error("Unterminated /* comment inside -f file.");
|
||||
|
||||
fl = new FileLine(filename, 0);
|
||||
fl = new FileLine(filename);
|
||||
|
||||
// Split into argument list and process
|
||||
// Note we don't respect quotes. It seems most simulators dont.
|
||||
@@ -1274,6 +1277,7 @@ V3Options::V3Options() {
|
||||
m_debugPartition = false;
|
||||
m_debugSelfTest = false;
|
||||
m_decoration = true;
|
||||
m_dpiHdrOnly = false;
|
||||
m_dumpDefines = false;
|
||||
m_exe = false;
|
||||
m_ignc = false;
|
||||
@@ -1418,7 +1422,6 @@ void V3Options::optimize(int level) {
|
||||
m_oCombine = flag;
|
||||
m_oConst = flag;
|
||||
m_oExpand = flag;
|
||||
m_oFlopGater = flag;
|
||||
m_oGate = flag;
|
||||
m_oInline = flag;
|
||||
m_oLife = flag;
|
||||
|
||||
+2
-2
@@ -120,6 +120,7 @@ class V3Options {
|
||||
bool m_debugPartition; // main switch: --debug-partition
|
||||
bool m_debugSelfTest; // main switch: --debug-self-test
|
||||
bool m_decoration; // main switch: --decoration
|
||||
bool m_dpiHdrOnly; // main switch: --dpi-hdr-only
|
||||
bool m_dumpDefines; // main switch: --dump-defines
|
||||
bool m_exe; // main switch: --exe
|
||||
bool m_ignc; // main switch: --ignc
|
||||
@@ -204,7 +205,6 @@ class V3Options {
|
||||
bool m_oDedupe; // main switch: -Od: logic deduplication
|
||||
bool m_oAssemble; // main switch: -Om: assign assemble
|
||||
bool m_oExpand; // main switch: -Ox: expansion of C macros
|
||||
bool m_oFlopGater; // main switch: -Of: flop gater detection
|
||||
bool m_oGate; // main switch: -Og: gate wire elimination
|
||||
bool m_oLife; // main switch: -Ol: variable lifetime
|
||||
bool m_oLifePost; // main switch: -Ot: delayed assignment elimination
|
||||
@@ -288,6 +288,7 @@ class V3Options {
|
||||
bool debugPartition() const { return m_debugPartition; }
|
||||
bool debugSelfTest() const { return m_debugSelfTest; }
|
||||
bool decoration() const { return m_decoration; }
|
||||
bool dpiHdrOnly() const { return m_dpiHdrOnly; }
|
||||
bool dumpDefines() const { return m_dumpDefines; }
|
||||
bool exe() const { return m_exe; }
|
||||
bool threadsDpiPure() const { return m_threadsDpiPure; }
|
||||
@@ -377,7 +378,6 @@ class V3Options {
|
||||
bool oDedupe() const { return m_oDedupe; }
|
||||
bool oAssemble() const { return m_oAssemble; }
|
||||
bool oExpand() const { return m_oExpand; }
|
||||
bool oFlopGater() const { return m_oFlopGater; }
|
||||
bool oGate() const { return m_oGate; }
|
||||
bool oDup() const { return oLife(); }
|
||||
bool oLife() const { return m_oLife; }
|
||||
|
||||
+27
-32
@@ -117,12 +117,12 @@ static bool domainsExclusive(const AstSenTree* fromp, const AstSenTree* top);
|
||||
void OrderGraph::loopsVertexCb(V3GraphVertex* vertexp) {
|
||||
if (debug()) cout<<"-Info-Loop: "<<vertexp<<" "<<endl;
|
||||
if (OrderLogicVertex* vvertexp = dynamic_cast<OrderLogicVertex*>(vertexp)) {
|
||||
std::cerr<<vvertexp->nodep()->fileline()->warnMore()
|
||||
std::cerr<<vvertexp->nodep()->fileline()->warnOther()
|
||||
<<" Example path: "
|
||||
<<vvertexp->nodep()->typeName()<<endl;
|
||||
}
|
||||
if (OrderVarVertex* vvertexp = dynamic_cast<OrderVarVertex*>(vertexp)) {
|
||||
std::cerr<<vvertexp->varScp()->fileline()->warnMore()
|
||||
std::cerr<<vvertexp->varScp()->fileline()->warnOther()
|
||||
<<" Example path: "
|
||||
<<vvertexp->varScp()->prettyName()<<endl;
|
||||
}
|
||||
@@ -202,7 +202,7 @@ public:
|
||||
// METHODS
|
||||
OrderVarVertex* newVarUserVertex(V3Graph* graphp, AstScope* scopep,
|
||||
AstVarScope* varscp, WhichVertex type, bool* createdp=NULL) {
|
||||
if (type>=WV_MAX) varscp->v3fatalSrc("Bad case");
|
||||
UASSERT_OBJ(type < WV_MAX, varscp, "Bad case");
|
||||
OrderVarVertex* vertexp = m_vertexp[type];
|
||||
if (!vertexp) {
|
||||
UINFO(6,"New vertex "<<varscp<<endl);
|
||||
@@ -282,7 +282,7 @@ private:
|
||||
if (varrefp->varp()->attrClocker() == AstVarAttrClocker::CLOCKER_YES) {
|
||||
if (m_inClocked) {
|
||||
varrefp->v3warn(CLKDATA, "Clock used as data (on rhs of assignment) in sequential block "
|
||||
<<varrefp->prettyName()<<endl);
|
||||
<<varrefp->prettyNameQ()<<endl);
|
||||
} else {
|
||||
m_hasClk = true;
|
||||
UINFO(5, "node is already marked as clocker "<<varrefp<<endl);
|
||||
@@ -320,7 +320,7 @@ private:
|
||||
if (m_inClocked) {
|
||||
nodep->v3warn(CLKDATA,
|
||||
"Clock used as data (on rhs of assignment) in sequential block "
|
||||
<<nodep->prettyName());
|
||||
<<nodep->prettyNameQ());
|
||||
} else {
|
||||
m_hasClk = true;
|
||||
m_childClkWidth = nodep->width(); // Pass up
|
||||
@@ -735,7 +735,7 @@ private:
|
||||
UINFO(4," STMT "<<nodep<<endl);
|
||||
//VV***** We reset user4p()
|
||||
AstNode::user4ClearTree();
|
||||
if (!m_activep || !m_activep->sensesp()) nodep->v3fatalSrc("NULL");
|
||||
UASSERT_OBJ(m_activep && m_activep->sensesp(), nodep, "NULL");
|
||||
// If inside combo logic, ignore the domain, we'll assign one based on interconnect
|
||||
AstSenTree* startDomainp = m_activep->sensesp();
|
||||
if (startDomainp->hasCombo()) startDomainp=NULL;
|
||||
@@ -833,10 +833,10 @@ private:
|
||||
//
|
||||
if (vertexp->isClock()) {
|
||||
// Seems obvious; no warning yet
|
||||
//nodep->v3warn(GENCLK,"Signal unoptimizable: Generated clock: "<<nodep->prettyName());
|
||||
//nodep->v3warn(GENCLK, "Signal unoptimizable: Generated clock: "<<nodep->prettyNameQ());
|
||||
} else if (nodep->varp()->isSigPublic()) {
|
||||
nodep->v3warn(UNOPT, "Signal unoptimizable: Feedback to public clock or circular logic: "
|
||||
<<nodep->prettyName());
|
||||
<<nodep->prettyNameQ());
|
||||
if (!nodep->fileline()->warnIsOff(V3ErrorCode::UNOPT)) {
|
||||
nodep->fileline()->modifyWarnOff(V3ErrorCode::UNOPT, true); // Complain just once
|
||||
// Give the user an example.
|
||||
@@ -850,7 +850,7 @@ private:
|
||||
// it was needed, that aren't any more
|
||||
// First v3warn not inside warnIsOff so we can see the suppressions with --debug
|
||||
nodep->v3warn(UNOPTFLAT, "Signal unoptimizable: Feedback to clock or circular logic: "
|
||||
<<nodep->prettyName());
|
||||
<<nodep->prettyNameQ());
|
||||
if (!nodep->fileline()->warnIsOff(V3ErrorCode::UNOPTFLAT)) {
|
||||
nodep->fileline()->modifyWarnOff(V3ErrorCode::UNOPTFLAT, true); // Complain just once
|
||||
// Give the user an example.
|
||||
@@ -963,7 +963,7 @@ private:
|
||||
}
|
||||
virtual void visit(AstTopScope* nodep) {
|
||||
// Process the last thing we're finishing
|
||||
if (m_topScopep) nodep->v3fatalSrc("Only one topscope should ever be created");
|
||||
UASSERT_OBJ(!m_topScopep, nodep, "Only one topscope should ever be created");
|
||||
UINFO(2," Loading tree...\n");
|
||||
//VV***** We reset userp()
|
||||
AstNode::user1ClearTree();
|
||||
@@ -1022,7 +1022,8 @@ private:
|
||||
m_activeSenVxp = NULL;
|
||||
m_inClocked = nodep->hasClocked();
|
||||
// Grab the sensitivity list
|
||||
if (nodep->sensesStorep()) nodep->v3fatalSrc("Senses should have been activeTop'ed to be global!");
|
||||
UASSERT_OBJ(!nodep->sensesStorep(), nodep,
|
||||
"Senses should have been activeTop'ed to be global!");
|
||||
iterate(nodep->sensesp());
|
||||
// Collect statements under it
|
||||
iterateChildren(nodep);
|
||||
@@ -1040,15 +1041,15 @@ private:
|
||||
virtual void visit(AstNodeVarRef* nodep) {
|
||||
if (m_scopep) {
|
||||
AstVarScope* varscp = nodep->varScopep();
|
||||
if (!varscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp");
|
||||
UASSERT_OBJ(varscp, nodep, "Var didn't get varscoped in V3Scope.cpp");
|
||||
if (m_inSenTree) {
|
||||
// Add CLOCK dependency... This is a root of the tree we'll trace
|
||||
if (nodep->lvalue()) nodep->v3fatalSrc("How can a sensitivity be setting a var?");
|
||||
UASSERT_OBJ(!nodep->lvalue(), nodep, "How can a sensitivity be setting a var?");
|
||||
OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD);
|
||||
varVxp->isClock(true);
|
||||
new OrderEdge(&m_graph, varVxp, m_activeSenVxp, WEIGHT_MEDIUM);
|
||||
} else {
|
||||
if (!m_logicVxp) nodep->v3fatalSrc("Var ref not under a logic block");
|
||||
UASSERT_OBJ(m_logicVxp, nodep, "Var ref not under a logic block");
|
||||
// What new directions is this used
|
||||
// We don't want to add extra edges if the logic block has many usages of same var
|
||||
bool gen = false;
|
||||
@@ -1307,8 +1308,8 @@ static bool domainsExclusive(const AstSenTree* fromp, const AstSenTree* top) {
|
||||
const AstSenItem* toSenListp = VN_CAST(top->sensesp(), SenItem);
|
||||
// If clk gating is ever reenabled, we may need to update this to handle
|
||||
// AstSenGate also.
|
||||
if (!fromSenListp) fromp->v3fatalSrc("sensitivity list item is not an AstSenItem");
|
||||
if (!toSenListp) top->v3fatalSrc("sensitivity list item is not an AstSenItem");
|
||||
UASSERT_OBJ(fromSenListp, fromp, "sensitivity list item is not an AstSenItem");
|
||||
UASSERT_OBJ(toSenListp, top, "sensitivity list item is not an AstSenItem");
|
||||
|
||||
if (fromSenListp->nextp()) return false;
|
||||
if (toSenListp->nextp()) return false;
|
||||
@@ -1337,9 +1338,8 @@ inline void OrderMoveDomScope::ready(OrderVisitor* ovp) {
|
||||
|
||||
// Mark one vertex as finished, remove from ready list if done
|
||||
inline void OrderMoveDomScope::movedVertex(OrderVisitor* ovp, OrderMoveVertex* vertexp) {
|
||||
if (!m_onReadyList) {
|
||||
vertexp->v3fatalSrc("Moving vertex from ready when nothing was on que as ready.");
|
||||
}
|
||||
UASSERT_OBJ(m_onReadyList, vertexp,
|
||||
"Moving vertex from ready when nothing was on que as ready.");
|
||||
if (m_readyVertices.empty()) { // Else more work to get to later
|
||||
m_onReadyList = false;
|
||||
m_readyDomScopeE.unlink(ovp->m_pomReadyDomScope, this);
|
||||
@@ -1406,9 +1406,8 @@ void OrderVisitor::processInputsOutIterate(OrderEitherVertex* vertexp, VertexVec
|
||||
// First make sure input path is fully recursed
|
||||
processInputsInIterate(vertexp, todoVec);
|
||||
// Propagate PrimaryIn through simple assignments
|
||||
if (!vertexp->isFromInput()) {
|
||||
vertexp->v3fatalSrc("processInputsOutIterate only for input marked vertexes");
|
||||
}
|
||||
UASSERT_OBJ(vertexp->isFromInput(), vertexp,
|
||||
"processInputsOutIterate only for input marked vertexes");
|
||||
vertexp->user(3); // out-edges processed
|
||||
|
||||
{
|
||||
@@ -1460,7 +1459,7 @@ void OrderVisitor::processCircular() {
|
||||
for (V3GraphEdge* edgep = vvertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
||||
if (edgep->weight()==0) { // was cut
|
||||
OrderEdge* oedgep = dynamic_cast<OrderEdge*>(edgep);
|
||||
if (!oedgep) vvertexp->varScp()->v3fatalSrc("Cuttable edge not of proper type");
|
||||
UASSERT_OBJ(oedgep, vvertexp->varScp(), "Cuttable edge not of proper type");
|
||||
UINFO(6," CutCircularO: "<<vvertexp->name()<<endl);
|
||||
nodeMarkCircular(vvertexp, oedgep);
|
||||
}
|
||||
@@ -1468,7 +1467,7 @@ void OrderVisitor::processCircular() {
|
||||
for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
if (edgep->weight()==0) { // was cut
|
||||
OrderEdge* oedgep = dynamic_cast<OrderEdge*>(edgep);
|
||||
if (!oedgep) vvertexp->varScp()->v3fatalSrc("Cuttable edge not of proper type");
|
||||
UASSERT_OBJ(oedgep, vvertexp->varScp(), "Cuttable edge not of proper type");
|
||||
UINFO(6," CutCircularI: "<<vvertexp->name()<<endl);
|
||||
nodeMarkCircular(vvertexp, oedgep);
|
||||
}
|
||||
@@ -1563,7 +1562,8 @@ void OrderVisitor::processDomainsIterate(OrderEitherVertex* vertexp) {
|
||||
}
|
||||
AstSenTree* newtreep = domainp->cloneTree(false);
|
||||
AstNodeSenItem* newtree2p = fromVertexp->domainp()->sensesp()->cloneTree(true);
|
||||
if (!newtree2p) fromVertexp->domainp()->v3fatalSrc("No senitem found under clocked domain");
|
||||
UASSERT_OBJ(newtree2p, fromVertexp->domainp(),
|
||||
"No senitem found under clocked domain");
|
||||
newtreep->addSensesp(newtree2p);
|
||||
newtree2p = NULL; // Below edit may replace it
|
||||
V3Const::constifyExpensiveEdit(newtreep); // Remove duplicates
|
||||
@@ -1759,9 +1759,7 @@ void OrderVisitor::processMoveDoneOne(OrderMoveVertex* vertexp) {
|
||||
|
||||
void OrderVisitor::processMoveOne(OrderMoveVertex* vertexp,
|
||||
OrderMoveDomScope* domScopep, int level) {
|
||||
if (vertexp->domScopep() != domScopep) {
|
||||
vertexp->v3fatalSrc("Domain mismatch; list misbuilt?");
|
||||
}
|
||||
UASSERT_OBJ(vertexp->domScopep() == domScopep, vertexp, "Domain mismatch; list misbuilt?");
|
||||
const OrderLogicVertex* lvertexp = vertexp->logicp();
|
||||
const AstScope* scopep = lvertexp->scopep();
|
||||
UINFO(5," POSmove l"<<std::setw(3)<<level<<" d="<<cvtToHex(lvertexp->domainp())
|
||||
@@ -1781,10 +1779,7 @@ AstActive* OrderVisitor::processMoveOneLogic(const OrderLogicVertex* lvertexp,
|
||||
AstNode* nodep = lvertexp->nodep();
|
||||
AstNodeModule* modp = VN_CAST(scopep->user1p(), NodeModule); // Stashed by visitor func
|
||||
UASSERT(modp, "NULL");
|
||||
if (VN_IS(nodep, UntilStable)) {
|
||||
nodep->v3fatalSrc("Not implemented");
|
||||
}
|
||||
else if (VN_IS(nodep, SenTree)) {
|
||||
if (VN_IS(nodep, SenTree)) {
|
||||
// Just ignore sensitivities, we'll deal with them when we move statements that need them
|
||||
}
|
||||
else { // Normal logic
|
||||
|
||||
+51
-29
@@ -77,6 +77,7 @@ private:
|
||||
// AstVar::user4() // int Global parameter number (for naming new module)
|
||||
// // (0=not processed, 1=iterated, but no number,
|
||||
// // 65+ parameter numbered)
|
||||
// AstCell::user5p() // string* Generate portion of hierarchical name
|
||||
AstUser4InUse m_inuser4;
|
||||
AstUser5InUse m_inuser5;
|
||||
// User1/2/3 used by constant function simulations
|
||||
@@ -115,6 +116,8 @@ private:
|
||||
|
||||
UnrollStateful m_unroller; // Loop unroller
|
||||
|
||||
string m_generateHierName; // Generate portion of hierarchical name
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
@@ -204,16 +207,14 @@ private:
|
||||
// Find it in the clone structure
|
||||
//UINFO(8,"Clone find 0x"<<hex<<(uint32_t)pinp->modVarp()<<endl);
|
||||
CloneMap::iterator cloneiter = clonemapp->find(pinp->modVarp());
|
||||
if (cloneiter == clonemapp->end()) {
|
||||
pinp->v3fatalSrc("Couldn't find pin in clone list");
|
||||
}
|
||||
UASSERT_OBJ(cloneiter != clonemapp->end(), pinp,
|
||||
"Couldn't find pin in clone list");
|
||||
pinp->modVarp(VN_CAST(cloneiter->second, Var));
|
||||
}
|
||||
else if (pinp->modPTypep()) {
|
||||
CloneMap::iterator cloneiter = clonemapp->find(pinp->modPTypep());
|
||||
if (cloneiter == clonemapp->end()) {
|
||||
pinp->v3fatalSrc("Couldn't find pin in clone list");
|
||||
}
|
||||
UASSERT_OBJ(cloneiter != clonemapp->end(), pinp,
|
||||
"Couldn't find pin in clone list");
|
||||
pinp->modPTypep(VN_CAST(cloneiter->second, ParamTypeDType));
|
||||
}
|
||||
else {
|
||||
@@ -221,7 +222,7 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
void visitCell(AstCell* nodep);
|
||||
void visitCell(AstCell* nodep, const string& hierName);
|
||||
void visitModules() {
|
||||
// Loop on all modules left to process
|
||||
// Hitting a cell adds to the appropriate level of this level-sorted list,
|
||||
@@ -233,6 +234,7 @@ private:
|
||||
if (!nodep->user5SetOnce()) { // Process once; note clone() must clear so we do it again
|
||||
m_modp = nodep;
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
if (m_modp->hierName().empty()) m_modp->hierName(m_modp->origName());
|
||||
iterateChildren(nodep);
|
||||
// Note above iterate may add to m_todoModps
|
||||
//
|
||||
@@ -242,10 +244,21 @@ private:
|
||||
AstCell* nodep = *it;
|
||||
if ((nonIf==0 && VN_IS(nodep->modp(), Iface))
|
||||
|| (nonIf==1 && !VN_IS(nodep->modp(), Iface))) {
|
||||
visitCell(nodep);
|
||||
string fullName (m_modp->hierName());
|
||||
if (string* genHierNamep = (string *) nodep->user5p()) {
|
||||
fullName += *genHierNamep;
|
||||
}
|
||||
visitCell(nodep, fullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (CellList::iterator it=m_cellps.begin(); it!=m_cellps.end(); ++it) {
|
||||
AstCell* cellp = *it;
|
||||
if (string* genHierNamep = (string *) cellp->user5p()) {
|
||||
cellp->user5p(NULL);
|
||||
delete genHierNamep; VL_DANGLING(genHierNamep);
|
||||
}
|
||||
}
|
||||
m_cellps.clear();
|
||||
m_modp = NULL;
|
||||
}
|
||||
@@ -267,6 +280,7 @@ private:
|
||||
|| VN_IS(nodep, Package)) { // Likewise haven't done wrapTopPackages yet
|
||||
// Add request to END of modules left to process
|
||||
m_todoModps.insert(make_pair(nodep->level(), nodep));
|
||||
m_generateHierName = "";
|
||||
visitModules();
|
||||
} else if (nodep->user5()) {
|
||||
UINFO(4," MOD-done "<<nodep<<endl); // Already did it
|
||||
@@ -276,6 +290,8 @@ private:
|
||||
}
|
||||
virtual void visit(AstCell* nodep) {
|
||||
// Must do ifaces first, so push to list and do in proper order
|
||||
string* genHierNamep = new string(m_generateHierName);
|
||||
nodep->user5p(genHierNamep);
|
||||
m_cellps.push_back(nodep);
|
||||
}
|
||||
|
||||
@@ -287,7 +303,7 @@ private:
|
||||
if (!nodep->valuep()) {
|
||||
nodep->v3error("Parameter without initial value is never given value"
|
||||
<<" (IEEE 1800-2017 6.20.1): "
|
||||
<<nodep->prettyName());
|
||||
<<nodep->prettyNameQ());
|
||||
} else {
|
||||
V3Const::constifyParamsEdit(nodep); // The variable, not just the var->init()
|
||||
if (!VN_IS(nodep->valuep(), Const)) { // Complex init, like an array
|
||||
@@ -318,7 +334,7 @@ private:
|
||||
return true;
|
||||
} else if (AstPin* pinp = VN_CAST(candp, Pin)) {
|
||||
UINFO(9,"Found interface parameter: "<<pinp<<endl);
|
||||
if (!pinp->exprp()) pinp->v3fatalSrc("Interface parameter pin missing expression");
|
||||
UASSERT_OBJ(pinp->exprp(), pinp, "Interface parameter pin missing expression");
|
||||
nodep->replaceWith(pinp->exprp()->cloneTree(false)); VL_DANGLING(nodep);
|
||||
return true;
|
||||
}
|
||||
@@ -402,7 +418,7 @@ private:
|
||||
nodep->name()+"__BRA__"+index+"__KET__");
|
||||
} else {
|
||||
nodep->v3error("Could not expand constant selection inside dotted reference: "
|
||||
<<nodep->selp()->prettyName());
|
||||
<<nodep->selp()->prettyNameQ());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -455,7 +471,7 @@ private:
|
||||
virtual void visit(AstBegin* nodep) {
|
||||
if (nodep->genforp()) {
|
||||
AstGenFor* forp = VN_CAST(nodep->genforp(), GenFor);
|
||||
if (!forp) nodep->v3fatalSrc("Non-GENFOR under generate-for BEGIN");
|
||||
UASSERT_OBJ(forp, nodep, "Non-GENFOR under generate-for BEGIN");
|
||||
// We should have a GENFOR under here. We will be replacing the begin,
|
||||
// so process here rather than at the generate to avoid iteration problems
|
||||
UINFO(9," BEGIN "<<nodep<<endl);
|
||||
@@ -479,7 +495,10 @@ private:
|
||||
// Note this clears nodep->genforp(), so begin is no longer special
|
||||
}
|
||||
} else {
|
||||
string rootHierName(m_generateHierName);
|
||||
m_generateHierName += "." + nodep->prettyName();
|
||||
iterateChildren(nodep);
|
||||
m_generateHierName = rootHierName;
|
||||
}
|
||||
}
|
||||
virtual void visit(AstGenFor* nodep) {
|
||||
@@ -557,10 +576,10 @@ public:
|
||||
//----------------------------------------------------------------------
|
||||
// VISITs
|
||||
|
||||
void ParamVisitor::visitCell(AstCell* nodep) {
|
||||
void ParamVisitor::visitCell(AstCell* nodep, const string& hierName) {
|
||||
// Cell: Check for parameters in the instantiation.
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->modp()) nodep->v3fatalSrc("Not linked?");
|
||||
UASSERT_OBJ(nodep->modp(), nodep, "Not linked?");
|
||||
// We always run this, even if no parameters, as need to look for interfaces,
|
||||
// and remove any recursive references
|
||||
{
|
||||
@@ -570,6 +589,7 @@ void ParamVisitor::visitCell(AstCell* nodep) {
|
||||
// Evaluate all module constants
|
||||
V3Const::constifyParamsEdit(nodep);
|
||||
AstNodeModule* srcModp = nodep->modp();
|
||||
srcModp->hierName(hierName + "." + nodep->name());
|
||||
|
||||
// Make sure constification worked
|
||||
// Must be a separate loop, as constant conversion may have changed some pointers.
|
||||
@@ -584,7 +604,7 @@ void ParamVisitor::visitCell(AstCell* nodep) {
|
||||
if (AstVar* modvarp = pinp->modVarp()) {
|
||||
if (!modvarp->isGParam()) {
|
||||
pinp->v3error("Attempted parameter setting of non-parameter: Param "
|
||||
<<pinp->prettyName()<<" of "<<nodep->prettyName());
|
||||
<<pinp->prettyNameQ()<<" of "<<nodep->prettyNameQ());
|
||||
} else if (VN_IS(pinp->exprp(), InitArray)
|
||||
&& VN_IS(modvarp->subDTypep(), UnpackArrayDType)) {
|
||||
// Array assigned to array
|
||||
@@ -597,7 +617,7 @@ void ParamVisitor::visitCell(AstCell* nodep) {
|
||||
if (!exprp) {
|
||||
//if (debug()) pinp->dumpTree(cout, "error:");
|
||||
pinp->v3error("Can't convert defparam value to constant: Param "
|
||||
<<pinp->name()<<" of "<<nodep->prettyName());
|
||||
<<pinp->prettyNameQ()<<" of "<<nodep->prettyNameQ());
|
||||
pinp->exprp()->replaceWith(
|
||||
new AstConst(pinp->fileline(),
|
||||
AstConst::WidthedValue(), modvarp->width(), 0));
|
||||
@@ -622,10 +642,10 @@ void ParamVisitor::visitCell(AstCell* nodep) {
|
||||
AstNodeDType* origp = modvarp->subDTypep();
|
||||
if (!exprp) {
|
||||
pinp->v3error("Parameter type pin value isn't a type: Param "
|
||||
<<pinp->prettyName()<<" of "<<nodep->prettyName());
|
||||
<<pinp->prettyNameQ()<<" of "<<nodep->prettyNameQ());
|
||||
} else if (!origp) {
|
||||
pinp->v3error("Parameter type variable isn't a type: Param "
|
||||
<<modvarp->prettyName());
|
||||
<<modvarp->prettyNameQ());
|
||||
} else {
|
||||
UINFO(9,"Parameter type assignment expr="<<exprp<<" to "<<origp<<endl);
|
||||
if (origp && exprp->sameTree(origp)) {
|
||||
@@ -640,7 +660,7 @@ void ParamVisitor::visitCell(AstCell* nodep) {
|
||||
}
|
||||
} else {
|
||||
pinp->v3error("Parameter not found in sub-module: Param "
|
||||
<<pinp->prettyName()<<" of "<<nodep->prettyName());
|
||||
<<pinp->prettyNameQ()<<" of "<<nodep->prettyNameQ());
|
||||
}
|
||||
}
|
||||
IfaceRefRefs ifaceRefRefs;
|
||||
@@ -682,11 +702,11 @@ void ParamVisitor::visitCell(AstCell* nodep) {
|
||||
UINFO(9," portIfaceRef "<<portIrefp<<endl);
|
||||
|
||||
if (!portIrefp) {
|
||||
pinp->v3error("Interface port '"<<modvarp->prettyName()
|
||||
<<"' is not an interface " << modvarp);
|
||||
pinp->v3error("Interface port "<<modvarp->prettyNameQ()
|
||||
<<" is not an interface " << modvarp);
|
||||
} else if (!pinIrefp) {
|
||||
pinp->v3error("Interface port '"<<modvarp->prettyName()
|
||||
<<"' is not connected to interface/modport pin expression");
|
||||
pinp->v3error("Interface port "<<modvarp->prettyNameQ()
|
||||
<<" is not connected to interface/modport pin expression");
|
||||
} else {
|
||||
UINFO(9," pinIfaceRef "<<pinIrefp<<endl);
|
||||
if (portIrefp->ifaceViaCellp() != pinIrefp->ifaceViaCellp()) {
|
||||
@@ -698,10 +718,11 @@ void ParamVisitor::visitCell(AstCell* nodep) {
|
||||
if (portIrefp->ifacep() != pinIrefp->ifacep()
|
||||
// Might be different only due to param cloning, so check names too
|
||||
&& portIrefp->ifaceName() != pinIrefp->ifaceName()) {
|
||||
pinp->v3error("Port '"<<pinp->prettyName()<<"' expects '"
|
||||
<<AstNode::prettyName(portIrefp->ifaceName())
|
||||
<<"' interface but pin connects '"
|
||||
<<AstNode::prettyName(pinIrefp->ifaceName())<<"' interface");
|
||||
pinp->v3error("Port "<<pinp->prettyNameQ()<<" expects "
|
||||
<<AstNode::prettyNameQ(portIrefp->ifaceName())
|
||||
<<" interface but pin connects "
|
||||
<<AstNode::prettyNameQ(pinIrefp->ifaceName())
|
||||
<<" interface");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -777,7 +798,8 @@ void ParamVisitor::visitCell(AstCell* nodep) {
|
||||
AstIfaceRefDType* cloneIrefp = portIrefp->clonep();
|
||||
UINFO(8," IfaceOld "<<portIrefp<<endl);
|
||||
UINFO(8," IfaceTo "<<pinIrefp<<endl);
|
||||
if (!cloneIrefp) portIrefp->v3fatalSrc("parameter clone didn't hit AstIfaceRefDType");
|
||||
UASSERT_OBJ(cloneIrefp, portIrefp,
|
||||
"parameter clone didn't hit AstIfaceRefDType");
|
||||
UINFO(8," IfaceClo "<<cloneIrefp<<endl);
|
||||
cloneIrefp->ifacep(pinIrefp->ifaceViaCellp());
|
||||
UINFO(8," IfaceNew "<<cloneIrefp<<endl);
|
||||
@@ -796,7 +818,7 @@ void ParamVisitor::visitCell(AstCell* nodep) {
|
||||
}
|
||||
else if (AstParamTypeDType* modptp = pinp->modPTypep()) {
|
||||
AstNodeDType* dtypep = VN_CAST(pinp->exprp(), NodeDType);
|
||||
if (!dtypep) pinp->v3fatalSrc("unlinked param dtype");
|
||||
UASSERT_OBJ(dtypep, pinp, "unlinked param dtype");
|
||||
if (modptp->childDTypep()) {
|
||||
pushDeletep(modptp->childDTypep()->unlinkFrBack());
|
||||
}
|
||||
|
||||
@@ -109,9 +109,8 @@ AstNodeDType* V3ParseGrammar::createArray(AstNodeDType* basep,
|
||||
if (prevp) nrangep->unlinkFrBack();
|
||||
AstRange* rangep = VN_CAST(nrangep, Range);
|
||||
if (!rangep) {
|
||||
if (!VN_IS(nrangep, UnsizedRange)) {
|
||||
nrangep->v3fatalSrc("Expected range or unsized range");
|
||||
}
|
||||
UASSERT_OBJ(VN_IS(nrangep, UnsizedRange), nrangep,
|
||||
"Expected range or unsized range");
|
||||
arrayp = new AstUnsizedArrayDType
|
||||
(nrangep->fileline(), VFlagChildDType(), arrayp);
|
||||
} else if (isPacked) {
|
||||
@@ -166,6 +165,8 @@ AstVar* V3ParseGrammar::createVariable(FileLine* fileline, string name,
|
||||
|
||||
AstVar* nodep = new AstVar(fileline, type, name, VFlagChildDType(), arrayDTypep);
|
||||
nodep->addAttrsp(attrsp);
|
||||
nodep->ansi(m_pinAnsi);
|
||||
nodep->declTyped(m_varDeclTyped);
|
||||
if (GRAMMARP->m_varDecl != AstVarType::UNKNOWN) nodep->combineType(GRAMMARP->m_varDecl);
|
||||
if (GRAMMARP->m_varIO != VDirection::NONE) {
|
||||
nodep->declDirection(GRAMMARP->m_varIO);
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "V3File.h"
|
||||
#include "V3ParseImp.h"
|
||||
#include "V3PreShell.h"
|
||||
#include "V3LanguageWords.h"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <fstream>
|
||||
@@ -74,8 +75,16 @@ V3ParseImp::~V3ParseImp() {
|
||||
|
||||
void V3ParseImp::ppline(const char* textp) {
|
||||
// Handle `line directive
|
||||
FileLine* prevFl = copyOrSameFileLine();
|
||||
int enterExit;
|
||||
fileline()->lineDirective(textp, enterExit/*ref*/);
|
||||
if (enterExit == 1) { // Enter
|
||||
fileline()->parent(prevFl);
|
||||
} else if (enterExit == 2) { // Exit
|
||||
FileLine* upFl = fileline()->parent();
|
||||
if (upFl) upFl = upFl->parent();
|
||||
if (upFl) fileline()->parent(upFl);
|
||||
}
|
||||
}
|
||||
|
||||
void V3ParseImp::verilatorCmtLintSave() {
|
||||
@@ -124,6 +133,21 @@ void V3ParseImp::verilatorCmtBad(const char* textp) {
|
||||
}
|
||||
}
|
||||
|
||||
void V3ParseImp::errorPreprocDirective(const char* textp) {
|
||||
// Find all `preprocessor spelling candidates
|
||||
// Can't make this static as might get more defines later when read cells
|
||||
VSpellCheck speller;
|
||||
V3LanguageWords words;
|
||||
for (V3LanguageWords::const_iterator it = words.begin(); it != words.end(); ++it) {
|
||||
string ppDirective = it->first;
|
||||
if (ppDirective[0] == '`') speller.pushCandidate(ppDirective);
|
||||
}
|
||||
V3PreShell::candidateDefines(&speller);
|
||||
string suggest = speller.bestCandidateMsg(textp);
|
||||
fileline()->v3error("Define or directive not defined: '"<<textp<<"'\n"
|
||||
<<(suggest.empty() ? "" : fileline()->warnMore()+suggest));
|
||||
}
|
||||
|
||||
void V3ParseImp::tag(const char* text) {
|
||||
if (m_tagNodep) {
|
||||
string tmp = text + strlen("/*verilator tag ");
|
||||
@@ -201,7 +225,9 @@ void V3ParseImp::parseFile(FileLine* fileline, const string& modfilename, bool i
|
||||
string modname = V3Os::filenameNonExt(modfilename);
|
||||
|
||||
UINFO(2,__FUNCTION__<<": "<<modname<<(inLibrary?" [LIB]":"")<<endl);
|
||||
VFileContent* contentp = new VFileContent;
|
||||
m_fileline = new FileLine(fileline);
|
||||
m_fileline->newContent();
|
||||
m_inLibrary = inLibrary;
|
||||
|
||||
// Preprocess into m_ppBuffer
|
||||
|
||||
+5
-1
@@ -148,6 +148,7 @@ public:
|
||||
void verilatorCmtLintSave();
|
||||
void verilatorCmtLintRestore();
|
||||
void verilatorCmtBad(const char* text);
|
||||
void errorPreprocDirective(const char* textp);
|
||||
void tag(const char* text);
|
||||
void tagNodep(AstNode* nodep) { m_tagNodep = nodep; }
|
||||
AstNode* tagNodep() const { return m_tagNodep;}
|
||||
@@ -159,7 +160,10 @@ public:
|
||||
int lastVerilogState() const { return m_lastVerilogState; }
|
||||
static const char* tokenName(int tok);
|
||||
|
||||
void ppPushText(const string& text) { m_ppBuffers.push_back(text); }
|
||||
void ppPushText(const string& text) {
|
||||
m_ppBuffers.push_back(text);
|
||||
if (fileline()->contentp()) fileline()->contentp()->pushText(text);
|
||||
}
|
||||
size_t ppInputToLex(char* buf, size_t max_size);
|
||||
|
||||
static V3ParseImp* parsep() { return s_parsep; }
|
||||
|
||||
@@ -66,6 +66,7 @@ void V3ParseImp::unputString(const char* textp, size_t length) {
|
||||
|
||||
int V3ParseImp::yylexReadTok() {
|
||||
// Call yylex() remembering last non-whitespace token
|
||||
parsep()->fileline()->startToken();
|
||||
int token = parsep()->m_lexerp->yylex();
|
||||
m_prevLexToken = token; // Save so can find '#' to parse following number
|
||||
return token;
|
||||
|
||||
+8
-10
@@ -60,7 +60,7 @@ public:
|
||||
private:
|
||||
// METHODS
|
||||
static VSymEnt* getTable(AstNode* nodep) {
|
||||
if (!nodep->user4p()) nodep->v3fatalSrc("Current symtable not found");
|
||||
UASSERT_OBJ(nodep->user4p(), nodep, "Current symtable not found");
|
||||
return nodep->user4u().toSymEnt();
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ public:
|
||||
return;
|
||||
}
|
||||
m_sympStack.pop_back();
|
||||
if (m_sympStack.empty()) { nodep->v3fatalSrc("symbol stack underflow"); return; }
|
||||
UASSERT_OBJ(!m_sympStack.empty(), nodep, "symbol stack underflow");
|
||||
m_symCurrentp = m_sympStack.back();
|
||||
}
|
||||
void showUpward() {
|
||||
@@ -139,10 +139,9 @@ public:
|
||||
void importItem(AstNode* packagep, const string& id_or_star) {
|
||||
// Import from package::id_or_star to this
|
||||
VSymEnt* symp = getTable(packagep);
|
||||
if (!symp) { // Internal problem, because we earlier found pkg to label it an ID__aPACKAGE
|
||||
packagep->v3fatalSrc("Import package not found");
|
||||
return;
|
||||
}
|
||||
UASSERT_OBJ(symp, packagep,
|
||||
// Internal problem, because we earlier found pkg to label it an ID__aPACKAGE
|
||||
"Import package not found");
|
||||
// Walk old sym table and reinsert into current table
|
||||
// We let V3LinkDot report the error instead of us
|
||||
symCurrentp()->importFromPackage(&m_syms, symp, id_or_star);
|
||||
@@ -150,10 +149,9 @@ public:
|
||||
void exportItem(AstNode* packagep, const string& id_or_star) {
|
||||
// Export from this the remote package::id_or_star
|
||||
VSymEnt* symp = getTable(packagep);
|
||||
if (!symp) { // Internal problem, because we earlier found pkg to label it an ID__aPACKAGE
|
||||
packagep->v3fatalSrc("Export package not found");
|
||||
return;
|
||||
}
|
||||
UASSERT_OBJ(symp, packagep,
|
||||
// Internal problem, because we earlier found pkg to label it an ID__aPACKAGE
|
||||
"Export package not found");
|
||||
symCurrentp()->exportFromPackage(&m_syms, symp, id_or_star);
|
||||
}
|
||||
void exportStarStar(AstNode* packagep) {
|
||||
|
||||
+26
-34
@@ -314,7 +314,7 @@ private:
|
||||
// important property of PartPropagateCp which allows it to be far
|
||||
// faster than a recursive algorithm on some graphs.
|
||||
CpMap::iterator it = m_seen.find(vxp);
|
||||
if (it != m_seen.end()) vxp->v3fatalSrc("Set CP on node twice");
|
||||
UASSERT_OBJ(it == m_seen.end(), vxp, "Set CP on node twice");
|
||||
m_seen[vxp] = cost;
|
||||
}
|
||||
uint32_t critPathCost(V3GraphVertex* vxp, GraphWay way) const {
|
||||
@@ -452,7 +452,7 @@ public:
|
||||
// This is mtaskp's relative with longest !wayward inclusive CP:
|
||||
EdgeSet::reverse_iterator edgeIt = edges.rbegin();
|
||||
uint32_t edgeCp = (*edgeIt).value();
|
||||
if (edgeCp != cp) vxp->v3fatalSrc("CP doesn't match longest wayward edge");
|
||||
UASSERT_OBJ(edgeCp == cp, vxp, "CP doesn't match longest wayward edge");
|
||||
}
|
||||
private:
|
||||
VL_UNCOPYABLE(CpCostAccessor);
|
||||
@@ -988,12 +988,10 @@ static void partInitHalfCriticalPaths(GraphWay way, V3Graph* mtasksp, bool check
|
||||
edgep; edgep = edgep->nextp(rev)) {
|
||||
// Run a few asserts on the initial mtask graph,
|
||||
// while we're iterating through...
|
||||
if (edgep->weight() == 0) {
|
||||
mtaskp->v3fatalSrc("Should be no cut edges in mtasks graph");
|
||||
}
|
||||
if (relatives.find(edgep->furtherp(rev)) != relatives.end()) {
|
||||
mtaskp->v3fatalSrc("Should be no redundant edges in mtasks graph");
|
||||
}
|
||||
UASSERT_OBJ(edgep->weight() != 0, mtaskp,
|
||||
"Should be no cut edges in mtasks graph");
|
||||
UASSERT_OBJ(relatives.find(edgep->furtherp(rev)) == relatives.end(), mtaskp,
|
||||
"Should be no redundant edges in mtasks graph");
|
||||
relatives.insert(edgep->furtherp(rev));
|
||||
|
||||
LogicMTask* relativep
|
||||
@@ -1187,9 +1185,8 @@ public:
|
||||
for (V3GraphEdge* edgep = itp->outBeginp(); edgep;
|
||||
edgep=edgep->outNextp()) {
|
||||
m_sb.addElem(MTaskEdge::cast(edgep));
|
||||
if (neighbors.find(edgep->top()) != neighbors.end()) {
|
||||
itp->v3fatalSrc("Redundant edge found in input to PartContraction()");
|
||||
}
|
||||
UASSERT_OBJ(neighbors.find(edgep->top()) == neighbors.end(), itp,
|
||||
"Redundant edge found in input to PartContraction()");
|
||||
neighbors.insert(edgep->top());
|
||||
}
|
||||
siblingPairFromRelatives(GraphWay::REVERSE, itp, true);
|
||||
@@ -1352,12 +1349,12 @@ private:
|
||||
LogicMTask* otherp = (pairp->bp() == mtaskp) ?
|
||||
pairp->ap() : pairp->bp();
|
||||
size_t erased = m_mtask2sibs[otherp].erase(pairp);
|
||||
if (erased <= 0) otherp->v3fatalSrc("Expected existing mtask");
|
||||
UASSERT_OBJ(erased > 0, otherp, "Expected existing mtask");
|
||||
erased = m_pairs.erase(*pairp);
|
||||
if (erased <= 0) mtaskp->v3fatalSrc("Expected existing mtask");
|
||||
UASSERT_OBJ(erased > 0, mtaskp, "Expected existing mtask");
|
||||
}
|
||||
size_t erased = m_mtask2sibs.erase(mtaskp);
|
||||
if (erased <= 0) mtaskp->v3fatalSrc("Expected existing mtask");
|
||||
UASSERT_OBJ(erased > 0, mtaskp, "Expected existing mtask");
|
||||
}
|
||||
|
||||
void contract(MergeCandidate* mergeCanp) {
|
||||
@@ -1571,24 +1568,21 @@ private:
|
||||
} else if (m_slowAsserts) {
|
||||
// It's fine if we already have this SiblingMC, we may have
|
||||
// created it earlier. Just confirm that we have associated data.
|
||||
if (m_mtask2sibs.find(ap) == m_mtask2sibs.end()) {
|
||||
ap->v3fatalSrc("Sibling not found");
|
||||
}
|
||||
if (m_mtask2sibs.find(bp) == m_mtask2sibs.end()) {
|
||||
bp->v3fatalSrc("Sibling not found");
|
||||
}
|
||||
UASSERT_OBJ(m_mtask2sibs.find(ap) != m_mtask2sibs.end(), ap,
|
||||
"Sibling not found");
|
||||
UASSERT_OBJ(m_mtask2sibs.find(bp) != m_mtask2sibs.end(), bp,
|
||||
"Sibling not found");
|
||||
bool found = false;
|
||||
for (SibpSet::iterator it = m_mtask2sibs[ap].begin();
|
||||
it != m_mtask2sibs[ap].end(); ++it) {
|
||||
const SiblingMC* sibsp = *it;
|
||||
if (!sibsp->removedFromSb() && !m_sb.contains(sibsp)) {
|
||||
ap->v3fatalSrc("One sibling must be the one we collided with");
|
||||
}
|
||||
UASSERT_OBJ(!(!sibsp->removedFromSb() && !m_sb.contains(sibsp)), ap,
|
||||
"One sibling must be the one we collided with");
|
||||
if ( (sibsp->ap() == ap && sibsp->bp() == bp)
|
||||
|| (sibsp->bp() == ap && sibsp->ap() == bp))
|
||||
found = true;
|
||||
}
|
||||
if (!found) ap->v3fatalSrc("Sibling not found");
|
||||
UASSERT_OBJ(found, ap, "Sibling not found");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1955,7 +1949,7 @@ private:
|
||||
while (!rankIt->second.empty()) {
|
||||
LogicMTaskSet::iterator begin = rankIt->second.begin();
|
||||
LogicMTask* donorp = *begin;
|
||||
if (donorp == mergedp) donorp->v3fatalSrc("Donor can't be merged edge");
|
||||
UASSERT_OBJ(donorp != mergedp, donorp, "Donor can't be merged edge");
|
||||
rankIt->second.erase(begin);
|
||||
// Merge donorp into mergedp.
|
||||
// Fix up the map, so donor's OLVs map to mergedp
|
||||
@@ -1976,9 +1970,8 @@ private:
|
||||
}
|
||||
|
||||
if (lastMergedp) {
|
||||
if (lastMergedp->rank() >= mergedp->rank()) {
|
||||
mergedp->v3fatalSrc("Merging must be on lower rank");
|
||||
}
|
||||
UASSERT_OBJ(lastMergedp->rank() < mergedp->rank(), mergedp,
|
||||
"Merging must be on lower rank");
|
||||
if (!lastMergedp->hasRelative(GraphWay::FORWARD, mergedp)) {
|
||||
new MTaskEdge(m_mtasksp, lastMergedp, mergedp, 1);
|
||||
}
|
||||
@@ -2309,7 +2302,7 @@ public:
|
||||
|
||||
// Update the ready list
|
||||
size_t erased = m_ready.erase(bestMtaskp);
|
||||
if (erased <= 0) bestMtaskp->v3fatalSrc("Should have erased something?");
|
||||
UASSERT_OBJ(erased > 0, bestMtaskp, "Should have erased something?");
|
||||
for (V3GraphEdge* edgeOutp = bestMtaskp->outBeginp();
|
||||
edgeOutp; edgeOutp = edgeOutp->outNextp()) {
|
||||
ExecMTask* nextp = dynamic_cast<ExecMTask*>(edgeOutp->top());
|
||||
@@ -2318,9 +2311,8 @@ public:
|
||||
"Tasks after one being assigned should not be assigned yet");
|
||||
// They also should not be ready yet, since they only now
|
||||
// may become ready
|
||||
if (m_ready.find(nextp) != m_ready.end()) {
|
||||
nextp->v3fatalSrc("Tasks after one being assigned should not be ready");
|
||||
}
|
||||
UASSERT_OBJ(m_ready.find(nextp) == m_ready.end(), nextp,
|
||||
"Tasks after one being assigned should not be ready");
|
||||
bool isReady = true;
|
||||
for (V3GraphEdge* edgeInp = nextp->inBeginp();
|
||||
edgeInp; edgeInp = edgeInp->inNextp()) {
|
||||
@@ -2514,7 +2506,7 @@ void V3Partition::setupMTaskDeps(V3Graph* mtasksp, const Vx2MTaskMap* vx2mtaskp)
|
||||
UASSERT(it != vx2mtaskp->end(), "MTask map can't find id");
|
||||
LogicMTask* otherMTaskp = it->second;
|
||||
UASSERT(otherMTaskp, "NULL other Mtask");
|
||||
if (otherMTaskp == mtaskp) mtaskp->v3fatalSrc("Would create a cycle edge");
|
||||
UASSERT_OBJ(otherMTaskp != mtaskp, mtaskp, "Would create a cycle edge");
|
||||
|
||||
// Don't create redundant edges.
|
||||
if (mtaskp->hasRelative(GraphWay::FORWARD, otherMTaskp)) {
|
||||
@@ -2543,7 +2535,7 @@ void V3Partition::go(V3Graph* mtasksp) {
|
||||
for (V3GraphVertex* vxp = m_fineDepsGraphp->verticesBeginp();
|
||||
vxp; vxp = vxp->verticesNextp()) {
|
||||
MTaskMoveVertex* mtmvVxp = dynamic_cast<MTaskMoveVertex*>(vxp);
|
||||
if (!mtmvVxp) vxp->v3fatalSrc("Every vertex here should be an MTaskMoveVertex");
|
||||
UASSERT_OBJ(mtmvVxp, vxp, "Every vertex here should be an MTaskMoveVertex");
|
||||
|
||||
LogicMTask* mtaskp = new LogicMTask(mtasksp, mtmvVxp);
|
||||
vx2mtask[mtmvVxp] = mtaskp;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user