Compare commits

...
101 Commits
Author SHA1 Message Date
Wilson Snyder 35fe205fd6 Version bump 2019-08-29 18:58:38 -04:00
Todd Strader 70cf2582d6 Add XSim support to driver.pl, bug1493. 2019-08-29 17:00:49 -04:00
Todd Strader ec620387af Add --dpi-hdr-only, bug1491. 2019-08-27 21:36:59 -04:00
Wilson Snyder e1e4bde125 Remove old V3ClkGater code 2019-08-27 17:51:06 -04:00
Todd Strader b947391662 Retain widthSized when simulating 2019-08-12 15:38:58 -04:00
Wilson Snyder edc0f442e4 Tests: Add tests for bug1487. 2019-08-09 05:45:44 -04:00
Wilson Snyder 562b9551bf Internals: Remove some unused arguments. No functional change intended. 2019-08-09 04:12:49 -04:00
Wilson Snyder f74af949da Remove trailing spaces. 2019-08-08 17:53:49 -04:00
Wilson Snyder 81e806e895 Fix elaboration time errors, bug1429. 2019-08-04 22:34:54 -04:00
Wilson Snyder 7d4958264a Support arbitrary-argument $warning/$info etc 2019-08-04 21:50:08 -04:00
Wilson Snyder 88b87429dc Tests: Add elaboration info test. 2019-08-04 21:31:06 -04:00
Wilson Snyder 429f463032 Don't check SYSTEMC environment, for part of CMake support, bug1363. 2019-08-04 20:21:58 -04:00
Wilson Snyder af4a39603c Makefile: Run hello world before other examples. 2019-08-04 20:20:29 -04:00
Wilson Snyder a13d230d8c Tests: Less verbosity 2019-08-04 15:04:34 -04:00
Wilson Snyder b7bd5956ea Concats should be unsigned 2019-08-04 14:48:17 -04:00
Wilson Snyder 1a36e05cc6 Fix core dump on v3Number replace'ed assertions. 2019-08-04 14:14:28 -04:00
Wilson Snyder 71265a8ec9 Internals: Rename unsized functions. No functional change. 2019-08-04 12:39:35 -04:00
Wilson Snyder 3a720204c2 Internals: Remove m_logicMap as m_detailedMap covers it. No functional change intended. 2019-08-04 08:16:24 -04:00
Wilson Snyder a95f58749f Fix internal error on gate optimization of assign, bug1475. 2019-08-03 21:49:39 -04:00
Wilson Snyder 9e58ede480 Internals: Rename V3Hashed functions for future commit. No functional change. 2019-08-03 19:05:59 -04:00
Wilson Snyder cf2ac30f66 Fix date. 2019-08-02 21:39:32 -04:00
Wilson Snyder 68b22a4a3d Tests: Summarize any skips. 2019-07-30 22:20:37 -04:00
Wilson Snyder 875dc9a27e --debug doesn't take an arg 2019-07-30 18:40:38 -04:00
Maarten De Braekeleer 1332446f2a Spelling fixes.
Signed-off-by: Wilson Snyder <[email protected]>
2019-07-30 18:27:36 -04:00
Wilson Snyder 6b798830c9 Fix spacing 2019-07-29 21:07:37 -04:00
Wilson Snyder ffa23b59b8 Tests: Add iff test, bug1482. 2019-07-26 19:31:02 -04:00
Maarten De Braekeleer 7d27461054 Spelling fixes.
Signed-off-by: Wilson Snyder <[email protected]>
2019-07-26 12:58:13 -04:00
Maarten De Braekeleer b3f318d8ab Add contributor certification. 2019-07-26 12:57:42 -04:00
Todd Strader 4a14788c9b When showing an error, show the instance location 2019-07-26 12:52:38 -04:00
Wilson Snyder 5560da2934 Commentary 2019-07-26 06:24:26 -04:00
Todd Strader 43ce048f9e Add rr support 2019-07-25 21:34:09 -04:00
Todd Strader b045111a67 Refactor SimulateVisitor to use AstConst 2019-07-23 13:58:17 -04:00
Todd Strader 654571bd1c V3Number cleanup 2019-07-22 15:07:08 -04:00
Todd Strader a3fd105acb Add more Travis coverage 2019-07-18 05:31:02 -04:00
Wilson Snyder 97561bf064 When showing an error, show source code. 2019-07-14 21:42:03 -04:00
Wilson Snyder a4820fc700 Misc minor error cleanups from show-source branch. 2019-07-14 20:59:56 -04:00
Wilson Snyder ee469eedaf Fix some errors reporting wrong objects. 2019-07-14 15:06:49 -04:00
Wilson Snyder a49d819437 Tests: Default test_regress to quiet 2019-07-14 15:04:19 -04:00
Wilson Snyder cbb9288cb1 Fix some errors reporting wrong objects. 2019-07-14 12:21:39 -04:00
Wilson Snyder 173efbc829 Offer suggestions on bad identifier errors. 2019-07-14 10:08:53 -04:00
Wilson Snyder 0fef3b02ec Fix some parse tokens having wrong fileline. 2019-07-13 12:01:26 -04:00
Wilson Snyder d1ee6689c4 Internals: Make V3LanguageWords a singleton. 2019-07-13 08:57:42 -04:00
Wilson Snyder b3eab9d78a Tests: Move t_math_synmul to extended tests. 2019-07-12 19:35:28 -04:00
Wilson Snyder defd05bdd7 Tests: Fix driver checking threads/gcc for extended tests. 2019-07-12 19:35:10 -04:00
Wilson Snyder a7e5cccf33 In errors, single quote signals and other from-user data. 2019-07-11 22:36:32 -04:00
Wilson Snyder 0f857ce423 In errors, single quote signals and other from-user data. 2019-07-11 22:09:30 -04:00
Wilson Snyder 83cf1882de Quote reserved word messages, and related internal cleanups. 2019-07-11 20:49:35 -04:00
Wilson Snyder e17543d6d8 Tests: Fill in some missing error coverage holes. 2019-07-11 20:18:36 -04:00
Wilson Snyder dacf45fea9 Fix error message continuations to avoid linenumber repetition 2019-07-11 19:15:40 -04:00
Wilson Snyder 13a20c5ee9 Internals: Fix inserted tabs. 2019-07-11 07:05:33 -04:00
Wilson Snyder 58dfe9d071 Internals: Detab and fix spacing style issues. No functional change.
When diff, recommend using "git diff --ignore-all-space"
When merging, recommend using "git merge -Xignore-all-space"
2019-07-11 06:57:49 -04:00
Wilson Snyder 6c5cc885a6 Internals: Cleanup FileLine constructors. No functional change intended. 2019-07-10 22:04:58 -04:00
Sebastien Van Cauwenberghe 2c44ab61ac Fix GDB returning wrong error code on Fedora 30
Signed-off-by: Wilson Snyder <[email protected]>
2019-07-10 06:47:20 -04:00
Sebastien Van Cauwenberghe 0847ea57ae Docs: Add name to contributors
Signed-off-by: Wilson Snyder <[email protected]>
2019-07-10 06:46:28 -04:00
Wilson Snyder 9af84c71f5 Internals: More UASSERT_OBJ conversions. No functional change intended. 2019-07-07 09:01:36 -04:00
Wilson Snyder 4ce77d3e68 Header mentions public use. 2019-07-07 09:00:57 -04:00
Philipp Wagner 3c67791624 docs: Document parse-order-dependency for config files, bug1474
Signed-off-by: Wilson Snyder <[email protected]>
2019-07-06 18:09:23 -04:00
Philipp Wagner a281c00752 docs: Contributor signoff
Signed-off-by: Wilson Snyder <[email protected]>
2019-07-06 18:08:22 -04:00
Wilson Snyder 2ca418288d Fix enum values not being sized based on parent, bug1442. 2019-07-06 16:26:44 -04:00
Wilson Snyder bea34d0ced Internals: Misc enum cleanups. No functional change. 2019-07-06 15:50:25 -04:00
Wilson Snyder a5f2ce0dad Tests: Disable t_leak on Travis-CI. 2019-07-06 13:47:17 -04:00
Gianfranco Costamagna 07ae7146e3 Honour system flags, this makes debug flags being injected correctly.
Signed-off-by: Wilson Snyder <[email protected]>
2019-07-06 13:02:36 -04:00
Wilson Snyder 8548ecfdac Internals: Add UASSERT_OBJ macro to replace hand-done ifs. No functional change intended.
This makes it easier to filter out correctly zero code-coverage lines.
2019-07-06 12:57:50 -04:00
Wilson Snyder 7e54ff1b37 Additional lcov code coverage. 2019-07-05 22:30:19 -04:00
Wilson Snyder a4b9745e6e Fix lifetime opt dropping 2019-07-05 22:30:19 -04:00
Wilson Snyder 8612ba2b8d Internals: Commentary and fix gate debug glibc warning 2019-07-05 22:30:19 -04:00
Todd Strader fea6e047e2 Add Travis badge 2019-07-05 05:26:08 -04:00
Wilson Snyder 01ef7122e9 Internals: Add lcov code coverage markers. 2019-06-30 22:37:03 -04:00
Wilson Snyder ac43e7322b Internals: Remove dead code. 2019-06-30 22:11:56 -04:00
Wilson Snyder 02a3f2089f Include filenames in savable error. 2019-06-30 22:01:53 -04:00
Wilson Snyder 33ad834106 Tests: Close some test coverage holes. 2019-06-30 22:00:18 -04:00
Wilson Snyder efe739c52f Tests: Cover --error-limit and update docs (found via coverage). 2019-06-30 21:36:57 -04:00
Wilson Snyder 5cd531a5e0 Tests: Fix glib warning 2019-06-30 21:35:55 -04:00
Wilson Snyder 0e8dfdbb71 Tests: Less verbosity. 2019-06-30 17:45:42 -04:00
Wilson Snyder ec32a9b976 Tests: Close some test coverage holes. 2019-06-30 17:38:41 -04:00
Wilson Snyder 1aae6df669 Internals: Run graph self tests on --debug-self-test 2019-06-30 17:36:58 -04:00
Wilson Snyder f7641d2ecc Change MULTITOP to warning to help linting, see manual. 2019-06-30 16:46:48 -04:00
Wilson Snyder 5f27c41ee3 Test driver: Cleanup rerun flags & status. 2019-06-29 21:36:33 -04:00
Wilson Snyder 51df77099a Test driver: With --quiet print summary every 15sec or Travis barfs. 2019-06-29 15:58:00 -04:00
Wilson Snyder b8b7e5c978 Test driver: Add --rerun and --quiet options 2019-06-29 15:24:56 -04:00
Wilson Snyder dddcfeb482 Test driver: Move one_test, no functional change. 2019-06-29 13:37:01 -04:00
Wilson Snyder 8d63f83177 Tests: Reduce verbosity unless use --verbose 2019-06-29 13:11:30 -04:00
Wilson Snyder d43e9aa2c5 Fix not in array context on non-power-of-two slices, msg2946. 2019-06-29 11:30:38 -04:00
Wilson Snyder a2c1b7f827 Tests: Fix t_flag_csplit skip on old make. 2019-06-29 08:25:50 -04:00
Wilson Snyder 2fff92fe78 Tests: Add array MDA test. 2019-06-29 08:12:00 -04:00
Wilson Snyder ba9af4aabf For internal messages, use <command-line> and <built-in> to match GCC. 2019-06-29 07:39:34 -04:00
Wilson Snyder ef02678305 tests: Fix default make test, broke earlier commit. 2019-06-29 07:22:19 -04:00
Wilson Snyder 6990db70fa Avoid unique internal fileline in selftest. 2019-06-28 22:34:01 -04:00
Wilson Snyder af5616c077 Fix line number in dup vars. 2019-06-28 22:34:01 -04:00
Todd Strader 009f053d6e Add initial Travis CI config 2019-06-27 11:26:25 -04:00
Wilson Snyder 6903c52ef7 Tests: Add coverage of missing include error. 2019-06-26 19:12:57 -04:00
Wilson Snyder aa65dfb1bc Tests: Use only 2 threads for t_gantt. 2019-06-26 19:06:52 -04:00
Wilson Snyder 84a2cd0ec7 Remove some old DIRPROJECT dead code. 2019-06-26 18:50:38 -04:00
Wilson Snyder 8e7559c6c9 Show included-from filenames in warnings, bug1439. 2019-06-22 17:01:39 -04:00
Wilson Snyder 951521320a Warnings: Suppress dup lines, and use lowercase note: to match gcc 2019-06-22 16:12:17 -04:00
Wilson Snyder abc0eb92a0 Debug: Show only first line of suppressed warnings 2019-06-22 14:25:31 -04:00
Wilson Snyder c514bb26e8 Always internal module wrapper TOP. 2019-06-22 13:43:49 -04:00
Wilson Snyder 5cb6474cc6 Fix not reporting some duplicate signals/ports, bug1462. 2019-06-22 12:43:48 -04:00
Wilson Snyder b8eb6368e9 Tests: Fix up duplicate var declarations. 2019-06-22 12:32:13 -04:00
Wilson Snyder 6239deb1df Tests: Disable t_flag_csplit with old make. 2019-06-19 08:44:40 -04:00
Wilson Snyder 89c2a6070a devel release 2019-06-16 10:01:09 -04:00
420 changed files with 7983 additions and 7591 deletions
+1 -1
View File
@@ -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
View File
@@ -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
+30 -1
View File
@@ -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]
+1
View File
@@ -14,6 +14,7 @@
.*\.key
.*\.vcd
.*\.1
\.travis\.yml
/obj_dir/
/obj_dbg/
/obj_nc/
+10 -28
View File
@@ -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)
+2
View File
@@ -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
View File
@@ -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
-7
View File
@@ -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
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+6 -6
View File
@@ -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*/
+1 -1
View File
@@ -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
+6 -6
View File
@@ -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*/
+8 -7
View File
@@ -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
View File
@@ -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
}
}
};
+6 -3
View File
@@ -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();
}
+1 -2
View File
@@ -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
}
}
}
+3 -3
View File
@@ -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");
}
+2 -1
View File
@@ -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
+263
View File
@@ -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:
+42
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
}
-924
View File
@@ -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);
}
-37
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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) {
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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:
+5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
-5
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
}
}
}
+2 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
+4 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+6 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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());
}
+4 -3
View File
@@ -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);
+26
View File
@@ -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
View File
@@ -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; }
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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