Merge from master for release.

This commit is contained in:
Wilson Snyder 2021-10-17 14:46:03 -04:00
commit 0185ee5df3
228 changed files with 4807 additions and 1969 deletions

22
Changes
View File

@ -8,6 +8,28 @@ The changes in each Verilator version are described below. The
contributors that suggested a given feature are shown in []. Thanks!
Verilator 4.214 2021-10-17
==========================
* Add profile-guided optmization of mtasks (#3150).
* Verilator_gantt has removed the ASCII graphics, use the VCD output instead.
* Verilator_gantt now shows the predicted mtask times, eval times, and additional statistics.
* Verilator_gantt data files now include processor information, to allow later processing.
* Support displaying x and z in $display task (#3107) (#3109). [Iru Cai]
* Fix verilator_profcfunc profile accounting (#3115).
* Fix display has no time units on class function (#3116). [Damien Pretet]
* Fix removing if statement with side effect in condition (#3131). [Alexander Grobman]
* Fix --waiver-output for multiline warnings (#2429) (#3141). [Keith Colbert]
* Fix internal error on bad widths (#3140) (#3145). [Zhanglei Wang]
* Fix crash on clang 12/13 (#3148). [Kouping Hsu]
* Fix cygwin compile error due to missing -std=gnu++14 (#3149). [Sun Kim]
* Fix $urandom_range when the range is 0 ... UINT_MAX (#3161). [Iru Cai]
* Fix constructor-parameter argument comma-separation in C++ (#3162). [Matthew Ballance]
* Fix missing install of vl_file_copy/vl_hier_graph (#3165). [Popolon]
* Fix calling new with arguments in same class (#3166). [Matthew Ballance]
* Fix false EOFNEWLINE warning when DOS carriage returns present (#3171).
Verilator 4.212 2021-09-01
==========================

View File

@ -118,23 +118,6 @@ INFOS = verilator.html verilator.pdf
INFOS_OLD = README README.html README.pdf
INST_PROJ_FILES = \
bin/verilator \
bin/verilator_ccache_report \
bin/verilator_coverage \
bin/verilator_gantt \
bin/verilator_includer \
bin/verilator_profcfunc \
include/verilated.mk \
include/*.[chv]* \
include/gtkwave/*.[chv]* \
include/vltstd/*.[chv]* \
INST_PROJ_BIN_FILES = \
bin/verilator_bin$(EXEEXT) \
bin/verilator_bin_dbg$(EXEEXT) \
bin/verilator_coverage_bin_dbg$(EXEEXT) \
EXAMPLES_FIRST = \
examples/make_hello_c \
examples/make_hello_sc \
@ -142,7 +125,7 @@ EXAMPLES_FIRST = \
EXAMPLES = $(EXAMPLES_FIRST) $(filter-out $(EXAMPLES_FIRST), $(sort $(wildcard examples/*)))
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
VL_INST_MAN_FILES = verilator.1 verilator_coverage.1 verilator_gantt.1 verilator_profcfunc.1
VL_INST_MAN_FILES = verilator.1 verilator_coverage.1
default: all
all: all_nomsg msg_test
@ -224,8 +207,10 @@ VL_INST_INC_SRCDIR_FILES = \
include/vltstd/*.[chv]* \
VL_INST_DATA_SRCDIR_FILES = \
examples/*/*.[chv]* examples/*/Makefile* \
examples/*/CMakeLists.txt
examples/*/*.[chv]* \
examples/*/CMakeLists.txt \
examples/*/Makefile* \
examples/*/vl_* \
installbin:
$(MKINSTALLDIRS) $(DESTDIR)$(bindir)
@ -369,6 +354,9 @@ clang-format:
PY_PROGRAMS = \
bin/verilator_ccache_report \
bin/verilator_difftree \
bin/verilator_gantt \
bin/verilator_profcfunc \
examples/xml_py/vl_file_copy \
examples/xml_py/vl_hier_graph \
docs/guide/conf.py \

View File

@ -34,11 +34,11 @@ my $opt_quiet_exit;
# No arguments can't do anything useful. Give help
if ($#ARGV < 0) {
pod2usage(-exitstatus=>2, -verbose=>0);
pod2usage(-exitstatus => 2, -verbose => 0);
}
# Insert debugging options up front
push @ARGV, (split ' ',$ENV{VERILATOR_TEST_FLAGS}||"");
push @ARGV, (split ' ', $ENV{VERILATOR_TEST_FLAGS} || "");
# We sneak a look at the flags so we can do some pre-environment checks
# All flags will hit verilator...
@ -46,7 +46,7 @@ foreach my $sw (@ARGV) {
push @Opt_Verilator_Sw, $sw;
}
Getopt::Long::config("no_auto_abbrev","pass_through");
Getopt::Long::config("no_auto_abbrev", "pass_through");
if (! GetOptions(
# Major operating modes
"help" => \&usage,
@ -60,7 +60,7 @@ if (! GetOptions(
# Additional parameters
"<>" => sub {}, # Ignored
)) {
pod2usage(-exitstatus=>2, -verbose=>0);
pod2usage(-exitstatus => 2, -verbose => 0);
}
if ($opt_gdbbt && !gdb_works()) {
@ -73,12 +73,12 @@ if ($opt_gdbbt && !gdb_works()) {
# Starting with that, escape all special chars for the shell;
# The shell will undo the escapes and the verilator binary should
# then see exactly the contents of @Opt_Verilator_Sw.
my @quoted_sw = map {sh_escape($_)} @Opt_Verilator_Sw;
my @quoted_sw = map { sh_escape($_) } @Opt_Verilator_Sw;
if ($opt_gdb) {
# Generic GDB interactive
run (aslr_off()
.($ENV{VERILATOR_GDB}||"gdb")
." ".verilator_bin()
. ($ENV{VERILATOR_GDB} || "gdb")
. " " . verilator_bin()
# Note, uncomment to set breakpoints before running:
# ." -ex 'break main'"
@ -87,42 +87,41 @@ if ($opt_gdb) {
# escapes as you would expect in a double-quoted string.
# That's not true for a single-quoted string, where \'
# actually terminates the string -- not what we want!
." -ex \"run ".join(' ', @quoted_sw)."\""
." -ex 'set width 0'"
." -ex 'bt'");
. " -ex \"run " . join(' ', @quoted_sw) . "\""
. " -ex 'set width 0'"
. " -ex 'bt'");
} elsif ($opt_rr) {
# Record with rr
run (aslr_off()
."rr record ".verilator_bin()
." ".join(' ', @quoted_sw));
. "rr record " . verilator_bin()
. " " . join(' ', @quoted_sw));
} elsif ($opt_gdbbt && $Debug) {
# Run under GDB to get gdbbt
run (aslr_off()
."gdb"
." ".verilator_bin()
." --batch --quiet --return-child-result"
." -ex \"run ".join(' ', @quoted_sw)."\""
." -ex 'set width 0'"
." -ex 'bt' -ex 'quit'");
. "gdb"
. " " . verilator_bin()
. " --batch --quiet --return-child-result"
. " -ex \"run " . join(' ', @quoted_sw)."\""
. " -ex 'set width 0'"
. " -ex 'bt' -ex 'quit'");
} elsif ($Debug) {
# Debug
run(aslr_off()
.verilator_bin()." ".join(' ',@quoted_sw));
run(aslr_off() . verilator_bin() . " " . join(' ', @quoted_sw));
} else {
# Normal, non gdb
run(verilator_bin()." ".join(' ',@quoted_sw));
run(verilator_bin() . " " . join(' ', @quoted_sw));
}
#----------------------------------------------------------------------
sub usage {
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
pod2usage(-verbose => 2, -exitval => 0, -output => \*STDOUT);
}
sub debug {
shift;
my $level = shift;
$Debug = $level||3;
$Debug = $level || 3;
}
#######################################################################
@ -163,13 +162,13 @@ sub verilator_bin {
sub gdb_works {
$! = undef; # Cleanup -x
system("gdb /bin/echo"
." --batch-silent --quiet --return-child-result"
." -ex 'run -n'" # `echo -n`
." -ex 'set width 0'"
." -ex 'bt'"
." -ex 'quit'");
. " --batch-silent --quiet --return-child-result"
. " -ex 'run -n'" # `echo -n`
. " -ex 'set width 0'"
. " -ex 'bt'"
. " -ex 'quit'");
my $status = $?;
return $status==0;
return $status == 0;
}
sub aslr_off {
@ -185,7 +184,7 @@ sub run {
# Run command, check errors
my $command = shift;
$! = undef; # Cleanup -x
print "\t$command\n" if $Debug>=3;
print "\t$command\n" if $Debug >= 3;
system($command);
my $status = $?;
if ($status) {
@ -193,7 +192,7 @@ sub run {
warn "%Error: verilator: Misinstalled, or VERILATOR_ROOT might need to be in environment\n";
}
if ($Debug) { # For easy rerunning
warn "%Error: export VERILATOR_ROOT=".($ENV{VERILATOR_ROOT}||"")."\n";
warn "%Error: export VERILATOR_ROOT=" . ($ENV{VERILATOR_ROOT} || "") . "\n";
warn "%Error: $command\n";
}
if ($status & 127) {
@ -201,13 +200,13 @@ sub run {
|| ($status & 127) == 8 # SIGFPA
|| ($status & 127) == 11) { # SIGSEGV
warn "%Error: Verilator internal fault, sorry. "
."Suggest trying --debug --gdbbt\n" if !$Debug;
. "Suggest trying --debug --gdbbt\n" if !$Debug;
} elsif (($status & 127) == 6) { # SIGABRT
warn "%Error: Verilator aborted. "
."Suggest trying --debug --gdbbt\n" if !$Debug;
. "Suggest trying --debug --gdbbt\n" if !$Debug;
} else {
warn "%Error: Verilator threw signal $status. "
."Suggest trying --debug --gdbbt\n" if !$Debug;
. "Suggest trying --debug --gdbbt\n" if !$Debug;
}
}
if (!$opt_quiet_exit && ($status != 256 || $Debug)) { # i.e. not normal exit(1)
@ -448,6 +447,7 @@ description of these arguments.
+verilator+prof+threads+file+<filename> Set profile filename
+verilator+prof+threads+start+<value> Set profile starting point
+verilator+prof+threads+window+<value> Set profile duration
+verilator+prof+vlt+file+<filename> Set profile guided filename
+verilator+rand+reset+<value> Set random reset technique
+verilator+seed+<value> Set random seed
+verilator+V Verbose version and config

View File

@ -30,7 +30,7 @@ $Debug = 0;
# No arguments can't do anything useful. Give help
if ($#ARGV < 0) {
pod2usage(-exitstatus=>2, -verbose=>0);
pod2usage(-exitstatus => 2, -verbose => 0);
}
# We sneak a look at the flags so we can do some pre-environment checks
@ -40,7 +40,7 @@ foreach my $sw (@ARGV) {
push @Opt_Verilator_Sw, $sw;
}
Getopt::Long::config("no_auto_abbrev","pass_through");
Getopt::Long::config("no_auto_abbrev", "pass_through");
if (! GetOptions (
# Major operating modes
"help" => \&usage,
@ -49,23 +49,23 @@ if (! GetOptions (
# Additional parameters
"<>" => sub {}, # Ignored
)) {
pod2usage(-exitstatus=>2, -verbose=>0);
pod2usage(-exitstatus => 2, -verbose => 0);
}
# Normal, non gdb
run(verilator_coverage_bin()
." ".join(' ',@Opt_Verilator_Sw));
. " " . join(' ', @Opt_Verilator_Sw));
#----------------------------------------------------------------------
sub usage {
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
pod2usage(-verbose => 2, -exitval => 0, -output => \*STDOUT);
}
sub debug {
shift;
my $level = shift;
$Debug = $level||3;
$Debug = $level || 3;
}
#######################################################################
@ -107,7 +107,7 @@ sub run {
# Run command, check errors
my $command = shift;
$! = undef; # Cleanup -x
print "\t$command\n" if $Debug>=3;
print "\t$command\n" if $Debug >= 3;
system($command);
my $status = $?;
if ($status) {
@ -115,11 +115,11 @@ sub run {
warn "%Error: verilator_coverage: Misinstalled, or VERILATOR_ROOT might need to be in environment\n";
}
if ($Debug) { # For easy rerunning
warn "%Error: export VERILATOR_ROOT=".($ENV{VERILATOR_ROOT}||"")."\n";
warn "%Error: export VERILATOR_ROOT=" . ($ENV{VERILATOR_ROOT} || "") . "\n";
warn "%Error: $command\n";
}
if ($status & 127) {
if (($status & 127) == 8 || ($status & 127) == 11) { # SIGFPA or SIGSEGV
if (($status & 127) == 8 || ($status & 127) == 11) { # SIGFPA or SIGSEGV
warn "%Error: Verilator_coverage internal fault, sorry.\n" if !$Debug;
} elsif (($status & 127) == 6) { # SIGABRT
warn "%Error: Verilator_coverage aborted.\n" if !$Debug;

View File

@ -1,233 +1,139 @@
#!/usr/bin/env perl
# See copyright, etc in below POD section.
#!/usr/bin/env python3
# pylint: disable=C0103,C0114,C0116
######################################################################
use warnings;
use Getopt::Long;
use IO::File;
use Pod::Usage;
use strict;
use vars qw($Debug);
import argparse
import collections
import glob
import os.path
import re
import sys
#======================================================================
# main
$Debug = 0;
my $Opt_A;
my $Opt_B;
my $Opt_Lineno = 1;
autoflush STDOUT 1;
autoflush STDERR 1;
Getopt::Long::config("no_auto_abbrev");
if (! GetOptions(
"help" => \&usage,
"debug" => \&debug,
"<>" => \&parameter,
"lineno!" => \$Opt_Lineno,
)) {
die "%Error: Bad usage, try 'verilator_difftree --help'\n";
}
def diff(a, b):
defined $Opt_A or die "%Error: No old diff filename\n";
defined $Opt_B or die "%Error: No new diff filename\n";
if not os.path.exists(a):
sys.exit("%Error: No old diff filename found: " + a)
if not os.path.exists(b):
sys.exit("%Error: No new diff filename found: " + b)
-e $Opt_A or die "%Error: No old diff filename found: $Opt_A\n";
-e $Opt_B or die "%Error: No new diff filename found: $Opt_B\n";
if os.path.isdir(a) and os.path.isdir(b):
diff_dir(a, b)
elif os.path.isfile(a) and os.path.isfile(b):
diff_file(a, b)
else:
sys.exit("%Error: Mix of files and dirs")
if (-d $Opt_A && -d $Opt_B) {
diff_dir($Opt_A, $Opt_B);
} elsif (-f $Opt_A && -f $Opt_B) {
diff_file($Opt_A, $Opt_B);
} else {
die "%Error: Mix of files and dirs\n";
}
sub diff_dir {
my $a = shift;
my $b = shift;
def diff_dir(a, b):
# Diff all files under two directories
my %files;
files = collections.defaultdict(lambda: {})
foreach my $fn (glob("$a/*.tree")) {
(my $base = $fn) =~ s!.*/!!;
$files{$base}{a} = $fn;
}
foreach my $fn (glob("$b/*.tree")) {
(my $base = $fn) =~ s!.*/!!;
$files{$base}{b} = $fn;
}
my $any;
foreach my $base (sort (keys %files)) {
my $a = $files{$base}{a};
my $b = $files{$base}{b};
next if !$a || !$b;
print "="x70,"\n";
print "= $a <-> $b\n";
diff_file($a,$b);
$any = 1;
}
$any or warn("%Warning: No .tree files found that have similar base names:\n "
.join("\n ", sort keys %files),"\n");
}
for fn in glob.glob(a + "/*.tree"):
base = re.sub(r'.*/', '', fn)
files[base]['a'] = fn
for fn in glob.glob(b + "/*.tree"):
base = re.sub(r'.*/', '', fn)
files[base]['b'] = fn
sub diff_file {
my $a = shift;
my $b = shift;
anyfile = False
for base in sorted(files.keys()):
a = files[base]['a']
b = files[base]['b']
if not a or not b:
continue
print("=" * 70)
print("= %s <-> %s" % (a, b))
diff_file(a, b)
anyfile = True
if not anyfile:
sys.stderr.write(
"%Warning: No .tree files found that have similar base names\n")
def diff_file(a, b):
# Compare the two tree files
(my $short_a = $a) =~ s/[^a-zA-Z0-9.]+/_/g;
(my $short_b = $b) =~ s/[^a-zA-Z0-9.]+/_/g;
my $tmp_a = "/tmp/${$}_${short_a}.a";
my $tmp_b = "/tmp/${$}_${short_b}.b";
short_a = re.sub(r'[^a-zA-Z0-9.]+', '_', a)
short_b = re.sub(r'[^a-zA-Z0-9.]+', '_', b)
tmp_a = "/tmp/%s_%s.a" % (os.getpid(), short_a)
tmp_b = "/tmp/%s_%s.b" % (os.getpid(), short_b)
my $vera = version_from($a);
my $verb = version_from($b);
my $verCvt = (($vera < 0x3900 && $verb >= 0x3900)
|| ($vera >= 0x3900 && $verb < 0x3900));
# Version conversion deprecated, but for future...
# vera = version_from(a)
# verb = version_from(b)
# verCvt = ((vera < 0x3900 and verb >= 0x3900)
# or (vera >= 0x3900 and verb < 0x3900))
filter($a, $tmp_a, $verCvt);
filter($b, $tmp_b, $verCvt);
system("diff -u $tmp_a $tmp_b");
unlink $tmp_a;
unlink $tmp_b;
}
filterf(a, tmp_a)
filterf(b, tmp_b)
os.system("diff -u " + tmp_a + " " + tmp_b)
os.unlink(tmp_a)
os.unlink(tmp_b)
sub version_from {
my $fn = shift;
def version_from(filename):
# Return dump format
my $f1 = IO::File->new ($fn) or die "%Error: $! $fn,";
while (defined (my $line=$f1->getline())) {
last if $. > 10;
return hex $1 if $line =~ /\(format (0x[0-9.]+)\)/;
}
return 1.0;
}
with open(filename) as fh:
lineno = 0
for line in fh:
if lineno > 10:
break
match = re.search(r'format (0x[0-9.]+)', line)
if match:
return hex(match.group(1))
return 1.0
sub filter {
my $fn1 = shift;
my $fn2 = shift;
my $verCvt = shift;
def filterf(fn1, fn2):
# Remove hex numbers before diffing
my $f1 = IO::File->new ($fn1) or die "%Error: $! $fn1,";
my $f2 = IO::File->new ($fn2,"w") or die "%Error: $! $fn2,";
while (defined (my $line=$f1->getline())) {
same_line:
next if $line =~ / This=/;
$line =~ s/0x[a-f0-9]+/0x/g;
$line =~ s/<e[0-9]+\#?>/<e>/g;
$line =~ s/{[a-z]*\d+}/{}/g if !$Opt_Lineno;
if ($verCvt) {
next if $line =~ /^ NETLIST/;
if ($line =~ /: ([A-Z]+) /) {
my $type = $1;
next if $type =~ 'DTYPE';
if ($type eq 'TYPETABLE' || $type eq 'RANGE') {
$line =~ /^(\s+\S+:) /; my $prefix = $1;
while (defined ($line=$f1->getline())) {
next if $line =~ /^\s+[a-z]/; # Table body
next if $line =~ /^${prefix}[0-9]:/;
goto same_line;
}
next;
}
}
}
print $f2 $line;
}
$f1->close;
$f2->close;
}
with open(fn1) as fh1:
with open(fn2, "w") as fh2:
for line in fh1:
if re.search(r' This=', line):
continue
line = re.sub(r'0x[a-f0-9]+', '0x', line)
line = re.sub(r'<e[0-9]+\#?>', '<e>', line)
if not Args.no_lineno:
line = re.sub(r'{[a-z]*\d+}', '{}', line)
fh2.write(line)
#----------------------------------------------------------------------
sub usage {
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
exit(1); # Unreachable
}
######################################################################
######################################################################
sub debug {
$Debug = 1;
}
sub parameter {
my $param = shift;
if (!defined $Opt_A) {
$Opt_A = $param;
} elsif (!defined $Opt_B) {
$Opt_B = $param;
} else {
die "%Error: Unknown parameter: $param\n";
}
}
#######################################################################
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
verilator_difftree - Compare two Verilator debugging trees
=head1 SYNOPSIS
verilator_difftree .../a/a.tree .../b/a.tree
verilator_difftree .../a .../b
=head1 DESCRIPTION
Verilator_difftree is used for debugging Verilator tree output files. It
performs a diff between two files, or all files common between two
parser = argparse.ArgumentParser(
allow_abbrev=False,
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""Compare two Verilator debugging trees""",
epilog=
"""Verilator_difftree is used for debugging Verilator tree output files.
It performs a diff between two files, or all files common between two
directories, ignoring irrelevant pointer differences.
=head1 ARGUMENTS
=over 4
=item --help
Displays this message and program version and exits.
=item --nolineno
Do not show differences in line numbering.
=back
=head1 DISTRIBUTION
The latest version is available from L<https://verilator.org>.
For documentation see
https://verilator.org/guide/latest/exe_verilator_difftree.html
Copyright 2005-2021 by Wilson Snyder. This program is free software; you
can redistribute it and/or modify it under the terms of either the GNU
Lesser General Public License Version 3 or the Perl Artistic License
Version 2.0.
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
=head1 AUTHORS
parser.add_argument('--debug',
action='store_const',
const=9,
help='enable debug')
parser.add_argument('--no-lineno',
action='store_false',
help='do not show differences in line numbering')
parser.add_argument('filea', help='input file a to diff')
parser.add_argument('fileb', help='input file b to diff')
Wilson Snyder <wsnyder@wsnyder.org>
=head1 SEE ALSO
C<verilator>
and L<https://verilator.org/verilator_doc.html> for detailed documentation.
=cut
Args = parser.parse_args()
diff(Args.filea, Args.fileb)
######################################################################
### Local Variables:
### compile-command: "$V4/bin/verilator_difftree {$V4D,$V4}/test_regress/obj_dir/t_EXAMPLE/V*_03_*.tree"
### End:
# Local Variables:
# compile-command: "./verilator_difftree ../test_regress/t/t_difftree.{a,b}.tree"
# End:

File diff suppressed because it is too large Load Diff

View File

@ -10,11 +10,11 @@
require 5.005;
use warnings;
print "// DESCR"."IPTION: Generated by verilator_includer via makefile\n";
print "// DESCR" . "IPTION: Generated by verilator_includer via makefile\n";
foreach my $param (@ARGV) {
if ($param =~ /^-D([^=]+)=(.*)/) {
print "#define $1 $2\n"
print "#define $1 $2\n";
} else {
print "#include \"$param\"\n"
print "#include \"$param\"\n";
}
}

View File

@ -1,248 +1,204 @@
#!/usr/bin/env perl
# See copyright, etc in below POD section.
#!/usr/bin/env python3
# pylint: disable=C0103,C0114,C0116,R0914,R0912,R0915,eval-used
######################################################################
require 5.006_001;
use warnings;
use Getopt::Long;
use IO::File;
use Pod::Usage;
eval { use Data::Dumper; $Data::Dumper::Indent = 1; }; # Debug, ok if missing
use strict;
use vars qw($Debug);
import argparse
import collections
import re
# from pprint import pprint
#======================================================================
######################################################################
#======================================================================
# main
def profcfunc(filename):
funcs = {}
$Debug = 0;
my $Opt_File;
autoflush STDOUT 1;
autoflush STDERR 1;
Getopt::Long::config("no_auto_abbrev");
if (! GetOptions(
"help" => \&usage,
"debug" => \&debug,
"<>" => \&parameter,
)) {
die "%Error: Bad usage, try 'verilator_profcfunc --help'\n";
}
with open(filename) as fh:
defined $Opt_File or die "%Error: No filename given\n";
for line in fh:
# %time cumesec selfsec calls {stuff} name
match = re.match(
r'^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$',
line)
if match:
pct = float(match.group(1))
sec = float(match.group(2))
calls = float(match.group(3))
func = match.group(4)
if func not in funcs:
funcs[func] = {'pct': 0, 'sec': 0, 'calls': 0}
funcs[func]['pct'] += pct
funcs[func]['sec'] += sec
funcs[func]['calls'] += calls
continue
profcfunc($Opt_File);
#----------------------------------------------------------------------
sub usage {
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
exit(1); # Unreachable
}
sub debug {
$Debug = 1;
}
sub parameter {
my $param = shift;
if (!defined $Opt_File) {
$Opt_File = $param;
} else {
die "%Error: Unknown parameter: $param\n";
}
}
#######################################################################
sub profcfunc {
my $filename = shift;
# Remove hex numbers before diffing
my $fh = IO::File->new ($filename) or die "%Error: $! $filename,";
my %funcs;
while (defined (my $line=$fh->getline())) {
# %time cumesec selfsec calls {stuff} name
if ($line =~ /^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$/) {
my $pct=$1; my $sec=$2; my $calls=$3; my $func=$4;
$funcs{$func}{pct} += $pct;
$funcs{$func}{sec} += $sec;
$funcs{$func}{calls} += $calls;
}
# Older gprofs have no call column for single-call functions
# %time cumesec selfsec {stuff} name
elsif ($line =~ /^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$/) {
my $pct=$1; my $sec=$2; my $calls=1; my $func=$3;
$funcs{$func}{pct} += $pct;
$funcs{$func}{sec} += $sec;
$funcs{$func}{calls} += $calls;
}
}
$fh->close;
# Older gprofs have no call column for single-call functions
# %time cumesec selfsec {stuff} name
match = re.match(
r'^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$',
line)
if match:
pct = float(match.group(1))
sec = float(match.group(2))
calls = 1
func = match.group(3)
if func not in funcs:
funcs[func] = {'pct': 0, 'sec': 0, 'calls': 0}
funcs[func]['pct'] += pct
funcs[func]['sec'] += sec
funcs[func]['calls'] += calls
continue
# Find modules
my %pointer_mods;
my %verilated_mods;
foreach my $func (keys %funcs) {
if ($func =~ /(.*)::_eval\(([a-zA-Z_0-9]+__Syms).*\)$/) {
$verilated_mods{$1} = qr/^$1/;
$pointer_mods{$2} = $1;
}
}
#print Dumper(\%pointer_mods, \%verilated_mods);
verilated_mods = {}
for func in funcs:
match = re.search(r'(.*)::eval(_step)?\(', func)
if match:
prefix = match.group(1)
if Args.debug:
print("-got _eval %s prefix=%s" % (func, prefix))
verilated_mods[prefix] = re.compile(r'^' + prefix)
# pprint(verilated_mods)
# Resort by Verilog name
my %vfuncs;
my %groups;
foreach my $func (keys %funcs) {
my $pct = $funcs{$func}{pct};
my $vfunc = $func;
my $design;
# Sort by Verilog name
vfuncs = {}
groups = {}
groups['type'] = collections.defaultdict(lambda: 0)
groups['design'] = collections.defaultdict(lambda: 0)
groups['module'] = collections.defaultdict(lambda: 0)
if ($func =~ /\(([a-zA-Z_0-9]+__Syms)/) {
$design = $pointer_mods{$1};
}
for func in funcs:
pct = funcs[func]['pct']
vfunc = func
foreach my $vde (keys %verilated_mods) {
last if $design;
if ($func =~ /$verilated_mods{$vde}/) {
$design=$vde;
last;
}
}
funcarg = re.sub(r'^.*\(', '', func)
if ($vfunc =~ /__PROF__([a-zA-Z_0-9]+)__l?([0-9]+)\(/) {
$vfunc = sprintf("VBlock %s:%d", $1, $2);
$groups{type}{"Verilog Blocks under $design"} += $pct;
$groups{design}{$design} += $pct;
$groups{module}{$1} += $pct;
} else {
if ($design) {
$vfunc = sprintf("VCommon %s", $func);
$groups{type}{"Common code under $design"} += $pct;
$groups{design}{$design} += $pct;
$groups{module}{$design." common code"} += $pct;
} elsif ($func =~ /^VL_[A-Z0-9_]+/
|| $func =~ /^_?vl_[a-zA-Z0-9_]+/
|| $func =~ /^verilated/i) {
$vfunc = sprintf("VLib %s", $func);
$groups{type}{'VLib'} += $pct;
$groups{design}{'VLib'} += $pct;
$groups{module}{'VLib'} += $pct;
} elsif ($func =~ /^_mcount_private/) {
$vfunc = sprintf("Prof %s", $func);
$groups{type}{'Prof'} += $pct;
$groups{design}{'Prof'} += $pct;
$groups{module}{'Prof'} += $pct;
} else {
$vfunc = sprintf("C++ %s", $func);
$groups{type}{'C++'} += $pct;
$groups{design}{'C++'} += $pct;
$groups{module}{'C++'} += $pct;
}
}
$vfuncs{$vfunc} = $funcs{$func};
}
design = None
for vde in verilated_mods:
if verilated_mods[vde].match(func) or verilated_mods[vde].match(
funcarg):
design = vde
break
vdesign = "-"
prof_match = re.search(r'__PROF__([a-zA-Z_0-9]+)__l?([0-9]+)\(', vfunc)
if design and prof_match:
linefunc = prof_match.group(1)
lineno = int(prof_match.group(2))
vfunc = "VBlock %s:%d" % (linefunc, lineno)
vdesign = design
groups['type']["Verilog Blocks under " + design] += pct
groups['design'][design] += pct
groups['module'][linefunc] += pct
elif design:
vfunc = "VCommon " + func
vdesign = design
groups['type']["Common code under " + design] += pct
groups['design'][design] += pct
groups['module'][design + " common code"] += pct
elif re.match(r'(VL_[A-Z0-9_]+|_?vl_[a-zA-Z0-9_]+|Verilated)', vfunc):
vfunc = "VLib " + func
groups['type']['VLib'] += pct
groups['design']['VLib'] += pct
groups['module']['VLib'] += pct
elif re.match(r'^_mcount_private', vfunc):
vfunc = "Prof " + func
groups['type']['Prof'] += pct
groups['design']['Prof'] += pct
groups['module']['Prof'] += pct
else:
vfunc = "C++ " + func
groups['type']['C++'] += pct
groups['design']['C++'] += pct
groups['module']['C++'] += pct
if vfunc not in vfuncs:
vfuncs[vfunc] = funcs[func]
vfuncs[vfunc]['design'] = vdesign
else:
vfuncs[vfunc]['pct'] += funcs[func]['pct']
vfuncs[vfunc]['calls'] += funcs[func]['calls']
vfuncs[vfunc]['sec'] += funcs[func]['sec']
for ftype in ['type', 'design', 'module']:
missing = 100
for item in groups[ftype].keys():
missing -= groups[ftype][item]
groups[ftype]["\377Unaccounted for/rounding error"] = missing
print("Overall summary by %s:" % ftype)
print(" %-6s %s" % ("% time", ftype))
for what in sorted(groups[ftype].keys()):
# \377 used to establish sort order
pwhat = re.sub(r'^\377', '', what)
print(" %6.2f %s" % (groups[ftype][what], pwhat))
print()
design_width = 1
for func in vfuncs:
if design_width < len(vfuncs[func]['design']):
design_width = len(vfuncs[func]['design'])
print("Verilog code profile:")
print(" These are split into three categories:")
print(" C++: Time in non-Verilated C++ code")
print(" Prof: Time in profile overhead")
print(" VBlock: Time attributable to a block in a" +
" Verilog file and line")
print(" VCommon: Time in a Verilated module," +
" due to all parts of the design")
print(" VLib: Time in Verilated common libraries," +
" called by the Verilated code")
print()
print(" % cumulative self ")
print((" time seconds seconds calls %-" + str(design_width) +
"s type filename and line number") % "design")
cume = 0
for func in sorted(vfuncs.keys(),
key=lambda f: vfuncs[f]['sec'],
reverse=True):
cume += vfuncs[func]['sec']
print(("%6.2f %9.2f %8.2f %10d %-" + str(design_width) + "s %s") %
(vfuncs[func]['pct'], cume, vfuncs[func]['sec'],
vfuncs[func]['calls'], vfuncs[func]['design'], func))
foreach my $type (qw(type design module)) {
my $missing = 100;
foreach (sort (keys %{$groups{$type}})) {
$missing -= $groups{$type}{$_};
}
if ($missing) {
$groups{$type}{"\377Unaccounted for/rounding error"} = $missing;
}
######################################################################
######################################################################
print("Overall summary by $type:\n");
printf(" %-6s %s\n","% time",$type);
foreach my $what (sort (keys %{$groups{$type}})) {
(my $pwhat = $what) =~ s/^\377//; # Just used to establish sort order
printf(" %6.2f %s\n", $groups{$type}{$what}, $pwhat);
}
print("\n");
}
print("Verilog code profile:\n");
print(" These are split into three categories:\n");
print(" C++: Time in non-Verilated C++ code\n");
print(" Prof: Time in profile overhead\n");
print(" VBlock: Time attributable to a block in a Verilog file and line\n");
print(" VCommon: Time in a Verilated module, due to all parts of the design\n");
print(" VLib: Time in Verilated common libraries, called by the Verilated code\n");
print("\n");
print(" % cumulative self \n");
print(" time seconds seconds calls type filename and line number\n");
my $cume = 0;
foreach my $func (sort {$vfuncs{$b}{sec} <=> $vfuncs{$a}{sec}
|| $a cmp $b}
(keys %vfuncs)) {
$cume += $vfuncs{$func}{sec};
printf +("%6.2f %9.2f %8.2f %8d %s\n",
$vfuncs{$func}{pct},
$cume, $vfuncs{$func}{sec},
$vfuncs{$func}{calls},
$func);
}
}
#######################################################################
__END__
=pod
=head1 NAME
verilator_profcfunc - Read gprof report created with --prof-cfuncs
=head1 SYNOPSIS
verilator --prof-cfuncs ....
gcc ....
{run executable}
gprof
verilator_profcfuncs gprof.out
=head1 DESCRIPTION
Verilator_profcfunc reads a profile report created by gprof. The names of
parser = argparse.ArgumentParser(
allow_abbrev=False,
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""Read gprof report created with --prof-cfuncs""",
epilog=
"""Verilator_profcfunc reads a profile report created by gprof. The names of
the functions are then transformed, assuming the user used Verilator's
--prof-cfuncs, and a report printed showing the percentage of time, etc,
in each Verilog block.
For documentation see
L<https://verilator.org/guide/latest/exe_verilator_profcfuncs.html>.
https://verilator.org/guide/latest/exe_verilator_profcfunc.html
=head1 ARGUMENT SUMMARY
<filename> Input file (gprof.out)
--help Display this help
=head1 DISTRIBUTION
The latest version is available from L<https://verilator.org>.
Copyright 2007-2021 by Wilson Snyder. This program is free software; you
Copyright 2002-2021 by Wilson Snyder. This program is free software; you
can redistribute it and/or modify it under the terms of either the GNU
Lesser General Public License Version 3 or the Perl Artistic License
Version 2.0.
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
=head1 SEE ALSO
parser.add_argument('--debug',
action='store_const',
const=9,
help='enable debug')
parser.add_argument('filename', help='input gprof output to process')
C<verilator>
and L<https://verilator.org/guide/latest/exe_verilator_profcfuncs.html> for
detailed documentation.
=cut
Args = parser.parse_args()
profcfunc(Args.filename)
######################################################################
### Local Variables:
### compile-command: "$V4/bin/verilator_profcfunc $V4/test_c/obj_dir/V*_03_*.tree $V4N/test_c/obj_dir/V*_03_*.tree"
### End:
# Local Variables:
# compile-command: "./verilator_profcfunc ../test_regress/t/t_profcfunc.gprof"
# End:

View File

@ -7,7 +7,7 @@
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[4.212 2021-09-01],
AC_INIT([Verilator],[4.214 2021-10-17],
[https://verilator.org],
[verilator],[https://verilator.org])
# When releasing, also update header of Changes file
@ -347,22 +347,18 @@ AC_SUBST(CFG_CXXFLAGS_PROFILE)
# Flag to select newest language standard supported
# Macros work such that first option that passes is the one we take
# Currently enabled c++14 due to packaged SystemC dependency
# c++17 is the newest that Verilator supports
# c++03 is the oldest that Verilator supports
# c++14 is the newest that Verilator is regressed to support
# c++11 is the oldest that Verilator supports
# gnu is requried for Cygwin to compile verilated.h successfully
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++20)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++20)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++17)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++14)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++14)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++11)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++11)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++03)
AC_SUBST(CFG_CXXFLAGS_STD_NEWEST)
# And likewise oldest standard (same list above, backwards)
# This is used for internal testing
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++03)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++11)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++14)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++17)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++20)
AC_SUBST(CFG_CXXFLAGS_STD_OLDEST)
# Flags for compiling Verilator internals including parser, and Verilated files
# These turn on extra warnings and are only used with 'configure --enable-ccwarn'

View File

@ -11,6 +11,7 @@ Andreas Kuster
Chris Randall
Conor McCullough
Dan Petrisko
Daniel Bates
David Horton
David Metz
David Stanford
@ -30,6 +31,7 @@ Harald Heckmann
Howard Su
Huang Rui
HyungKi Jeong
Iru Cai
Ivan Vnučec
Iztok Jeras
James Hanlon
@ -46,6 +48,7 @@ Josh Redford
Julien Margetts
Kaleb Barrett
Kanad Kanhere
Keith Colbert
Kevin Kiningham
Krzysztof Bieganski
Kuba Ober
@ -101,3 +104,4 @@ Yossi Nivin
Yuri Victorovich
Yutetsu TAKATSUKASA
Yves Mathieu
Zhanglei Wang

View File

@ -0,0 +1,6 @@
.. comment: generated by t_lint_didnotconverge_bad
.. code-block:: sv
:linenos:
always_comb b = ~a;
always_comb a = b;

View File

@ -0,0 +1,7 @@
.. comment: generated by t_lint_didnotconverge_bad
.. code-block::
-V{t#,#}+ Vt_lint_didnotconverge_bad___024root___change_request
-V{t#,#}+ Vt_lint_didnotconverge_bad___024root___change_request_1
-V{t#,#} CHANGE: t/t_lint_didnotconverge_bad.v:14: a
%Error: t/t_lint_didnotconverge_bad.v:7: Verilated model didn't converge

View File

@ -0,0 +1,4 @@
.. comment: generated by t_lint_didnotconverge_nodbg_bad
.. code-block::
%Error: t/t_lint_didnotconverge_bad.v:7: Verilated model didn't converge

View File

@ -0,0 +1,5 @@
.. comment: generated by t_lint_stmtdly_bad
.. code-block:: sv
:emphasize-lines: 1
#100 $finish; //<--- Warning

View File

@ -0,0 +1,4 @@
.. comment: generated by t_lint_stmtdly_bad
.. code-block::
%Warning-STMTDLY: example.v:1:8 Unsupported: Ignoring delay on this delayed statement.

View File

@ -1,4 +1,4 @@
# pylint: disable=C0103,C0114,C0116,E0402,W0622
# pylint: disable=C0103,C0114,C0116,C0301,E0402,W0622
#
# Configuration file for Verilator's Sphinx documentation builder.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0

View File

@ -62,6 +62,12 @@ Summary:
makes sense for a single-clock-domain module where it's typical to want
to capture one posedge eval() and one negedge eval().
.. option:: +verilator+prof+vlt+file+<filename>
When a model was Verilated using :vlopt:`--prof-threads`, sets the
profile-guided optimization data runtime filename to dump to. Defaults
to :file:`profile.vlt`.
.. option:: +verilator+rand+reset+<value>
When a model was Verilated using :vlopt:`--x-initial unique

View File

@ -816,7 +816,7 @@ Summary:
When compiling the C++ code, enable the compiler's profiling flag
(e.g. :code:`g++ -pg`). See :ref:`Profiling`.
Using :vlopt:`--prof-cfuncs` also enables :vlopt:`prof-c`.
Using :vlopt:`--prof-cfuncs` also enables :vlopt:`--prof-c`.
.. option:: --prof-cfuncs
@ -828,12 +828,12 @@ Summary:
came from. This allows gprof or oprofile reports to be correlated with
the original Verilog source statements. See :ref:`Profiling`.
Using :vlopt:`--prof-cfuncs` also enables :vlopt:`prof-c`.
Using :vlopt:`--prof-cfuncs` also enables :vlopt:`--prof-c`.
.. option:: --prof-threads
Enable gantt chart data collection for threaded builds. See :ref:`Thread
Profiling`.
Profiling` and :ref:`Thread PGO`.
.. option:: --protect-key <key>
@ -1612,6 +1612,12 @@ The grammar of configuration commands is as follows:
:option:`/*verilator&32;public_flat*/`, etc, metacomments. See
e.g. :ref:`VPI Example`.
.. option:: profile_data -mtask "<mtask_hash>" -cost <cost_value>
Feeds profile-guided optimization data into the Verilator algorithms in
order to improve model runtime performance. This option is not expected
to be used by users directly. See :ref:`Thread PGO`.
.. option:: sc_bv -module "<modulename>" [-task "<taskname>"] -var "<signame>"
.. option:: sc_bv -module "<modulename>" [-function "<funcname>"] -var "<signame>"

View File

@ -10,42 +10,60 @@ starts and ends, and showing when each thread is busy or idle.
For an overview of use of verilator_gantt, see :ref:`Profiling`.
Gantt Chart Contents
--------------------
Gantt Chart VCD
---------------
The generated Gantt chart has time on the X-axis. Times shown are to the
scale printed, i.e. a certain about of time for each character width. The
Y-axis shows threads, each thread's execution is shown on one line. That
line shows "[" at the position in time when it executes.
Verilated_gnatt creates a value change dump (VCD) format dump file which
may be viewed in a waveform viewer (e.g. C<GTKWave>):
Following the "[" is the CPU number the task executed on, followed by zero
or more "-" to make the width of the characters match the scaled execution
time, followed by a "]". If the scale is too small, the CPU number and
mtask number will not be printed. If the scale is very small, a "&"
indicates multiple mtasks started at that time position.
.. figure:: figures/fig_gantt_min.png
Example verilator_gantt output, as viewed with GTKWave.
The viewed waveform chart has time on the X-axis, with one unit for each
time tick of the system's high-performance counter.
Also creates a value change dump (VCD) format dump file which may be viewed
in a waveform viewer (e.g. C<GTKWave>). See below.
Gantt Chart VCD Signals
-----------------------
In waveforms there are the following signals. Most signals the "decimal"
format will remove the leading zeros and make the traces easier to read.
In waveforms there are the following signals. In GTKWave, using a data
format of "decimal" will remove the leading zeros and make the traces
easier to read.
parallelism
evals
Increments each time when eval_step was measured to be active. This
allow visualization of how much time eval_step was active.
eval_loop
Increments each time when the evaluation loop within eval_step was
measured to be active. For best performance there is only a single
evaluation loop within each eval_step call, that is the eval_loop
waveform looks identical to the evals waveform.
measured_parallelism
The number of mtasks active at this time, for best performance this will
match the thread count. You may want to use an "analog step" format to
match the thread count. In GTKWave, use a data format of "analog step" to
view this signal.
predicted_parallelism
The number of mtasks Verilator predicted would be active at this time,
for best performance this will match the thread count. In GTKWave, use a
data format of "analog step" to view this signal.
cpu#_thread
For the given CPU number, the thread number executing.
For the given CPU number, the thread number measured to be executing.
mtask#_cpu
For the given mtask id, the CPU it is executing on.
For the given mtask id, the CPU it was measured to execute on.
thread#_mtask
For the given thread number, the mtask id executing.
For the given thread number, the mtask id it was executing.
predicted_thread#_mtask
For the given thread number, the mtask id Verilator predicted would be
executing.
verilator_gantt Arguments
-------------------------
@ -60,13 +78,6 @@ The filename to read data from, defaults to "profile_threads.dat".
Displays a help summary, the program version, and exits.
.. option:: --scale <n>
Sets the number of time units per character on the X-axis of the generated
Gantt chart. (On x86, time units are rdtsc ticks.) Defaults to 0, which
will automatically compute a reasonable scale where no two mtasks need to
fit into same character width's worth of scaled time.
.. option:: --no-vcd
Disables creating a .vcd file.

View File

@ -1,20 +1,24 @@
.. Copyright 2003-2021 by Wilson Snyder.
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
verilator_profcfuncs
====================
verilator_profcfunc
===================
Verilator_profcfunc reads a profile report created by gprof. The names of
the functions are then transformed, assuming the user used Verilator's
--prof-cfuncs, and a report printed showing the percentage of time, etc, in
each Verilog block.
For an overview of use of verilator_profcfuncs, see :ref:`Profiling`.
Due to rounding errors in gprof reports, the input report's percentages may
not total to 100%. In the verilator_profcfunc report this will get
reported as a rounding error.
verilator_profcfuncs Arguments
------------------------------
For an overview of use of verilator_profcfunc, see :ref:`Profiling`.
.. program:: verilator_profcfuncs
verilator_profcfunc Arguments
-----------------------------
.. program:: verilator_profcfunc
.. option:: <filename>

View File

@ -15,5 +15,5 @@ options to each executable.
exe_verilator.rst
exe_verilator_coverage.rst
exe_verilator_gantt.rst
exe_verilator_profcfuncs.rst
exe_verilator_profcfunc.rst
exe_sim.rst

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

@ -74,7 +74,7 @@ For --cc/--sc, it creates:
- DPI import and export declarations (from --dpi)
* - *{prefix}*\ __Dpi.cpp
- Global DPI export wrappers (from --dpi)
* - *{prefix}*\ __Dpi_Export\ *{__n}\ .cpp
* - *{prefix}*\ __Dpi_Export\ *{__n}*\ .cpp
- DPI export wrappers scoped to this particular model (from --dpi)
* - *{prefix}*\ __Inlines.h
- Inline support functions
@ -145,3 +145,23 @@ After running Make, the C++ compiler may produce the following:
- Intermediate dependencies
* - *{prefix}{misc}*\ .o
- Intermediate objects
The Verilated executable may produce the following:
.. list-table::
* - coverage.dat
- Code coverage output, and default input filename for :command:`verilator_coverage`
* - gmon.out
- GCC/clang code profiler output, often fed into :command:`verilator_profcfunc`
* - profile.vlt
- -profile data file for :ref:`Thread PGO`
* - profile_threads.dat
- -profile-threads data file for :command:`verilator_gnatt`
Verilator_gantt may produce the following:
.. list-table::
* - profile_threads.vcd
- Gantt report waveform output

View File

@ -51,15 +51,15 @@ In brief, to install from git:
unsetenv VERILATOR_ROOT # For csh; ignore error if on bash
unset VERILATOR_ROOT # For bash
cd verilator
git pull # Make sure git repository is up-to-date
git tag # See what versions exist
git pull # Make sure git repository is up-to-date
git tag # See what versions exist
#git checkout master # Use development branch (e.g. recent bug fixes)
#git checkout stable # Use most recent stable release
#git checkout v{version} # Switch to specified release version
autoconf # Create ./configure script
./configure # Configure and create Makefile
make -j # Build Verilator itself
autoconf # Create ./configure script
./configure # Configure and create Makefile
make -j `nproc` # Build Verilator itself (if error, try just 'make')
sudo make install
@ -287,7 +287,7 @@ Compile Verilator:
::
make -j
make -j `nproc` # Or if error on `nproc`, the number of CPUs in system
Test

View File

@ -26,7 +26,8 @@ risk of reset bugs in trade for performance; see the above documentation
for these options.
If using Verilated multithreaded, use ``numactl`` to ensure you are using
non-conflicting hardware resources. See :ref:`Multithreading`.
non-conflicting hardware resources. See :ref:`Multithreading`. Also
consider using profile-guided optimization, see :ref:`Thread PGO`.
Minor Verilog code changes can also give big wins. You should not have any
UNOPTFLAT warnings from Verilator. Fixing these warnings can result in
@ -93,9 +94,7 @@ cases, for example regressions, it is usually worth spending extra
compilation time to reduce total CPU time.
If you will be running many simulations on a single model, you can
investigate profile guided optimization. With GCC, using GCC's
"-fprofile-arcs", then GCC's "-fbranch-probabilities" will yield another
15% or so.
investigate profile guided optimization. See :ref:`Compiler PGO`.
Modern compilers also support link-time optimization (LTO), which can help
especially if you link in DPI code. To enable LTO on GCC, pass "-flto" in
@ -230,7 +229,7 @@ coverage point insertions into the model and collect the coverage data.
To get the coverage data from the model, in the user wrapper code,
typically at the end once a test passes, call
:code:`Verilated::coveragep()->write` with an argument of the filename for
:code:`Verilated::threadContextp()->coveragep()->write` with an argument of the filename for
the coverage data file to write coverage data to (typically
"logs/coverage.dat").
@ -298,6 +297,9 @@ With the :vlopt:`--prof-threads` option, Verilator will:
* Add code to save profiling data in non-human-friendly form to the file
specified with :vlopt:`+verilator+prof+threads+file+\<filename\>`.
* Add code to save profiling data for thread profile-guided
optimization. See :ref:`Thread PGO`.
The :command:`verilator_gantt` program may then be run to transform the
saved profiling file into a nicer visual format and produce some related
statistics.
@ -306,7 +308,7 @@ statistics.
Example verilator_gantt output, as viewed with GTKWave.
The parallelism shows the number of CPUs being used at a given moment.
The measured_parallelism shows the number of CPUs being used at a given moment.
The cpu_thread section shows which thread is executing on each of the physical CPUs.
@ -314,6 +316,7 @@ statistics.
For more information see :command:`verilator_gantt`.
.. _Profiling ccache efficiency:
Profiling ccache efficiency
@ -377,3 +380,120 @@ For example:
os >> main_time;
os >> *topp;
}
Profile-Guided Optimization
===========================
Profile-guided optimization is the technique where profiling data is
collected by running your simulation executable, then this information is
used to guide the next Verilation or compilation.
There are two forms of profile-guided optimizations. Unfortunately for
best results they must each be performed from the highest level code to the
lowest, which means performing them separately and in this order:
* :ref:`Thread PGO`
* :ref:`Compiler PGO`
Other forms of PGO may be supported in the future, such as clock and reset
toggle rate PGO, branch prediction PGO, statement execution time PGO, or
others as they prove beneficial.
.. _Thread PGO:
Thread Profile-Guided Optimization
----------------------------------
Verilator supports thread profile-guided optimization (Thread PGO) to
improve multithreaded performance.
When using multithreading, Verilator computes how long macro tasks take and
tries to balance those across threads. (What is a macro-task? See the
Verilator internals document (:file:`docs/internals.rst` in the
distribution.) If the estimations are incorrect, the threads will not be
balanced, leading to decreased performance. Thread PGO allows collecting
profiling data to replace the estimates and better optimize these
decisions.
To use Thread PGO, Verilate the model with the :vlopt:`--prof-threads`
option.
Run the model executable. When the executable exits, it will create a
profile.vlt file.
Rerun Verilator, optionally omitting the :vlopt:`--prof-threads` option,
and adding the profile.vlt generated earlier to the command line.
Note there is no Verilator equivalent to GCC's --fprofile-use. Verilator's
profile data file (profile.vlt) can be placed on the verilator command line
directly without any prefix.
If results from multiple simulations are to be used in generating the
optimization, multiple simulation's profile.vlt may be concatenated
externally, or each of the files may be fed as separate command line
options into Verilator. Verilator will simply sum the profile results, so
a longer running test will have proportionally more weight for optimization
than a shorter running test.
If you provide any profile feedback data to Verilator, and it cannot use
it, it will issue the :option:`PROFOUTOFDATE` warning that threads were
scheduled using estimated costs. This usually indicates that the profile
data was generated from different Verilog source code than Verilator is
currently running against. Therefore, repeat the data collection phase to
create new profiling data, then rerun Verilator with the same input source
files and that new profiling data.
.. _Compiler PGO:
Compiler Profile-Guided Optimization
------------------------------------
GCC and Clang support compiler profile-guided optimization (PGO). This
optimizes any C/C++ program including Verilated code. Using compiler PGO
typically yields improvements of 5-15% on both single-threaded and
multi-threaded models.
To use compiler PGO with GCC or Clang, please see the appropriate compiler
documentation. The process in GCC 10 was as follows:
1. Compile the Verilated model with the compiler's "-fprofile-generate"
flag:
.. code-block:: bash
verilator [whatever_flags] --make \
-CFLAGS -fprofile-generate -LDFLAGS -fprofile-generate
or, if calling make yourself, add -fprofile-generate appropriately to your
Makefile.
2. Run your simulation. This will create \*.gcda file(s) in the same
directory as the source files.
3. Recompile the model with -fprofile-use. The compiler will read the
\*.gcda file(s).
For GCC:
.. code-block:: bash
verilator [whatever_flags] --build \
-CFLAGS "-fprofile-use -fprofile-correction"
For Clang:
.. code-block:: bash
llvm-profdata merge -output default.profdata *.profraw
verilator [whatever_flags] --build \
-CFLAGS "-fprofile-use -fprofile-correction"
or, if calling make yourself, add these CFLAGS switches appropriately to
your Makefile.
Clang and GCC also support -fauto-profile which uses sample-based
feedback-directed optimization. See the appropriate compiler
documentation.

View File

@ -130,9 +130,10 @@ List Of Warnings
.. option:: BADSTDPRAGMA
Error that a pragma is badly formed, when that pragma is defined by IEEE1800-2017.
For example, an empty `pragma line, or an incorrect specified '`pragma protect'.
Note that 3rd party pragmas not defined by IEEE1800-2017 are ignored.
Error that a pragma is badly formed, when that pragma is defined by IEEE
1800-2017. For example, an empty pragma line, or an incorrect specified
'pragma protect'. Note that 3rd party pragmas not defined by IEEE
1800-2017 are ignored.
.. option:: BLKANDNBLK
@ -481,39 +482,44 @@ List Of Warnings
Faulty example:
.. code-block:: sv
.. include:: ../../docs/gen/ex_DIDNOTCONVERGE_faulty.rst
always_comb b = ~a;
always_comb a = b
Results in at runtime (not when Verilated):
This code will toggle forever, and thus to prevent an infinite loop, the
executable will give the didn't converge error.
.. include:: ../../docs/gen/ex_DIDNOTCONVERGE_nodbg_msg.rst
To debug this, first review any UNOPTFLAT warnings that were ignored.
Though typically it is safe to ignore UNOPTFLAT (at a performance cost),
at the time of issuing a UNOPTFLAT Verilator did not know if the logic
would eventually converge and assumed it would.
This is because the signals keep toggling even with out time
passing. Thus to prevent an infinite loop, the Verilated executable
gives the DIDNOTCONVERGE error.
To debug this, first review any UNOPT or UNOPTFLAT warnings that were
ignored. Though typically it is safe to ignore UNOPTFLAT (at a
performance cost), at the time of issuing a UNOPTFLAT Verilator did not
know if the logic would eventually converge and assumed it would.
Next, run Verilator with :vlopt:`--prof-cfuncs -CFLAGS -DVL_DEBUG
<--prof-cfuncs>`. Rerun the test. Now just before the convergence
error you should see additional output similar to this:
.. code-block::
.. include:: ../../docs/gen/ex_DIDNOTCONVERGE_msg.rst
CHANGE: filename.v:1: b
CHANGE: filename.v:2: a
This means that signal b and signal a keep changing, inspect the code
that modifies these signals. Note if many signals are getting printed
then most likely all of them are oscillating. It may also be that
e.g. "a" may be oscillating, then "a" feeds signal "c" which then is
also reported as oscillating.
The CHANGE line means that on the given filename and line number that
drove a signal, the signal 'a' kept changing. Inspect the code that
modifies these signals. Note if many signals are getting printed then
most likely all of them are oscillating. It may also be that e.g. "a"
may be oscillating, then "a" feeds signal "c" which then is also
reported as oscillating.
One way DIDNOTCONVERGE may occur is flops are built out of gate
primitives. error. Verilator does not support building flops or latches
out of gate primitives, and any such code must change to use behavioral
primitives. Verilator does not support building flops or latches out of
gate primitives, and any such code must change to use behavioral
constructs (e.g. always_ff and always_latch).
Another way DIDNOTCONVERGE may occur is if # delays are used to generate
clocks. Verilator ignores the delays and gives an :option:`ASSIGNDLY`
or :option:`STMTDLY` warning. If these were suppressed, due to the
absense of the delay, the code may now oscillate.
Finally, rare, more difficult cases can be debugged like a C++ program;
either enter :command:`gdb` and use its tracing facilities, or edit the
generated C++ code to add appropriate prints to see what is going on.
@ -572,7 +578,7 @@ List Of Warnings
must end in newline, as otherwise for example :command:`cat` with the
file as an argument may produce undesirable results.
Repair by adding a newline to the end of the file.
Repair by appending a newline to the end of the file.
Disabled by default as this is a code style warning; it will simulate
correctly.
@ -792,8 +798,8 @@ List Of Warnings
.. TODO better example
Warns that a module has multiple definitions. Generally this indicates
a coding error, or a mistake in a library file and it's good practice to
have one module per file (and only put each file once on the command
a coding error, or a mistake in a library file, and it's good practice
to have one module per file (and only put each file once on the command
line) to avoid these issues. For some gate level netlists duplicates
are sometimes unavoidable, and MODDUP should be disabled.
@ -1005,9 +1011,25 @@ List Of Warnings
a var/reg must be used as the target of procedural assignments.
.. option:: PROFOUTOFDATE
Warns that threads were scheduled using estimated costs, despite the
fact that data was provided from profile-guided optimization (see
:ref:`Thread PGO`) as fed into Verilator using the
:option:`profile_data` configuration file option. This usually
indicates that the profile data was generated from different Verilog
source code than Verilator is currently running against.
It is recommended to create new profiling data, then rerun Verilator
with the same input source files and that new profiling data.
Ignoring this warning may only slow simulations, it will simulate
correctly.
.. option:: PROTECTED
Warning that a '`pragma protected' section was encountered. The code
Warning that a 'pragma protected' section was encountered. The code
inside the protected region will be partly checked for correctness, but is
otherwise ignored.
@ -1183,9 +1205,25 @@ List Of Warnings
Faulty example:
.. code-block:: sv
.. include:: ../../docs/gen/ex_STMTDLY_faulty.rst
#100 $finish; //<--- Warning
Results in:
.. include:: ../../docs/gen/ex_STMTDLY_msg.rst
This is a warning because Verilator does not support delayed statements.
It will simply ignore all such delays. In many cases ignoring a delay
might be harmless, but if the delayed statement is, as in this example,
used to cause some important action at a later time, it might be an
important difference.
Some possible work arounds:
* Move the delayed statement into the C++ wrapper file, where the
stimulus and clock generation can be done in C++.
* Convert the statement into a FSM, or other statement that tests
against $time.
.. option:: SYMRSVDWORD
@ -1353,7 +1391,9 @@ List Of Warnings
Often UNOPTFLAT is caused by logic that isn't truly circular as viewed by
synthesis which analyzes interconnection per-bit, but is circular to
simulation which analyzes per-bus:
simulation which analyzes per-bus.
Faulty example:
.. code-block:: sv
@ -1466,9 +1506,16 @@ List Of Warnings
Error that a construct might be legal according to IEEE but is not
currently supported by Verilator.
A typical workaround is to recode the construct into a simpler and more
common alternative language construct.
Alternatively, check if the construct is supported by other tools, and
if so please consider submitting a github pull request against the
Verilator sources to implement the missing unsupported feature.
This error may be ignored with :vlopt:`--bbox-unsup`, however this will
make the design simulate incorrectly; see the details under
:vlopt:`--bbox-unsup`.
make the design simulate incorrectly and is only intended for lint
usage; see the details under :vlopt:`--bbox-unsup`.
.. option:: UNUSED
@ -1598,7 +1645,7 @@ List Of Warnings
* A part select has a different size then needed to index into the
packed or unpacked array (etc).
Verilator for attempts to track the minimum width of unsized constants
Verilator attempts to track the minimum width of unsized constants
and will suppress the warning when the minimum width is appropriate to
fit the required size.
@ -1607,11 +1654,13 @@ List Of Warnings
The recommendation is to fix these issues by:
* Resizing the variable or constant. E.g. :code:`2'd2` instead of :code:`3'd2`.
* Resizing the variable or constant to match the needed size for the
expression. E.g. :code:`2'd2` instead of :code:`3'd2`.
* Using :code:`'0` or :code:`'1`.
* Using :code:`'0` or :code:`'1` which automatically resize in an
expression.
* Using part select to narrow a variable. E.g. :code:`too_wide[1:0]`.
* Using part selects to narrow a variable. E.g. :code:`too_wide[1:0]`.
* Using concatenate to widen a variable. E.g. :code:`{1'b1, too_narrow}`.

View File

@ -405,6 +405,9 @@ routines in the sources to rely more heavily on randomness, and
generally try harder not to keep input nodes together when we have the
option to scramble things.
Profile-guided optimization make this a bit better, by adjusting mtask
scheduling, but this does not yet guide the packing into mtasks.
Performance Regression
""""""""""""""""""""""

View File

@ -676,7 +676,6 @@ prev
printf
printtimescale
profcfunc
profcfuncs
prototyptes
ps
pthread

View File

@ -2258,6 +2258,7 @@ VerilatedContext::VerilatedContext()
Verilated::lastContextp(this);
Verilated::threadContextp(this);
m_ns.m_profThreadsFilename = "profile_threads.dat";
m_ns.m_profVltFilename = "profile.vlt";
m_fdps.resize(31);
std::fill(m_fdps.begin(), m_fdps.end(), static_cast<FILE*>(nullptr));
m_fdFreeMct.resize(30);
@ -2340,6 +2341,14 @@ std::string VerilatedContext::profThreadsFilename() const VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
return m_ns.m_profThreadsFilename;
}
void VerilatedContext::profVltFilename(const std::string& flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_ns.m_profVltFilename = flag;
}
std::string VerilatedContext::profVltFilename() const VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
return m_ns.m_profVltFilename;
}
void VerilatedContext::randReset(int val) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_s.m_randReset = val;
@ -2495,6 +2504,8 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
profThreadsWindow(std::atol(value.c_str()));
} else if (commandArgVlValue(arg, "+verilator+prof+threads+file+", value /*ref*/)) {
profThreadsFilename(value);
} else if (commandArgVlValue(arg, "+verilator+prof+vlt+file+", value /*ref*/)) {
profVltFilename(value);
} else if (commandArgVlValue(arg, "+verilator+rand+reset+", value /*ref*/)) {
randReset(std::atoi(value.c_str()));
} else if (commandArgVlValue(arg, "+verilator+seed+", value /*ref*/)) {

View File

@ -346,6 +346,7 @@ protected:
vluint32_t m_profThreadsWindow = 2; // +prof+threads window size
// Slow path
std::string m_profThreadsFilename; // +prof+threads filename
std::string m_profVltFilename; // +prof+vlt filename
} m_ns;
mutable VerilatedMutex m_argMutex; // Protect m_argVec, m_argVecLoaded
@ -522,6 +523,8 @@ public: // But for internal use only
vluint32_t profThreadsWindow() const VL_MT_SAFE { return m_ns.m_profThreadsWindow; }
void profThreadsFilename(const std::string& flag) VL_MT_SAFE;
std::string profThreadsFilename() const VL_MT_SAFE;
void profVltFilename(const std::string& flag) VL_MT_SAFE;
std::string profVltFilename() const VL_MT_SAFE;
// Internal: Find scope
const VerilatedScope* scopeFind(const char* namep) const VL_MT_SAFE;

View File

@ -23,8 +23,6 @@ CFG_WITH_LONGTESTS = @CFG_WITH_LONGTESTS@
CFG_CXXFLAGS_PROFILE = @CFG_CXXFLAGS_PROFILE@
# Select newest language
CFG_CXXFLAGS_STD_NEWEST = @CFG_CXXFLAGS_STD_NEWEST@
# Select oldest language (for Verilator internal testing only)
CFG_CXXFLAGS_STD_OLDEST = @CFG_CXXFLAGS_STD_OLDEST@
# Compiler flags to use to turn off unused and generated code warnings, such as -Wno-div-by-zero
CFG_CXXFLAGS_NO_UNUSED = @CFG_CXXFLAGS_NO_UNUSED@
# Compiler flags that turn on extra warnings

View File

@ -364,7 +364,7 @@ public:
std::ofstream os{filename};
if (os.fail()) {
std::string msg = std::string{"%Error: Can't write '"} + filename + "'";
const std::string msg = std::string{"%Error: Can't write '"} + filename + "'";
VL_FATAL_MT("", 0, "", msg.c_str());
return;
}

View File

@ -82,9 +82,12 @@ extern IData VL_RANDOM_SEEDED_II(int obits, IData seed) VL_MT_SAFE;
inline IData VL_URANDOM_RANGE_I(IData hi, IData lo) {
vluint64_t rnd = vl_rand64();
if (VL_LIKELY(hi > lo)) {
// (hi - lo + 1) can be zero when hi is UINT_MAX and lo is zero
if (VL_UNLIKELY(hi - lo + 1 == 0)) return rnd;
// Modulus isn't very fast but it's common that hi-low is power-of-two
return (rnd % (hi - lo + 1)) + lo;
} else {
if (VL_UNLIKELY(lo - hi + 1 == 0)) return rnd;
return (rnd % (lo - hi + 1)) + hi;
}
}

View File

@ -549,8 +549,8 @@ public: // But only for verilated*.cpp
const VerilatedLockGuard lock{s().m_exportMutex};
const auto& it = s().m_exportMap.find(namep);
if (VL_LIKELY(it != s().m_exportMap.end())) return it->second;
std::string msg = (std::string{"%Error: Testbench C called "} + namep
+ " but no such DPI export function name exists in ANY model");
const std::string msg = (std::string{"%Error: Testbench C called "} + namep
+ " but no such DPI export function name exists in ANY model");
VL_FATAL_MT("unknown", 0, "", msg.c_str());
return -1;
}

View File

@ -0,0 +1,113 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//=============================================================================
//
// Code available from: https://verilator.org
//
// Copyright 2012-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//=============================================================================
///
/// \file
/// \brief Verilated general profiling header
///
/// This file is not part of the Verilated public-facing API.
/// It is only for internal use by Verilated library routines.
///
//=============================================================================
#ifndef VERILATOR_VERILATED_PROFILER_H_
#define VERILATOR_VERILATED_PROFILER_H_
#include "verilatedos.h"
#include "verilated.h" // for VerilatedMutex and clang annotations
// Profile record, private class used only by this header
class VerilatedProfilerRec final {
std::string m_name; // Hashed name of mtask/etc
size_t m_counterNumber = 0; // Which counter has data
public:
// METHODS
VerilatedProfilerRec(size_t counterNumber, const std::string& name)
: m_name{name}
, m_counterNumber{counterNumber} {}
VerilatedProfilerRec() = default;
size_t counterNumber() const { return m_counterNumber; }
std::string name() const { return m_name; }
};
// Create some number of bucketed profilers
template <std::size_t T_Entries> class VerilatedProfiler final {
// Counters are stored packed, all together, versus in VerilatedProfilerRec to
// reduce cache effects
std::array<vluint64_t, T_Entries> m_counters{}; // Time spent on this record
std::deque<VerilatedProfilerRec> m_records; // Record information
public:
// METHODS
VerilatedProfiler() = default;
~VerilatedProfiler() = default;
void write(const char* modelp, const std::string& filename) VL_MT_SAFE;
void addCounter(size_t counter, const std::string& name) {
VL_DEBUG_IF(assert(counter < T_Entries););
m_records.emplace_back(VerilatedProfilerRec{counter, name});
}
void startCounter(size_t counter) {
vluint64_t val;
VL_RDTSC(val);
// -= so when we add end time in stopCounter, we already subtracted
// out, without needing to hold another temporary
m_counters[counter] -= val;
}
void stopCounter(size_t counter) {
vluint64_t val;
VL_RDTSC(val);
m_counters[counter] += val;
}
};
template <std::size_t T_Entries>
void VerilatedProfiler<T_Entries>::write(const char* modelp,
const std::string& filename) VL_MT_SAFE {
static VerilatedMutex s_mutex;
const VerilatedLockGuard lock{s_mutex};
// On the first call we create the file. On later calls we append.
// So when we have multiple models in an executable, possibly even
// running on different threads, each will have a different symtab so
// each will collect is own data correctly. However when each is
// destroid we need to get all the data, not keep overwriting and only
// get the last model's data.
static bool s_firstCall = true;
VL_DEBUG_IF(VL_DBG_MSGF("+prof+vlt+file writing to '%s'\n", filename.c_str()););
FILE* fp = nullptr;
if (!s_firstCall) fp = std::fopen(filename.c_str(), "a");
if (VL_UNLIKELY(!fp))
fp = std::fopen(filename.c_str(), "w"); // firstCall, or doesn't exist yet
if (VL_UNLIKELY(!fp)) {
VL_FATAL_MT(filename.c_str(), 0, "", "+prof+vlt+file file not writable");
// cppcheck-suppress resourceLeak // bug, doesn't realize fp is nullptr
return; // LCOV_EXCL_LINE
}
s_firstCall = false;
// TODO Perhaps merge with verilated_coverage output format, so can
// have a common merging and reporting tool, etc.
fprintf(fp, "// Verilated model profile-guided optimization data dump file\n");
fprintf(fp, "`verilator_config\n");
for (const auto& it : m_records) {
const std::string& name = it.name();
fprintf(fp, "profile_data -model \"%s\" -mtask \"%s\" -cost 64'd%" VL_PRI64 "u\n", modelp,
name.c_str(), m_counters[it.counterNumber()]);
}
std::fclose(fp);
}
#endif

View File

@ -204,7 +204,7 @@ void VerilatedSave::flush() VL_MT_UNSAFE_ONE {
if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) {
// LCOV_EXCL_START
// write failed, presume error (perhaps out of disk space)
std::string msg = std::string{__FUNCTION__} + ": " + std::strerror(errno);
const std::string msg = std::string{__FUNCTION__} + ": " + std::strerror(errno);
VL_FATAL_MT("", 0, "", msg.c_str());
close();
break;

View File

@ -25,6 +25,7 @@
#include "verilated_threads.h"
#include <cstdio>
#include <fstream>
//=============================================================================
// Globals
@ -145,7 +146,7 @@ void VlThreadPool::profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES
}
}
void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
void VlThreadPool::profileDump(const char* filenamep, vluint64_t tickStart, vluint64_t tickEnd)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
VL_DEBUG_IF(VL_DBG_MSGF("+prof+threads writing to '%s'\n", filenamep););
@ -159,7 +160,7 @@ void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
// TODO Perhaps merge with verilated_coverage output format, so can
// have a common merging and reporting tool, etc.
fprintf(fp, "VLPROFTHREAD 1.0 # Verilator thread profile dump version 1.0\n");
fprintf(fp, "VLPROFTHREAD 1.1 # Verilator thread profile dump version 1.1\n");
fprintf(fp, "VLPROF arg --threads %" VL_PRI64 "u\n", vluint64_t(m_workers.size() + 1));
fprintf(fp, "VLPROF arg +verilator+prof+threads+start+%" VL_PRI64 "u\n",
Verilated::threadContextp()->profThreadsStart());
@ -167,6 +168,16 @@ void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
Verilated::threadContextp()->profThreadsWindow());
fprintf(fp, "VLPROF stat yields %" VL_PRI64 "u\n", VlMTaskVertex::yields());
// Copy /proc/cpuinfo into this output so verilator_gantt can be run on
// a different machine
{
const std::unique_ptr<std::ifstream> ifp{new std::ifstream("/proc/cpuinfo")};
if (!ifp->fail()) {
std::string line;
while (std::getline(*ifp, line)) { fprintf(fp, "VLPROFPROC %s\n", line.c_str()); }
}
}
vluint32_t thread_id = 0;
for (const auto& pi : m_allProfiles) {
++thread_id;
@ -177,20 +188,36 @@ void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
case VlProfileRec::TYPE_BARRIER: //
printing = true;
break;
case VlProfileRec::TYPE_EVAL:
if (!printing) break;
fprintf(fp,
"VLPROF eval start %" VL_PRI64 "u elapsed %" VL_PRI64 "u"
" cpu %u on thread %u\n",
ei.m_startTime - tickStart, (ei.m_endTime - ei.m_startTime), ei.m_cpu,
thread_id);
break;
case VlProfileRec::TYPE_EVAL_LOOP:
if (!printing) break;
fprintf(fp,
"VLPROF eval_loop start %" VL_PRI64 "u elapsed %" VL_PRI64 "u"
" cpu %u on thread %u\n",
ei.m_startTime - tickStart, (ei.m_endTime - ei.m_startTime), ei.m_cpu,
thread_id);
break;
case VlProfileRec::TYPE_MTASK_RUN:
if (!printing) break;
fprintf(fp,
"VLPROF mtask %d"
" start %" VL_PRI64 "u end %" VL_PRI64 "u elapsed %" VL_PRI64 "u"
" predict_time %u cpu %u on thread %u\n",
ei.m_mtaskId, ei.m_startTime, ei.m_endTime,
(ei.m_endTime - ei.m_startTime), ei.m_predictTime, ei.m_cpu, thread_id);
" start %" VL_PRI64 "u elapsed %" VL_PRI64 "u"
" predict_start %u predict_cost %u cpu %u on thread %u\n",
ei.m_mtaskId, ei.m_startTime - tickStart, (ei.m_endTime - ei.m_startTime),
ei.m_predictStart, ei.m_predictCost, ei.m_cpu, thread_id);
break;
default: assert(false); break; // LCOV_EXCL_LINE
}
}
}
fprintf(fp, "VLPROF stat ticks %" VL_PRI64 "u\n", ticksElapsed);
fprintf(fp, "VLPROF stat ticks %" VL_PRI64 "u\n", tickEnd - tickStart);
std::fclose(fp);
}

View File

@ -131,21 +131,36 @@ public:
class VlProfileRec final {
protected:
friend class VlThreadPool;
enum VlProfileE { TYPE_MTASK_RUN, TYPE_BARRIER };
VlProfileE m_type = TYPE_BARRIER; // Record type
vluint32_t m_mtaskId = 0; // Mtask we're logging
vluint32_t m_predictTime = 0; // How long scheduler predicted would take
vluint64_t m_startTime = 0; // Tick at start of execution
enum VlProfileE { TYPE_MTASK_RUN, TYPE_EVAL, TYPE_EVAL_LOOP, TYPE_BARRIER };
// Layout below allows efficient packing.
// Leave endTime first, so no math needed to calculate address in endRecord
vluint64_t m_endTime = 0; // Tick at end of execution
vluint64_t m_startTime = 0; // Tick at start of execution
vluint32_t m_mtaskId = 0; // Mtask we're logging
vluint32_t m_predictStart = 0; // Time scheduler predicted would start
vluint32_t m_predictCost = 0; // How long scheduler predicted would take
VlProfileE m_type = TYPE_BARRIER; // Record type
unsigned m_cpu; // Execution CPU number (at start anyways)
public:
class Barrier {};
VlProfileRec() = default;
explicit VlProfileRec(Barrier) { m_cpu = getcpu(); }
void startRecord(vluint64_t time, uint32_t mtask, uint32_t predict) {
void startEval(vluint64_t time) {
m_type = VlProfileRec::TYPE_EVAL;
m_startTime = time;
m_cpu = getcpu();
}
void startEvalLoop(vluint64_t time) {
m_type = VlProfileRec::TYPE_EVAL_LOOP;
m_startTime = time;
m_cpu = getcpu();
}
void startRecord(vluint64_t time, vluint32_t mtask, vluint32_t predictStart,
vluint32_t predictCost) {
m_type = VlProfileRec::TYPE_MTASK_RUN;
m_mtaskId = mtask;
m_predictTime = predict;
m_predictStart = predictStart;
m_predictCost = predictCost;
m_startTime = time;
m_cpu = getcpu();
}
@ -292,7 +307,8 @@ public:
return &(t_profilep->back());
}
void profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex);
void profileDump(const char* filenamep, vluint64_t ticksElapsed) VL_MT_SAFE_EXCLUDES(m_mutex);
void profileDump(const char* filenamep, vluint64_t tickStart, vluint64_t tickEnd)
VL_MT_SAFE_EXCLUDES(m_mutex);
// In profiling mode, each executing thread must call
// this once to setup profiling state:
void setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex);

View File

@ -481,8 +481,8 @@ void VerilatedTrace<VL_DERIVED_T>::addCallbackRecord(std::vector<CallbackRecord>
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
if (VL_UNCOVERABLE(timeLastDump() != 0)) { // LCOV_EXCL_START
std::string msg = (std::string{"Internal: "} + __FILE__ + "::" + __FUNCTION__
+ " called with already open file");
const std::string msg = (std::string{"Internal: "} + __FILE__ + "::" + __FUNCTION__
+ " called with already open file");
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
} // LCOV_EXCL_STOP
cbVec.push_back(cbRec);

View File

@ -472,7 +472,12 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
(val) = ((vluint64_t)lo) | (((vluint64_t)hi) << 32); \
}
#elif defined(__aarch64__)
# define VL_RDTSC(val) asm volatile("mrs %[rt],PMCCNTR_EL0" : [rt] "=r"(val));
// 1 GHz virtual system timer on SBSA level 5 compliant systems, else often 100 MHz
# define VL_RDTSC(val) \
{ \
asm volatile("isb" : : : "memory"); \
asm volatile("mrs %[rt],CNTVCT_EL0" : [rt] "=r"(val)); \
}
#else
// We just silently ignore unknown OSes, as only leads to missing statistics
# define VL_RDTSC(val) (val) = 0;

View File

@ -230,7 +230,8 @@ string AstNode::prettyTypeName() const {
//######################################################################
// Insertion
inline void AstNode::debugTreeChange(const char* prefix, int lineno, bool next){
inline void AstNode::debugTreeChange(const AstNode* nodep, const char* prefix, int lineno,
bool next){
#ifdef VL_DEBUG
// Called on all major tree changers.
// Only for use for those really nasty bugs relating to internals
@ -254,8 +255,8 @@ 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(UASSERT_OBJ(newp, nodep, "Null item passed to addNext"););
nodep->debugTreeChange("-addNextThs: ", __LINE__, false);
newp->debugTreeChange("-addNextNew: ", __LINE__, true);
debugTreeChange(nodep, "-addNextThs: ", __LINE__, false);
debugTreeChange(newp, "-addNextNew: ", __LINE__, true);
if (!nodep) { // verilog.y and lots of other places assume this
return newp;
} else {
@ -285,7 +286,7 @@ AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) {
newp->editCountInc();
if (oldtailp->m_iterpp) *(oldtailp->m_iterpp) = newp; // Iterate on new item
}
nodep->debugTreeChange("-addNextOut:", __LINE__, true);
debugTreeChange(nodep, "-addNextOut:", __LINE__, true);
return nodep;
}
@ -300,8 +301,8 @@ void AstNode::addNextHere(AstNode* newp) {
// New could be head of single node, or list
UASSERT(newp, "Null item passed to addNext");
UASSERT(!newp->backp(), "New node (back) already assigned?");
this->debugTreeChange("-addHereThs: ", __LINE__, false);
newp->debugTreeChange("-addHereNew: ", __LINE__, true);
debugTreeChange(this, "-addHereThs: ", __LINE__, false);
debugTreeChange(newp, "-addHereNew: ", __LINE__, true);
newp->editCountInc();
AstNode* addlastp = newp->m_headtailp; // Last node in list to be added
@ -339,7 +340,7 @@ void AstNode::addNextHere(AstNode* newp) {
}
if (this->m_iterpp) *(this->m_iterpp) = newp; // Iterate on new item
this->debugTreeChange("-addHereOut: ", __LINE__, true);
debugTreeChange(this, "-addHereOut: ", __LINE__, true);
}
void AstNode::setOp1p(AstNode* newp) {
@ -347,12 +348,12 @@ void AstNode::setOp1p(AstNode* newp) {
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);
debugTreeChange(this, "-setOp1pThs: ", __LINE__, false);
debugTreeChange(newp, "-setOp1pNew: ", __LINE__, true);
m_op1p = newp;
newp->editCountInc();
newp->m_backp = this;
this->debugTreeChange("-setOp1pOut: ", __LINE__, false);
debugTreeChange(this, "-setOp1pOut: ", __LINE__, false);
}
void AstNode::setOp2p(AstNode* newp) {
@ -360,12 +361,12 @@ void AstNode::setOp2p(AstNode* newp) {
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);
debugTreeChange(this, "-setOp2pThs: ", __LINE__, false);
debugTreeChange(newp, "-setOp2pNew: ", __LINE__, true);
m_op2p = newp;
newp->editCountInc();
newp->m_backp = this;
this->debugTreeChange("-setOp2pOut: ", __LINE__, false);
debugTreeChange(this, "-setOp2pOut: ", __LINE__, false);
}
void AstNode::setOp3p(AstNode* newp) {
@ -373,12 +374,12 @@ void AstNode::setOp3p(AstNode* newp) {
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);
debugTreeChange(this, "-setOp3pThs: ", __LINE__, false);
debugTreeChange(newp, "-setOp3pNew: ", __LINE__, true);
m_op3p = newp;
newp->editCountInc();
newp->m_backp = this;
this->debugTreeChange("-setOp3pOut: ", __LINE__, false);
debugTreeChange(this, "-setOp3pOut: ", __LINE__, false);
}
void AstNode::setOp4p(AstNode* newp) {
@ -386,12 +387,12 @@ void AstNode::setOp4p(AstNode* newp) {
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);
debugTreeChange(this, "-setOp4pThs: ", __LINE__, false);
debugTreeChange(newp, "-setOp4pNew: ", __LINE__, true);
m_op4p = newp;
newp->editCountInc();
newp->m_backp = this;
this->debugTreeChange("-setOp4pOut: ", __LINE__, false);
debugTreeChange(this, "-setOp4pOut: ", __LINE__, false);
}
void AstNode::addOp1p(AstNode* newp) {
@ -453,7 +454,7 @@ void AstNRelinker::dump(std::ostream& str) const {
}
AstNode* AstNode::unlinkFrBackWithNext(AstNRelinker* linkerp) {
this->debugTreeChange("-unlinkWNextThs: ", __LINE__, true);
debugTreeChange(this, "-unlinkWNextThs: ", __LINE__, true);
AstNode* oldp = this;
UASSERT(oldp->m_backp, "Node has no back, already unlinked?");
oldp->editCountInc();
@ -508,12 +509,12 @@ AstNode* AstNode::unlinkFrBackWithNext(AstNRelinker* linkerp) {
// Iterator fixup
if (oldp->m_iterpp) *(oldp->m_iterpp) = nullptr;
oldp->m_iterpp = nullptr;
oldp->debugTreeChange("-unlinkWNextOut: ", __LINE__, true);
debugTreeChange(oldp, "-unlinkWNextOut: ", __LINE__, true);
return oldp;
}
AstNode* AstNode::unlinkFrBack(AstNRelinker* linkerp) {
this->debugTreeChange("-unlinkFrBkThs: ", __LINE__, true);
debugTreeChange(this, "-unlinkFrBkThs: ", __LINE__, true);
AstNode* oldp = this;
UASSERT(oldp->m_backp, "Node has no back, already unlinked?");
oldp->editCountInc();
@ -572,7 +573,7 @@ AstNode* AstNode::unlinkFrBack(AstNRelinker* linkerp) {
oldp->m_backp = nullptr;
oldp->m_headtailp = this;
oldp->m_iterpp = nullptr;
oldp->debugTreeChange("-unlinkFrBkOut: ", __LINE__, true);
debugTreeChange(oldp, "-unlinkFrBkOut: ", __LINE__, true);
return oldp;
}
@ -592,8 +593,8 @@ void AstNode::relink(AstNRelinker* linkerp) {
}
AstNode* backp = linkerp->m_backp;
this->debugTreeChange("-relinkNew: ", __LINE__, true);
backp->debugTreeChange("-relinkTre: ", __LINE__, true);
debugTreeChange(this, "-relinkNew: ", __LINE__, true);
debugTreeChange(backp, "-relinkTre: ", __LINE__, true);
switch (linkerp->m_chg) {
case AstNRelinker::RELINK_NEXT: backp->addNextHere(newp); break;
@ -618,7 +619,7 @@ void AstNode::relink(AstNRelinker* linkerp) {
}
// Empty the linker so not used twice accidentally
linkerp->m_backp = nullptr;
this->debugTreeChange("-relinkOut: ", __LINE__, true);
debugTreeChange(this, "-relinkOut: ", __LINE__, true);
}
void AstNode::relinkOneLink(AstNode*& pointpr, // Ref to pointer that gets set to newp
@ -700,7 +701,7 @@ AstNode* AstNode::cloneTreeIterList() {
}
AstNode* AstNode::cloneTree(bool cloneNextLink) {
this->debugTreeChange("-cloneThs: ", __LINE__, cloneNextLink);
debugTreeChange(this, "-cloneThs: ", __LINE__, cloneNextLink);
cloneClearTree();
AstNode* newp;
if (cloneNextLink && this->m_nextp) {
@ -712,7 +713,7 @@ AstNode* AstNode::cloneTree(bool cloneNextLink) {
}
newp->m_backp = nullptr;
newp->cloneRelinkTree();
newp->debugTreeChange("-cloneOut: ", __LINE__, true);
debugTreeChange(newp, "-cloneOut: ", __LINE__, true);
return newp;
}
@ -764,7 +765,7 @@ void AstNode::deleteTree() {
// deleteTree always deletes the next link, because you must have called
// unlinkFromBack or unlinkFromBackWithNext as appropriate before calling this.
UASSERT(!m_backp, "Delete called on node with backlink still set");
this->debugTreeChange("-delTree: ", __LINE__, true);
debugTreeChange(this, "-delTree: ", __LINE__, true);
this->editCountInc();
// MUST be depth first!
deleteTreeIter();

View File

@ -60,15 +60,21 @@ using MTaskIdSet = std::set<int>; // Set of mtaskIds for Var sorting
if (VL_UNCOVERABLE(reasonp)) return reasonp; \
} while (false)
// (V)erilator (N)ode is: True if AstNode is of a a given AstType
// (V)erilator (N)ode is: Returns true iff AstNode is of the given AstNode subtype, and not
// nullptr.
#define VN_IS(nodep, nodetypename) (AstNode::privateIs<Ast##nodetypename>(nodep))
// (V)erilator (N)ode cast: Cast to given type if can; effectively
// dynamic_cast<nodetypename>(nodep)
// (V)erilator (N)ode cast: More efficient but otherwise same as dynamic_cast, use this instead.
// Cast to given type if node is of such type, otherwise returns nullptr.
#define VN_CAST(nodep, nodetypename) (AstNode::privateCast<Ast##nodetypename>(nodep))
#define VN_CAST_CONST(nodep, nodetypename) (AstNode::privateConstCast<Ast##nodetypename>(nodep))
#define VN_CAST_CONST(nodep, nodetypename) (AstNode::privateCastConst<Ast##nodetypename>(nodep))
// (V)erilator (N)ode deleted: Reference to deleted child (for assertions only)
// (V)erilator (N)ode as: Assert node is of given type then cast to that type. Node can be nullptr.
// Use this to downcast instead of VN_CAST when you know the true type of the node.
#define VN_AS(nodep, nodetypename) (AstNode::privateAs<Ast##nodetypename>(nodep))
#define VN_AS_CONST(nodep, nodetypename) (AstNode::privateAsConst<Ast##nodetypename>(nodep))
// (V)erilator (N)ode deleted: Pointer to deleted AstNode (for assertions only)
#define VN_DELETED(nodep) VL_UNLIKELY((vluint64_t)(nodep) == 0x1)
//######################################################################
@ -1437,7 +1443,7 @@ private:
public:
static void relinkOneLink(AstNode*& pointpr, AstNode* newp);
// cppcheck-suppress functionConst
void debugTreeChange(const char* prefix, int lineno, bool next);
static void debugTreeChange(const AstNode* nodep, const char* prefix, int lineno, bool next);
protected:
// CONSTRUCTORS
@ -1838,16 +1844,36 @@ protected:
private:
void iterateListBackwards(AstNVisitor& v);
// CONVERSION
// For internal use only.
template <typename T> inline static bool privateTypeTest(const AstNode* nodep);
public:
// These for use by VN_IS macro only
template <class T> static bool privateIs(const AstNode* nodep);
// These for use by VN_CAST macro only
template <class T> static T* privateCast(AstNode* nodep);
// These for use by VN_CAST_CONST macro only
template <class T> static const T* privateConstCast(const AstNode* nodep);
// For use via the VN_IS macro only
template <typename T> inline static bool privateIs(const AstNode* nodep) {
return nodep && privateTypeTest<T>(nodep);
}
// For use via the VN_CAST macro only
template <typename T> inline static T* privateCast(AstNode* nodep) {
return privateIs<T>(nodep) ? reinterpret_cast<T*>(nodep) : nullptr;
}
// For use via the VN_CAST_CONST macro only
template <typename T> inline static const T* privateCastConst(const AstNode* nodep) {
return privateIs<T>(nodep) ? reinterpret_cast<const T*>(nodep) : nullptr;
}
// For use via the VN_AS macro only
template <typename T> inline static T* privateAs(AstNode* nodep) {
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
<< "'");
return reinterpret_cast<T*>(nodep);
}
// For use via the VN_AS_CONST macro only
template <typename T> inline static const T* privateAsConst(const AstNode* nodep) {
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
<< "'");
return reinterpret_cast<const T*>(nodep);
}
};
// Specialisations of privateIs/privateCast

View File

@ -1338,7 +1338,7 @@ void AstNodeCoverOrAssert::dump(std::ostream& str) const {
}
void AstDisplay::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
// str<<" "<<displayType().ascii();
// str << " " << displayType().ascii();
}
void AstEnumItemRef::dump(std::ostream& str) const {
this->AstNodeMath::dump(str);

View File

@ -4436,6 +4436,7 @@ public:
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
virtual bool isPure() const override { return !outp(); }
virtual bool cleanOut() const override { return true; }
virtual bool same(const AstNode* samep) const override { return true; }
AstNode* searchp() const { return op1p(); } // op1 = Search expression

View File

@ -136,6 +136,7 @@ private:
"Unsupported: Complicated event expression in sensitive activity list");
return nullptr;
}
UASSERT_OBJ(nodep->varrefp(), nodep, "No clock found on sense item");
AstVarScope* clkvscp = nodep->varrefp()->varScopep();
if (nodep->edgeType() == VEdgeType::ET_POSEDGE) {
AstVarScope* lastVscp = getCreateLastClk(clkvscp);

View File

@ -24,6 +24,7 @@
#include <map>
#include <set>
#include <string>
#include <unordered_map>
//######################################################################
// Resolve wildcards in files, modules, ftasks or variables
@ -346,6 +347,9 @@ using V3ConfigFileResolver = V3ConfigWildcardResolver<V3ConfigFile>;
class V3ConfigResolver final {
V3ConfigModuleResolver m_modules; // Access to module names (with wildcards)
V3ConfigFileResolver m_files; // Access to file names (with wildcards)
std::unordered_map<string, std::unordered_map<string, vluint64_t>>
m_profileData; // Access to profile_data records
FileLine* m_profileFileLine = nullptr;
static V3ConfigResolver s_singleton; // Singleton (not via local static, as that's slow)
V3ConfigResolver() = default;
@ -356,6 +360,20 @@ public:
V3ConfigModuleResolver& modules() { return m_modules; }
V3ConfigFileResolver& files() { return m_files; }
void addProfileData(FileLine* fl, const string& model, const string& key, vluint64_t cost) {
if (!m_profileFileLine) m_profileFileLine = fl;
if (cost == 0) cost = 1; // Cost 0 means delete (or no data)
m_profileData[model][key] += cost;
}
vluint64_t getProfileData(const string& model, const string& key) const {
const auto mit = m_profileData.find(model);
if (mit == m_profileData.cend()) return 0;
const auto it = mit->second.find(key);
if (it == mit->second.cend()) return 0;
return it->second;
}
FileLine* getProfileDataFileLine() const { return m_profileFileLine; } // Maybe null
};
V3ConfigResolver V3ConfigResolver::s_singleton;
@ -392,10 +410,6 @@ void V3Config::addIgnore(V3ErrorCode code, bool on, const string& filename, int
}
}
void V3Config::addModulePragma(const string& module, AstPragmaType pragma) {
V3ConfigResolver::s().modules().at(module).addModulePragma(pragma);
}
void V3Config::addInline(FileLine* fl, const string& module, const string& ftask, bool on) {
if (ftask.empty()) {
V3ConfigResolver::s().modules().at(module).setInline(on);
@ -408,6 +422,15 @@ void V3Config::addInline(FileLine* fl, const string& module, const string& ftask
}
}
void V3Config::addModulePragma(const string& module, AstPragmaType pragma) {
V3ConfigResolver::s().modules().at(module).addModulePragma(pragma);
}
void V3Config::addProfileData(FileLine* fl, const string& model, const string& key,
vluint64_t cost) {
V3ConfigResolver::s().addProfileData(fl, model, key, cost);
}
void V3Config::addVarAttr(FileLine* fl, const string& module, const string& ftask,
const string& var, AstAttrType attr, AstSenTree* sensep) {
// Semantics: sensep only if public_flat_rw
@ -497,6 +520,13 @@ void V3Config::applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar
if (vp) vp->apply(varp);
}
vluint64_t V3Config::getProfileData(const string& model, const string& key) {
return V3ConfigResolver::s().getProfileData(model, key);
}
FileLine* V3Config::getProfileDataFileLine() {
return V3ConfigResolver::s().getProfileDataFileLine();
}
bool V3Config::waive(FileLine* filelinep, V3ErrorCode code, const string& message) {
V3ConfigFile* filep = V3ConfigResolver::s().files().resolve(filelinep->filename());
if (!filep) return false;

View File

@ -33,17 +33,23 @@ public:
static void addCoverageBlockOff(const string& file, int lineno);
static void addCoverageBlockOff(const string& module, const string& blockname);
static void addIgnore(V3ErrorCode code, bool on, const string& filename, int min, int max);
static void addWaiver(V3ErrorCode code, const string& filename, const string& message);
static void addModulePragma(const string& module, AstPragmaType pragma);
static void addInline(FileLine* fl, const string& module, const string& ftask, bool on);
static void addModulePragma(const string& module, AstPragmaType pragma);
static void addProfileData(FileLine* fl, const string& model, const string& key,
vluint64_t cost);
static void addWaiver(V3ErrorCode code, const string& filename, const string& message);
static void addVarAttr(FileLine* fl, const string& module, const string& ftask,
const string& signal, AstAttrType type, AstSenTree* nodep);
static void applyCase(AstCase* nodep);
static void applyCoverageBlock(AstNodeModule* modulep, AstBegin* nodep);
static void applyIgnores(FileLine* filelinep);
static void applyModule(AstNodeModule* modulep);
static void applyFTask(AstNodeModule* modulep, AstNodeFTask* ftaskp);
static void applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar* varp);
static uint64_t getProfileData(const string& model, const string& key);
static FileLine* getProfileDataFileLine();
static bool waive(FileLine* filelinep, V3ErrorCode code, const string& message);
};

View File

@ -923,8 +923,9 @@ private:
if (ccastp) {
andp->replaceWith(ccastp);
VL_DO_DANGLING(andp->deleteTree(), andp);
return true;
}
return ccastp;
return false;
}
static bool operandAndOrSame(const AstNode* nodep) {
@ -1312,6 +1313,15 @@ private:
// but for now can disable en-mass until V3Purify takes effect.
return m_doShort || VN_IS(nodep, VarRef) || VN_IS(nodep, Const);
}
bool isTreePureRecurse(AstNode* nodep) {
// Should memoize this if call commonly
if (!nodep->isPure()) return false;
if (nodep->op1p() && !isTreePureRecurse(nodep->op1p())) return false;
if (nodep->op2p() && !isTreePureRecurse(nodep->op2p())) return false;
if (nodep->op3p() && !isTreePureRecurse(nodep->op3p())) return false;
if (nodep->op4p() && !isTreePureRecurse(nodep->op4p())) return false;
return true;
}
// Extraction checks
bool warnSelect(AstSel* nodep) {
@ -1337,10 +1347,19 @@ private:
&& (nodep->msbConst() > maxDeclBit || nodep->lsbConst() > maxDeclBit)) {
// See also warning in V3Width
// Must adjust by element width as declRange() is in number of elements
string msbLsbProtected;
if (nodep->declElWidth() == 0) {
msbLsbProtected = "(nodep->declElWidth() == 0) "
+ std::to_string(nodep->msbConst()) + ":"
+ std::to_string(nodep->lsbConst());
} else {
msbLsbProtected = std::to_string(nodep->msbConst() / nodep->declElWidth())
+ ":"
+ std::to_string(nodep->lsbConst() / nodep->declElWidth());
}
nodep->v3warn(SELRANGE,
"Selection index out of range: "
<< (nodep->msbConst() / nodep->declElWidth()) << ":"
<< (nodep->lsbConst() / nodep->declElWidth()) << " outside "
<< msbLsbProtected << " outside "
<< nodep->declRange().hiMaxSelect() << ":0"
<< (nodep->declRange().lo() >= 0
? ""
@ -2199,8 +2218,8 @@ private:
void swapSides(AstNodeBiCom* nodep) {
// COMMUTATIVE({a},CONST) -> COMMUTATIVE(CONST,{a})
// This simplifies later optimizations
AstNode* lhsp = nodep->lhsp()->unlinkFrBackWithNext();
AstNode* rhsp = nodep->rhsp()->unlinkFrBackWithNext();
AstNode* const lhsp = nodep->lhsp()->unlinkFrBackWithNext();
AstNode* const rhsp = nodep->rhsp()->unlinkFrBackWithNext();
nodep->lhsp(rhsp);
nodep->rhsp(lhsp);
iterate(nodep); // Again?
@ -2209,8 +2228,8 @@ private:
int operandConcatMove(AstConcat* nodep) {
// CONCAT under concat (See moveConcat)
// Return value: true indicates to do it; 2 means move to LHS
AstConcat* abConcp = VN_CAST(nodep->lhsp(), Concat);
AstConcat* bcConcp = VN_CAST(nodep->rhsp(), Concat);
AstConcat* const abConcp = VN_CAST(nodep->lhsp(), Concat);
AstConcat* const bcConcp = VN_CAST(nodep->rhsp(), Concat);
if (!abConcp && !bcConcp) return 0;
if (bcConcp) {
AstNode* ap = nodep->lhsp();
@ -2783,10 +2802,13 @@ private:
}
VL_DO_DANGLING(nodep->deleteTree(), nodep);
} else if (!afterComment(nodep->ifsp()) && !afterComment(nodep->elsesp())) {
// Empty block, remove it
// Note if we support more C++ then there might be side
// effects in the condition itself
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
if (!isTreePureRecurse(nodep->condp())) {
// Condition has side effect - leave - perhaps in
// future simplify to remove all but side effect terms
} else {
// Empty block, remove it
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
}
} else if (!afterComment(nodep->ifsp())) {
UINFO(4, "IF({x}) nullptr {...} => IF(NOT{x}}: " << nodep << endl);
AstNode* condp = nodep->condp();

View File

@ -392,10 +392,15 @@ public:
emitCCallArgs(nodep, "");
}
virtual void visit(AstCNew* nodep) override {
bool comma = false;
puts("std::make_shared<" + prefixNameProtect(nodep->dtypep()) + ">(");
puts("vlSymsp"); // TODO make this part of argsp, and eliminate when unnecessary
if (nodep->argsp()) puts(", ");
iterateAndNextNull(nodep->argsp());
if (nodep->argsp()) comma = true;
for (AstNode* subnodep = nodep->argsp(); subnodep; subnodep = subnodep->nextp()) {
if (comma) puts(", ");
iterate(subnodep);
comma = true;
}
puts(")");
}
virtual void visit(AstCMethodHard* nodep) override {

View File

@ -323,7 +323,24 @@ class EmitCModel final : public EmitCFunc {
puts(" loop\\n\"););\n");
if (initial)
puts(topModNameProtected + "__" + protect("_eval_settle") + "(&(vlSymsp->TOP));\n");
const string recName = "__Vprfloop";
if (v3Global.opt.profThreads() && !initial) {
puts("VlProfileRec* " + recName + " = nullptr;\n");
// Leave this if() here, as don't want to call VL_RDTSC_Q unless profiling
puts("if (VL_UNLIKELY(vlSymsp->__Vm_profile_cycle_start)) {\n");
// Eval start
puts(/**/ recName + " = vlSymsp->__Vm_threadPoolp->profileAppend();\n");
puts(/**/ recName + "->startEvalLoop(VL_RDTSC_Q());\n");
puts("}\n");
}
puts(topModNameProtected + "__" + protect("_eval") + "(&(vlSymsp->TOP));\n");
if (v3Global.opt.profThreads() && !initial) {
puts("if (VL_UNLIKELY(" + recName + ")) " + recName + "->endRecord(VL_RDTSC_Q());\n");
}
if (v3Global.rootp()->changeRequest()) {
puts("if (VL_UNLIKELY(++__VclockLoop > " + cvtToStr(v3Global.opt.convergeLimit())
+ ")) {\n");
@ -354,7 +371,7 @@ class EmitCModel final : public EmitCFunc {
+ ");\n");
}
void emitStandardMethods(AstNodeModule* modp) {
void emitStandardMethods1(AstNodeModule* modp) {
UASSERT_OBJ(modp->isTop(), modp, "Attempting to emitWrapEval for non-top class");
const string topModNameProtected = prefixNameProtect(modp);
@ -385,16 +402,21 @@ class EmitCModel final : public EmitCFunc {
emitSettleLoop(modp, /* initial: */ true);
ensureNewLine();
puts("}\n");
}
void emitStandardMethods2(AstNodeModule* modp) {
const string topModNameProtected = prefixNameProtect(modp);
// ::eval_step
puts("\nvoid " + topClassName() + "::eval_step() {\n");
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+++++TOP Evaluate " + topClassName()
+ "::eval_step\\n\"); );\n");
puts("#ifdef VL_DEBUG\n");
putsDecoration("// Debug assertions\n");
puts(topModNameProtected + "__" + protect("_eval_debug_assertions")
+ "(&(vlSymsp->TOP));\n");
puts("#endif // VL_DEBUG\n");
putsDecoration("// Initialize\n");
puts("if (VL_UNLIKELY(!vlSymsp->__Vm_didInit)) " + protect("_eval_initial_loop")
+ "(vlSymsp);\n");
@ -406,56 +428,80 @@ class EmitCModel final : public EmitCFunc {
puts("Verilated::mtaskId(" + cvtToStr(mtaskId) + ");\n");
}
if (v3Global.opt.mtasks() && v3Global.opt.profThreads()) {
if (v3Global.opt.profThreads()) {
puts("if (VL_UNLIKELY((vlSymsp->_vm_contextp__->profThreadsStart() != "
"vlSymsp->__Vm_profile_time_finished)\n");
puts(" && (VL_TIME_Q() > vlSymsp->_vm_contextp__->profThreadsStart())\n");
puts(" && (vlSymsp->_vm_contextp__->profThreadsWindow() >= 1))) {\n");
// Within a profile (either starting, middle, or end)
puts("if (vlSymsp->__Vm_profile_window_ct == 0) {\n"); // Opening file?
puts(/**/ "if (vlSymsp->__Vm_profile_window_ct == 0) {\n"); // Opening file?
puts(/**/ "VL_DEBUG_IF(VL_DBG_MSGF(\"+ profile start warmup\\n\"););\n");
// Start profile on this cycle. We'll capture a window worth, then
// only analyze the next window worth. The idea is that the first window
// capture will hit some cache-cold stuff (eg printf) but it'll be warm
// by the time we hit the second window, we hope.
puts("vlSymsp->__Vm_profile_cycle_start = VL_RDTSC_Q();\n");
puts(/****/ "vlSymsp->__Vm_profile_cycle_start = VL_RDTSC_Q();\n");
// "* 2" as first half is warmup, second half is collection
puts("vlSymsp->__Vm_profile_window_ct = vlSymsp->_vm_contextp__->profThreadsWindow() "
"* 2 "
"+ "
"1;\n");
puts("}\n");
puts("--(vlSymsp->__Vm_profile_window_ct);\n");
puts("if (vlSymsp->__Vm_profile_window_ct == "
"vlSymsp->_vm_contextp__->profThreadsWindow()) {\n");
puts(/****/ "vlSymsp->__Vm_profile_window_ct"
" = vlSymsp->_vm_contextp__->profThreadsWindow()"
" * 2 + 1;\n");
puts(/**/ "}\n");
puts(/**/ "--(vlSymsp->__Vm_profile_window_ct);\n");
puts(/**/ "if (vlSymsp->__Vm_profile_window_ct"
" == vlSymsp->_vm_contextp__->profThreadsWindow()) {\n");
// This barrier record in every threads' profile demarcates the
// cache-warm-up cycles before the barrier from the actual profile
// cycles afterward.
puts("vlSymsp->__Vm_threadPoolp->profileAppendAll(");
puts("VlProfileRec(VlProfileRec::Barrier()));\n");
puts("vlSymsp->__Vm_profile_cycle_start = VL_RDTSC_Q();\n");
puts("}\n");
puts("else if (vlSymsp->__Vm_profile_window_ct == 0) {\n");
// Ending file.
puts("vluint64_t elapsed = VL_RDTSC_Q() - vlSymsp->__Vm_profile_cycle_start;\n");
puts("vlSymsp->__Vm_threadPoolp->profileDump(vlSymsp->_vm_contextp__->"
"profThreadsFilename().c_str(), elapsed);\n");
puts(/****/ "vlSymsp->__Vm_threadPoolp->profileAppendAll(");
puts(/****/ "VlProfileRec{VlProfileRec::Barrier{}});\n");
puts(/****/ "vlSymsp->__Vm_profile_cycle_start = VL_RDTSC_Q();\n");
puts(/**/ "}\n");
// Ending trace file?
puts(/**/ "else if (vlSymsp->__Vm_profile_window_ct == 0) {\n");
puts(/****/ "vluint64_t tick_end = VL_RDTSC_Q();\n");
puts(/****/ "VL_DEBUG_IF(VL_DBG_MSGF(\"+ profile end\\n\"););\n");
puts(/****/ "vlSymsp->__Vm_threadPoolp->profileDump("
"vlSymsp->_vm_contextp__->profThreadsFilename().c_str(), "
"vlSymsp->__Vm_profile_cycle_start, "
"tick_end);\n");
// This turns off the test to enter the profiling code, but still
// allows the user to collect another profile by changing
// profThreadsStart
puts("vlSymsp->__Vm_profile_time_finished = "
"vlSymsp->_vm_contextp__->profThreadsStart();\n");
puts("vlSymsp->__Vm_profile_cycle_start = 0;\n");
puts(/****/ "vlSymsp->__Vm_profile_time_finished = "
"vlSymsp->_vm_contextp__->profThreadsStart();\n");
puts(/****/ "vlSymsp->__Vm_profile_cycle_start = 0;\n");
puts(/**/ "}\n");
puts("}\n");
}
const string recName = "__Vprfeval";
if (v3Global.opt.profThreads()) {
puts("VlProfileRec* " + recName + " = nullptr;\n");
// Leave this if() here, as don't want to call VL_RDTSC_Q unless profiling
puts("if (VL_UNLIKELY(vlSymsp->__Vm_profile_cycle_start)) {\n");
// Eval start
puts(/**/ recName + " = vlSymsp->__Vm_threadPoolp->profileAppend();\n");
puts(/**/ recName + "->startEval(VL_RDTSC_Q());\n");
puts("}\n");
}
emitSettleLoop(modp, /* initial: */ false);
putsDecoration("// Evaluate cleanup\n");
if (v3Global.opt.threads() == 1) {
puts("Verilated::endOfThreadMTask(vlSymsp->__Vm_evalMsgQp);\n");
}
if (v3Global.opt.threads()) puts("Verilated::endOfEval(vlSymsp->__Vm_evalMsgQp);\n");
puts("}\n");
if (v3Global.opt.profThreads()) {
// End eval record
puts("if (VL_UNLIKELY(" + recName + ")) " + recName + "->endRecord(VL_RDTSC_Q());\n");
}
puts("}\n");
}
void emitStandardMethods3(AstNodeModule* modp) {
const string topModNameProtected = prefixNameProtect(modp);
// ::eval_end_step
if (v3Global.needTraceDumper() && !optSystemC()) {
puts("\nvoid " + topClassName() + "::eval_end_step() {\n");
@ -572,7 +618,9 @@ class EmitCModel final : public EmitCFunc {
emitConstructorImplementation(modp);
emitDestructorImplementation();
emitStandardMethods(modp);
emitStandardMethods1(modp);
emitStandardMethods2(modp);
emitStandardMethods3(modp);
if (v3Global.opt.trace()) { emitTraceMethods(modp); }
if (v3Global.opt.savable()) { emitSerializationFunctions(); }

View File

@ -21,6 +21,7 @@
#include "V3EmitC.h"
#include "V3EmitCBase.h"
#include "V3LanguageWords.h"
#include "V3PartitionGraph.h"
#include <algorithm>
#include <map>
@ -214,12 +215,13 @@ class EmitCSyms final : EmitCBaseVisitor {
} else {
varBase = whole;
}
// UINFO(9,"For "<<scopep->name()<<" - "<<varp->name()<<" Scp "<<scpName<<"
// Var "<<varBase<<endl);
// UINFO(9, "For " << scopep->name() << " - " << varp->name() << " Scp "
// << scpName << "Var " << varBase << endl);
const string varBasePretty = AstNode::prettyName(varBase);
const string scpPretty = AstNode::prettyName(scpName);
const string scpSym = scopeSymString(scpName);
// UINFO(9," scnameins sp "<<scpName<<" sp "<<scpPretty<<" ss "<<scpSym<<endl);
// UINFO(9, " scnameins sp " << scpName << " sp " << scpPretty << " ss "
// << scpSym << endl);
if (v3Global.opt.vpi()) varHierarchyScopes(scpName);
if (m_scopeNames.find(scpSym) == m_scopeNames.end()) {
m_scopeNames.insert(std::make_pair(
@ -393,6 +395,7 @@ void EmitCSyms::emitSymHdr() {
if (v3Global.needTraceDumper()) {
puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n");
}
if (v3Global.opt.profThreads()) puts("#include \"verilated_profiler.h\"\n");
puts("\n// INCLUDE MODEL CLASS\n");
puts("\n#include \"" + topClassName() + ".h\"\n");
@ -474,6 +477,21 @@ void EmitCSyms::emitSymHdr() {
puts("];\n");
}
if (v3Global.opt.profThreads()) {
puts("\n// PROFILING\n");
vluint64_t maxProfilerId = 0;
if (v3Global.opt.mtasks()) {
for (const V3GraphVertex* vxp
= v3Global.rootp()->execGraphp()->depGraphp()->verticesBeginp();
vxp; vxp = vxp->verticesNextp()) {
ExecMTask* mtp = dynamic_cast<ExecMTask*>(const_cast<V3GraphVertex*>(vxp));
if (maxProfilerId < mtp->profilerId()) maxProfilerId = mtp->profilerId();
}
}
++maxProfilerId; // As size must include 0
puts("VerilatedProfiler<" + cvtToStr(maxProfilerId) + "> _vm_profiler;\n");
}
if (!m_scopeNames.empty()) { // Scope names
puts("\n// SCOPE NAMES\n");
for (const auto& itr : m_scopeNames) {
@ -653,6 +671,7 @@ void EmitCSyms::emitSymImp() {
}
puts("// FUNCTIONS\n");
// Destructor
puts(symClassName() + "::~" + symClassName() + "()\n");
puts("{\n");
@ -662,7 +681,11 @@ void EmitCSyms::emitSymImp() {
puts("if (__Vm_dumping) _traceDumpClose();\n");
puts("#endif // VM_TRACE\n");
}
if (v3Global.opt.mtasks()) { puts("delete __Vm_threadPoolp;\n"); }
if (v3Global.opt.profThreads()) {
puts("_vm_profiler.write(\"" + topClassName()
+ "\", _vm_contextp__->profVltFilename());\n");
}
if (v3Global.opt.mtasks()) puts("delete __Vm_threadPoolp;\n");
puts("}\n\n");
// Constructor
@ -717,6 +740,19 @@ void EmitCSyms::emitSymImp() {
}
puts("{\n");
if (v3Global.opt.profThreads()) {
puts("// Configure profiling\n");
if (v3Global.opt.mtasks()) {
for (const V3GraphVertex* vxp
= v3Global.rootp()->execGraphp()->depGraphp()->verticesBeginp();
vxp; vxp = vxp->verticesNextp()) {
ExecMTask* mtp = dynamic_cast<ExecMTask*>(const_cast<V3GraphVertex*>(vxp));
puts("_vm_profiler.addCounter(" + cvtToStr(mtp->profilerId()) + ", \""
+ mtp->hashName() + "\");\n");
}
}
}
puts("// Configure time unit / time precision\n");
if (!v3Global.rootp()->timeunit().isNone()) {
puts("_vm_contextp__->timeunit(");

View File

@ -110,6 +110,7 @@ public:
PINNOTFOUND, // instance port name not found in it's module
PKGNODECL, // Error: Package/class needs to be predeclared
PROCASSWIRE, // Procedural assignment on wire
PROFOUTOFDATE, // Profile data out of date
PROTECTED, // detected `pragma protected
RANDC, // Unsupported: 'randc' converted to 'rand'
REALCVT, // Real conversion
@ -173,7 +174,7 @@ public:
"LATCH", "LITENDIAN", "MODDUP",
"MULTIDRIVEN", "MULTITOP","NOLATCH", "NULLPORT", "PINCONNECTEMPTY",
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PROCASSWIRE",
"PROTECTED", "RANDC", "REALCVT", "REDEFMACRO",
"PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO",
"SELRANGE", "SHORTREAL", "SPLITVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET",
"TICKCOUNT", "TIMESCALEMOD",
"UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS",

View File

@ -81,7 +81,7 @@ private:
virtual void visit(AstNode* nodep) override {
#if VL_DEBUG
UINFO(0, "%Warning: Hashing node as AstNode: " << nodep);
UINFO(0, "%Warning: Hashing node as AstNode: " << nodep << endl);
#endif
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
@ -455,6 +455,9 @@ private:
iterateNull(nodep->ftaskp());
});
}
virtual void visit(AstMTaskBody* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
virtual void visit(AstNodeProcedure* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}

View File

@ -132,6 +132,8 @@ void V3LinkLevel::timescaling(const ModVec& mods) {
v3Global.rootp()->timeprecisionMerge(v3Global.rootp()->fileline(),
VTimescale(VTimescale::TS_DEFAULT));
}
// Classes under package have timescale propaged in V3LinkParse
}
//######################################################################

View File

@ -504,7 +504,8 @@ private:
{
// Module: Create sim table for entire module and iterate
cleanFileline(nodep);
//
// Classes inherit from upper package
if (m_modp && nodep->timeunit().isNone()) nodep->timeunit(m_modp->timeunit());
m_modp = nodep;
m_genblkAbove = 0;
m_genblkNum = 0;

View File

@ -605,6 +605,24 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const {
while (bit && bitIs0(bit)) bit--;
while ((bit % 3) != 2) bit++;
for (; bit > 0; bit -= 3) {
const int numX = countX(bit - 2, 3);
const int numZ = countZ(bit - 2, 3);
if (numX == 3 || numX == width() - (bit - 2)) {
str += 'x';
continue;
}
if (numZ == 3 || numZ == width() - (bit - 2)) {
str += 'z';
continue;
}
if (numX > 0) {
str += 'X';
continue;
}
if (numZ > 0) {
str += 'Z';
continue;
}
int v = bitsValue(bit - 2, 3);
str += static_cast<char>('0' + v);
}
@ -617,6 +635,24 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const {
while (bit && bitIs0(bit)) bit--;
while ((bit % 4) != 3) bit++;
for (; bit > 0; bit -= 4) {
const int numX = countX(bit - 3, 4);
const int numZ = countZ(bit - 3, 4);
if (numX == 4 || numX == width() - (bit - 3)) {
str += 'x';
continue;
}
if (numZ == 4 || numZ == width() - (bit - 3)) {
str += 'z';
continue;
}
if (numX > 0) {
str += 'X';
continue;
}
if (numZ > 0) {
str += 'Z';
continue;
}
int v = bitsValue(bit - 3, 4);
if (v >= 10) {
str += static_cast<char>('a' + v - 10);
@ -667,17 +703,33 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const {
if (issigned) dchars++; // space for sign
fmtsize = cvtToStr(int(dchars));
}
if (issigned) {
if (width() > 64) {
str = toDecimalS();
bool hasXZ = false;
if (isAllX()) {
str = "x";
hasXZ = true;
} else if (isAllZ()) {
str = "z";
hasXZ = true;
} else if (isAnyX()) {
str = "X";
hasXZ = true;
} else if (isAnyZ()) {
str = "Z";
hasXZ = true;
}
if (!hasXZ) {
if (issigned) {
if (width() > 64) {
str = toDecimalS();
} else {
str = cvtToStr(toSQuad());
}
} else {
str = cvtToStr(toSQuad());
}
} else {
if (width() > 64) {
str = toDecimalU();
} else {
str = cvtToStr(toUQuad());
if (width() > 64) {
str = toDecimalU();
} else {
str = cvtToStr(toUQuad());
}
}
}
const bool zeropad = fmtsize.length() > 0 && fmtsize[0] == '0';
@ -977,6 +1029,22 @@ bool V3Number::isLtXZ(const V3Number& rhs) const {
}
return false;
}
int V3Number::countX(int lsb, int nbits) const {
int count = 0;
for (int bitn = 0; bitn < nbits; ++bitn) {
if (lsb + bitn >= width()) return count;
if (bitIsX(lsb + bitn)) ++count;
}
return count;
}
int V3Number::countZ(int lsb, int nbits) const {
int count = 0;
for (int bitn = 0; bitn < nbits; ++bitn) {
if (lsb + bitn >= width()) return count;
if (bitIsZ(lsb + bitn)) ++count;
}
return count;
}
int V3Number::widthMin() const {
for (int bit = width() - 1; bit > 0; bit--) {

View File

@ -204,6 +204,9 @@ private:
return v;
}
int countX(int lsb, int nbits) const;
int countZ(int lsb, int nbits) const;
int words() const { return ((width() + 31) / 32); }
uint32_t hiWordMask() const { return VL_MASK_I(width()); }

View File

@ -18,6 +18,7 @@
#include "verilatedos.h"
#include "V3EmitCBase.h"
#include "V3Config.h"
#include "V3Os.h"
#include "V3File.h"
#include "V3GraphAlg.h"
@ -27,6 +28,7 @@
#include "V3PartitionGraph.h"
#include "V3Scoreboard.h"
#include "V3Stats.h"
#include "V3UniqueNames.h"
#include <list>
#include <memory>
@ -2237,11 +2239,11 @@ public:
std::vector<uint32_t> busyUntil(m_nThreads, 0);
// MTasks ready to be assigned next. All their dependencies are already assigned.
std::set<const ExecMTask*, MTaskCmp> readyMTasks;
std::set<ExecMTask*, MTaskCmp> readyMTasks;
// Build initial ready list
for (V3GraphVertex* vxp = mtaskGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
const ExecMTask* const mtaskp = dynamic_cast<ExecMTask*>(vxp);
ExecMTask* const mtaskp = dynamic_cast<ExecMTask*>(vxp);
if (isReady(schedule, mtaskp)) readyMTasks.insert(mtaskp);
}
@ -2250,9 +2252,9 @@ public:
// on each thread (in that thread's local time frame.)
uint32_t bestTime = 0xffffffff;
uint32_t bestThreadId = 0;
const ExecMTask* bestMtaskp = nullptr; // Todo: const ExecMTask*
ExecMTask* bestMtaskp = nullptr; // Todo: const ExecMTask*
for (uint32_t threadId = 0; threadId < m_nThreads; ++threadId) {
for (const ExecMTask* const mtaskp : readyMTasks) {
for (ExecMTask* const mtaskp : readyMTasks) {
uint32_t timeBegin = busyUntil[threadId];
if (timeBegin > bestTime) {
UINFO(6, "th " << threadId << " busy until " << timeBegin
@ -2287,10 +2289,11 @@ public:
std::vector<const ExecMTask*>& bestThread = schedule.threads[bestThreadId];
// Update algorithm state
bestMtaskp->predictStart(bestTime); // Only for gantt reporting
const uint32_t bestEndTime = bestTime + bestMtaskp->cost();
schedule.mtaskState[bestMtaskp].completionTime = bestEndTime;
schedule.mtaskState[bestMtaskp].threadId = bestThreadId;
if (!bestThread.empty()) { schedule.mtaskState[bestThread.back()].nextp = bestMtaskp; }
if (!bestThread.empty()) schedule.mtaskState[bestThread.back()].nextp = bestMtaskp;
busyUntil[bestThreadId] = bestEndTime;
// Add the MTask to the schedule
@ -2301,7 +2304,7 @@ public:
UASSERT_OBJ(erased > 0, bestMtaskp, "Should have erased something?");
for (V3GraphEdge* edgeOutp = bestMtaskp->outBeginp(); edgeOutp;
edgeOutp = edgeOutp->outNextp()) {
const ExecMTask* const nextp = dynamic_cast<ExecMTask*>(edgeOutp->top());
ExecMTask* const nextp = dynamic_cast<ExecMTask*>(edgeOutp->top());
// Dependent MTask should not yet be assigned to a thread
UASSERT(schedule.threadId(nextp) == ThreadSchedule::UNASSIGNED,
"Tasks after one being assigned should not be assigned yet");
@ -2614,15 +2617,152 @@ void V3Partition::go(V3Graph* mtasksp) {
}
}
void add(std::unordered_map<int, vluint64_t>& cmap, int id, vluint64_t cost) { cmap[id] += cost; }
using EstimateAndProfiled = std::pair<uint64_t, vluint64_t>; // cost est, cost profiled
using Costs = std::unordered_map<uint32_t, EstimateAndProfiled>;
static void normalizeCosts(Costs& costs) {
const auto scaleCost = [](vluint64_t value, double multiplier) {
double scaled = static_cast<double>(value) * multiplier;
if (value && scaled < 1) scaled = 1;
return static_cast<uint64_t>(scaled);
};
// For all costs with a profile, compute sum
vluint64_t sumCostProfiled = 0; // For data with estimate and profile
vluint64_t sumCostEstimate = 0; // For data with estimate and profile
for (const auto& est : costs) {
if (est.second.second) {
sumCostEstimate += est.second.first;
sumCostProfiled += est.second.second;
}
}
if (sumCostEstimate) {
// For data where we don't have profiled data, compute how much to
// scale up/down the estimate to make on same relative scale as
// profiled data. (Improves results if only a few profiles missing.)
double estToProfile
= static_cast<double>(sumCostProfiled) / static_cast<double>(sumCostEstimate);
UINFO(5, "Estimated data needs scaling by "
<< estToProfile << ", sumCostProfiled=" << sumCostProfiled
<< " sumCostEstimate=" << sumCostEstimate << endl);
for (auto& est : costs) {
uint64_t& costEstimate = est.second.first;
costEstimate = scaleCost(costEstimate, estToProfile);
}
}
// COSTS can overflow a uint32. Using maximum value of costs, scale all down
vluint64_t maxCost = 0;
for (auto& est : costs) {
const uint64_t& costEstimate = est.second.first;
const uint64_t& costProfiled = est.second.second;
if (maxCost < costEstimate) maxCost = costEstimate;
if (maxCost < costProfiled) maxCost = costProfiled;
UINFO(9,
"Post uint scale: ce = " << est.second.first << " cp=" << est.second.second << endl);
}
vluint64_t scaleDownTo = 10000000; // Extra room for future algorithms to add costs
if (maxCost > scaleDownTo) {
const double scaleup = static_cast<double>(scaleDownTo) / static_cast<double>(maxCost);
UINFO(5, "Scaling data to within 32-bits by multiply by=" << scaleup << ", maxCost="
<< maxCost << endl);
for (auto& est : costs) {
est.second.first = scaleCost(est.second.first, scaleup);
est.second.second = scaleCost(est.second.second, scaleup);
}
}
}
void V3Partition::selfTestNormalizeCosts() {
{ // Test that omitted profile data correctly scales estimates
Costs costs({// id est prof
{1, {10, 1000}},
{2, {20, 0}}, // Note no profile
{3, {30, 3000}}});
normalizeCosts(costs);
UASSERT_SELFTEST(uint64_t, costs[1].first, 1000);
UASSERT_SELFTEST(uint64_t, costs[1].second, 1000);
UASSERT_SELFTEST(uint64_t, costs[2].first, 2000);
UASSERT_SELFTEST(uint64_t, costs[2].second, 0);
UASSERT_SELFTEST(uint64_t, costs[3].first, 3000);
UASSERT_SELFTEST(uint64_t, costs[3].second, 3000);
}
{ // Test that very large profile data properly scales
Costs costs({// id est prof
{1, {10, 100000000000}},
{2, {20, 200000000000}},
{3, {30, 1}}}); // Make sure doesn't underflow
normalizeCosts(costs);
UASSERT_SELFTEST(uint64_t, costs[1].first, 2500000);
UASSERT_SELFTEST(uint64_t, costs[1].second, 5000000);
UASSERT_SELFTEST(uint64_t, costs[2].first, 5000000);
UASSERT_SELFTEST(uint64_t, costs[2].second, 10000000);
UASSERT_SELFTEST(uint64_t, costs[3].first, 7500000);
UASSERT_SELFTEST(uint64_t, costs[3].second, 1);
}
}
static void fillinCosts(V3Graph* execMTaskGraphp) {
V3UniqueNames m_uniqueNames; // For generating unique mtask profile hash names
// Pass 1: See what profiling data applies
Costs costs; // For each mtask, costs
for (const V3GraphVertex* vxp = execMTaskGraphp->verticesBeginp(); vxp;
vxp = vxp->verticesNextp()) {
ExecMTask* mtp = dynamic_cast<ExecMTask*>(const_cast<V3GraphVertex*>(vxp));
// Compute name of mtask, for hash lookup
mtp->hashName(m_uniqueNames.get(mtp->bodyp()));
// This estimate is 64 bits, but the final mtask graph algorithm needs 32 bits
vluint64_t costEstimate = V3InstrCount::count(mtp->bodyp(), false);
vluint64_t costProfiled = V3Config::getProfileData(v3Global.opt.prefix(), mtp->hashName());
if (costProfiled) {
UINFO(5, "Profile data for mtask " << mtp->id() << " " << mtp->hashName()
<< " cost override " << costProfiled << endl);
}
costs[mtp->id()] = std::make_pair(costEstimate, costProfiled);
}
normalizeCosts(costs /*ref*/);
int totalEstimates = 0;
int missingProfiles = 0;
for (const V3GraphVertex* vxp = execMTaskGraphp->verticesBeginp(); vxp;
vxp = vxp->verticesNextp()) {
ExecMTask* mtp = dynamic_cast<ExecMTask*>(const_cast<V3GraphVertex*>(vxp));
const uint32_t costEstimate = costs[mtp->id()].first;
const uint64_t costProfiled = costs[mtp->id()].second;
UINFO(9, "ce = " << costEstimate << " cp=" << costProfiled << endl);
UASSERT(costEstimate <= (1UL << 31), "cost scaling math would overflow uint32");
UASSERT(costProfiled <= (1UL << 31), "cost scaling math would overflow uint32");
const uint64_t costProfiled32 = static_cast<uint32_t>(costProfiled);
uint32_t costToUse = costProfiled32;
if (!costProfiled32) {
costToUse = costEstimate;
if (costEstimate != 0) ++missingProfiles;
}
if (costEstimate != 0) ++totalEstimates;
mtp->cost(costToUse);
mtp->priority(costToUse);
}
if (missingProfiles) {
if (FileLine* fl = V3Config::getProfileDataFileLine()) {
fl->v3warn(PROFOUTOFDATE, "Profile data for mtasks may be out of date. "
<< missingProfiles << " of " << totalEstimates
<< " mtasks had no data");
}
}
}
static void finalizeCosts(V3Graph* execMTaskGraphp) {
GraphStreamUnordered ser(execMTaskGraphp, GraphWay::REVERSE);
while (const V3GraphVertex* vxp = ser.nextp()) {
ExecMTask* mtp = dynamic_cast<ExecMTask*>(const_cast<V3GraphVertex*>(vxp));
uint32_t costCount = V3InstrCount::count(mtp->bodyp(), false);
mtp->cost(costCount);
mtp->priority(costCount);
// "Priority" is the critical path from the start of the mtask, to
// the end of the graph reachable from this mtask. Given the
// choice among several ready mtasks, we'll want to start the
@ -2661,6 +2801,14 @@ static void finalizeCosts(V3Graph* execMTaskGraphp) {
}
}
// Assign profiler IDs
vluint64_t profilerId = 0;
for (const V3GraphVertex* vxp = execMTaskGraphp->verticesBeginp(); vxp;
vxp = vxp->verticesNextp()) {
ExecMTask* mtp = dynamic_cast<ExecMTask*>(const_cast<V3GraphVertex*>(vxp));
mtp->profilerId(profilerId++);
}
// Removing tasks may cause edges that were formerly non-transitive to
// become transitive. Also we just created new edges around the removed
// tasks, which could be transitive. Prune out all transitive edges.
@ -2711,12 +2859,17 @@ static void addMTaskToFunction(const ThreadSchedule& schedule, const uint32_t th
// Leave this if() here, as don't want to call VL_RDTSC_Q unless profiling
addStrStmt("if (VL_UNLIKELY(vlSymsp->__Vm_profile_cycle_start)) {\n" + //
recName + " = vlSymsp->__Vm_threadPoolp->profileAppend();\n" + //
recName + "->startRecord(VL_RDTSC_Q() - vlSymsp->__Vm_profile_cycle_start,"
+ //
recName + "->startRecord(VL_RDTSC_Q()," + //
" " + cvtToStr(mtaskp->id()) + "," + //
" " + cvtToStr(mtaskp->predictStart()) + "," + //
" " + cvtToStr(mtaskp->cost()) + ");\n" + //
"}\n");
}
if (v3Global.opt.profThreads()) {
// No lock around startCounter, as counter numbers are unique per thread
addStrStmt("vlSymsp->_vm_profiler.startCounter(" + cvtToStr(mtaskp->profilerId())
+ ");\n");
}
//
addStrStmt("Verilated::mtaskId(" + cvtToStr(mtaskp->id()) + ");\n");
@ -2725,10 +2878,12 @@ static void addMTaskToFunction(const ThreadSchedule& schedule, const uint32_t th
funcp->addStmtsp(mtaskp->bodyp()->unlinkFrBack());
if (v3Global.opt.profThreads()) {
// Leave this if() here, as don't want to call VL_RDTSC_Q unless profiling
addStrStmt("if (VL_UNLIKELY(" + recName + ")) {\n" + //
recName + "->endRecord(VL_RDTSC_Q() - vlSymsp->__Vm_profile_cycle_start);\n"
+ "}\n");
// No lock around stopCounter, as counter numbers are unique per thread
addStrStmt("vlSymsp->_vm_profiler.stopCounter(" + cvtToStr(mtaskp->profilerId()) + ");\n");
}
if (v3Global.opt.profThreads()) {
addStrStmt("if (VL_UNLIKELY(" + recName + ")) " //
+ recName + "->endRecord(VL_RDTSC_Q());\n");
}
// Flush message queue
@ -2852,9 +3007,10 @@ void V3Partition::finalize() {
// V3LifePost) that can change the cost of logic within each mtask.
// Now that logic is final, recompute the cost and priority of each
// ExecMTask.
fillinCosts(execGraphp->mutableDepGraphp());
finalizeCosts(execGraphp->mutableDepGraphp());
// Replace the graph body with it's multi-threaded implementation.
// Replace the graph body with its multi-threaded implementation.
implementExecGraph(execGraphp);
}

View File

@ -50,6 +50,7 @@ public:
void go(V3Graph* mtasksp);
static void selfTest();
static void selfTestNormalizeCosts();
// Print out a hash of the shape of graphp. Only needed to debug the
// origin of some nondeterminism; otherwise this is pretty useless.

View File

@ -56,11 +56,14 @@ class ExecMTask final : public AbstractMTask {
private:
AstMTaskBody* const m_bodyp; // Task body
const uint32_t m_id; // Unique id of this mtask.
string m_hashName; // Hashed name for profile-driven optimization
uint32_t m_priority = 0; // Predicted critical path from the start of
// this mtask to the ends of the graph that are reachable from this
// mtask. In abstract time units.
uint32_t m_cost = 0; // Predicted runtime of this mtask, in the same
// abstract time units as priority().
uint64_t m_predictStart = 0; // Predicted start time of task
uint64_t m_profilerId = 0; // VerilatedCounter number for profiling
VL_UNCOPYABLE(ExecMTask);
public:
@ -74,11 +77,17 @@ public:
void priority(uint32_t pri) { m_priority = pri; }
virtual uint32_t cost() const override { return m_cost; }
void cost(uint32_t cost) { m_cost = cost; }
void predictStart(vluint64_t time) { m_predictStart = time; }
vluint64_t predictStart() const { return m_predictStart; }
void profilerId(vluint64_t id) { m_profilerId = id; }
vluint64_t profilerId() const { return m_profilerId; }
string cFuncName() const {
// If this MTask maps to a C function, this should be the name
return string("__Vmtask") + "__" + cvtToStr(m_id);
}
virtual string name() const override { return string("mt") + cvtToStr(id()); }
string hashName() const { return m_hashName; }
void hashName(const string& name) { m_hashName = name; }
void dump(std::ostream& str) const {
str << name() << "." << cvtToHex(this);
if (priority() || cost()) str << " [pr=" << priority() << " c=" << cvtToStr(cost()) << "]";

View File

@ -824,9 +824,7 @@ void V3PreProcImp::openFile(FileLine*, VInFilter* filterp, const string& filenam
for (const char* cp = sp; cp < ep; cp++) {
if (VL_UNLIKELY(*cp == '\r' || *cp == '\0')) {
strip = true;
break;
}
if (VL_UNLIKELY(*cp == '\n')) {
} else if (VL_UNLIKELY(*cp == '\n')) {
eof_newline = 0;
++eof_lineno;
} else {
@ -853,7 +851,7 @@ void V3PreProcImp::openFile(FileLine*, VInFilter* filterp, const string& filenam
FileLine* fl = new FileLine{flsp};
fl->contentLineno(eof_lineno);
fl->column(eof_newline + 1, eof_newline + 1);
fl->v3warn(EOFNEWLINE, "Missing newline at end of file (POSIX 3.206)."
fl->v3warn(EOFNEWLINE, "Missing newline at end of file (POSIX 3.206).\n"
<< fl->warnMore() << "... Suggest add newline.");
}
}

View File

@ -24,8 +24,11 @@
void V3Waiver::addEntry(V3ErrorCode errorCode, const std::string& filename,
const std::string& str) {
std::stringstream entry;
const size_t pos = str.find('\n');
entry << "lint_off -rule " << errorCode.ascii() << " -file \"*" << filename << "\" -match \""
<< str << "\"";
<< str.substr(0, pos);
if (pos != std::string::npos) entry << "*";
entry << "\"";
s_waiverList.push_back(entry.str());
}

View File

@ -1179,7 +1179,6 @@ private:
return;
}
}
nodep->backp()->dumpTree(cout, "-FIXME-tr ");
nodep->v3warn(E_UNSUPPORTED, "Unsupported/illegal unbounded ('$') in this context.");
}
virtual void visit(AstIsUnbounded* nodep) override {
@ -2196,12 +2195,13 @@ private:
virtual void visit(AstInitArray* nodep) override {
// InitArray has type of the array; children are array values
if (m_vup->prelim()) { // First stage evaluation
AstNodeDType* vdtypep = m_vup->dtypep();
AstNodeDType* const vdtypep = m_vup->dtypeNullp();
UASSERT_OBJ(vdtypep, nodep, "InitArray type not assigned by AstPattern/Var visitor");
nodep->dtypep(vdtypep);
if (AstNodeArrayDType* arrayp = VN_CAST(vdtypep->skipRefp(), NodeArrayDType)) {
if (AstNodeArrayDType* const arrayp = VN_CAST(vdtypep->skipRefp(), NodeArrayDType)) {
userIterateChildren(nodep, WidthVP(arrayp->subDTypep(), BOTH).p());
} else {
UINFO(1, "dtype object " << vdtypep->skipRefp() << endl);
nodep->v3fatalSrc("InitArray on non-array");
}
}
@ -3191,7 +3191,7 @@ private:
}
virtual void visit(AstNew* nodep) override {
if (nodep->didWidthAndSet()) return;
if (nodep->didWidth()) return;
AstClassRefDType* refp
= m_vup ? VN_CAST(m_vup->dtypeNullSkipRefp(), ClassRefDType) : nullptr;
if (!refp) { // e.g. int a = new;

View File

@ -590,6 +590,7 @@ static void verilate(const string& argString) {
V3TSP::selfTest();
V3ScoreboardBase::selfTest();
V3Partition::selfTest();
V3Partition::selfTestNormalizeCosts();
V3Broken::selfTest();
}

View File

@ -516,52 +516,23 @@ def write_visitor(filename):
def write_impl(filename):
with open_file(filename) as fh:
fh.write("\n")
fh.write(" // These for use by VN_IS only\n")
fh.write("// For internal use. They assume argument is not nullptr.\n")
for typen in sorted(Classes.keys()):
fh.write("template<> inline bool AstNode::privateIs<Ast" + typen +
">(const AstNode* nodep) { ")
fh.write("template<> inline bool AstNode::privateTypeTest<Ast" +
typen + ">(const AstNode* nodep) { ")
if typen == "Node":
fh.write("return nodep != NULL; ")
fh.write("return true; ")
else:
fh.write("return nodep && ")
fh.write("return ")
if re.search(r'^Node', typen):
fh.write(
"(static_cast<int>(nodep->type()) >= static_cast<int>(AstType::first"
+ typen + ")) && ")
"static_cast<int>(nodep->type()) >= static_cast<int>(AstType::first"
+ typen + ") && ")
fh.write(
"(static_cast<int>(nodep->type()) <= static_cast<int>(AstType::last"
+ typen + ")); ")
"static_cast<int>(nodep->type()) <= static_cast<int>(AstType::last"
+ typen + "); ")
else:
fh.write(
"(static_cast<int>(nodep->type()) == static_cast<int>(AstType::at"
+ typen + ")); ")
fh.write("}\n")
fh.write(" // These for use by VN_CAST macro only\n")
for typen in sorted(Classes.keys()):
fh.write("template<> inline Ast" + typen +
"* AstNode::privateCast<Ast" + typen +
">(AstNode* nodep) { ")
if typen == "Node":
fh.write("return nodep; ")
else:
fh.write("return AstNode::privateIs<Ast" + typen +
">(nodep) ? ")
fh.write("reinterpret_cast<Ast" + typen + "*>(nodep) : NULL; ")
fh.write("}\n")
fh.write(" // These for use by VN_CAST_CONST macro only\n")
for typen in sorted(Classes.keys()):
fh.write("template<> inline const Ast" + typen +
"* AstNode::privateConstCast<Ast" + typen +
">(const AstNode* nodep) { ")
if typen == "Node":
fh.write("return nodep; ")
else:
fh.write("return AstNode::privateIs<Ast" + typen +
">(nodep) ? ")
fh.write("reinterpret_cast<const Ast" + typen +
"*>(nodep) : NULL; ")
fh.write("nodep->type() == AstType::at" + typen + "; ")
fh.write("}\n")

View File

@ -121,6 +121,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
"no_clocker" { FL; return yVLT_NO_CLOCKER; }
"no_inline" { FL; return yVLT_NO_INLINE; }
"parallel_case" { FL; return yVLT_PARALLEL_CASE; }
"profile_data" { FL; return yVLT_PROFILE_DATA; }
"public" { FL; return yVLT_PUBLIC; }
"public_flat" { FL; return yVLT_PUBLIC_FLAT; }
"public_flat_rd" { FL; return yVLT_PUBLIC_FLAT_RD; }
@ -133,12 +134,15 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
"tracing_on" { FL; return yVLT_TRACING_ON; }
-?"-block" { FL; return yVLT_D_BLOCK; }
-?"-cost" { FL; return yVLT_D_COST; }
-?"-file" { FL; return yVLT_D_FILE; }
-?"-function" { FL; return yVLT_D_FUNCTION; }
-?"-lines" { FL; return yVLT_D_LINES; }
-?"-match" { FL; return yVLT_D_MATCH; }
-?"-model" { FL; return yVLT_D_MODEL; }
-?"-module" { FL; return yVLT_D_MODULE; }
-?"-msg" { FL; return yVLT_D_MSG; }
-?"-mtask" { FL; return yVLT_D_MTASK; }
-?"-rule" { FL; return yVLT_D_RULE; }
-?"-task" { FL; return yVLT_D_TASK; }
-?"-var" { FL; return yVLT_D_VAR; }

View File

@ -103,7 +103,7 @@ public:
return nodep;
}
AstNode* createGatePin(AstNode* exprp) {
AstRange* rangep = m_gateRangep;
AstRange* const rangep = m_gateRangep;
if (!rangep) {
return exprp;
} else {
@ -112,14 +112,14 @@ public:
}
AstNode* createTypedef(FileLine* fl, const string& name, AstNode* attrsp, AstNodeDType* basep,
AstNodeRange* rangep) {
AstNode* nodep = new AstTypedef(fl, name, attrsp, VFlagChildDType(),
GRAMMARP->createArray(basep, rangep, false));
AstNode* const nodep = new AstTypedef{fl, name, attrsp, VFlagChildDType{},
GRAMMARP->createArray(basep, rangep, false)};
SYMP->reinsert(nodep);
PARSEP->tagNodep(nodep);
return nodep;
}
AstNode* createTypedefFwd(FileLine* fl, const string& name) {
AstNode* nodep = new AstTypedefFwd(fl, name);
AstNode* const nodep = new AstTypedefFwd{fl, name};
SYMP->reinsert(nodep);
PARSEP->tagNodep(nodep);
return nodep;
@ -166,12 +166,12 @@ public:
finalp->unlinkFrBack();
rangearraysp = rangesp;
}
if (AstRange* finalRangep = VN_CAST(finalp, Range)) { // not an UnsizedRange
if (AstRange* const finalRangep = VN_CAST(finalp, Range)) { // not an UnsizedRange
if (dtypep->implicit()) {
// It's no longer implicit but a wire logic type
AstBasicDType* newp = new AstBasicDType(
AstBasicDType* const newp = new AstBasicDType{
dtypep->fileline(), AstBasicDTypeKwd::LOGIC, dtypep->numeric(),
dtypep->width(), dtypep->widthMin());
dtypep->width(), dtypep->widthMin()};
VL_DO_DANGLING(dtypep->deleteTree(), dtypep);
dtypep = newp;
}
@ -261,7 +261,7 @@ int V3ParseGrammar::s_modTypeImpNum = 0;
static void ERRSVKWD(FileLine* fileline, const string& tokname) {
static int toldonce = 0;
fileline->v3error(
string("Unexpected '") + tokname + "': '" + tokname
std::string{"Unexpected '"} + tokname + "': '" + tokname
+ "' is a SystemVerilog keyword misused as an identifier."
+ (!toldonce++ ? "\n" + V3Error::warnMore()
+ "... Suggest modify the Verilog-2001 code to avoid SV keywords,"
@ -278,19 +278,6 @@ static void UNSUPREAL(FileLine* fileline) {
void yyerror(const char* errmsg) { PARSEP->bisonLastFileline()->v3error(errmsg); }
void yyerrorf(const char* format, ...) {
const int maxlen = 2000;
char msg[maxlen];
va_list ap;
va_start(ap, format);
VL_VSNPRINTF(msg, maxlen, format, ap);
msg[maxlen - 1] = '\0';
va_end(ap);
yyerror(msg);
}
//======================================================================
class AstSenTree;
@ -363,6 +350,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
%token<fl> yVLT_NO_CLOCKER "no_clocker"
%token<fl> yVLT_NO_INLINE "no_inline"
%token<fl> yVLT_PARALLEL_CASE "parallel_case"
%token<fl> yVLT_PROFILE_DATA "profile_data"
%token<fl> yVLT_PUBLIC "public"
%token<fl> yVLT_PUBLIC_FLAT "public_flat"
%token<fl> yVLT_PUBLIC_FLAT_RD "public_flat_rd"
@ -375,12 +363,15 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
%token<fl> yVLT_TRACING_ON "tracing_on"
%token<fl> yVLT_D_BLOCK "--block"
%token<fl> yVLT_D_COST "--cost"
%token<fl> yVLT_D_FILE "--file"
%token<fl> yVLT_D_FUNCTION "--function"
%token<fl> yVLT_D_LINES "--lines"
%token<fl> yVLT_D_MODULE "--module"
%token<fl> yVLT_D_MATCH "--match"
%token<fl> yVLT_D_MODEL "--model"
%token<fl> yVLT_D_MODULE "--module"
%token<fl> yVLT_D_MSG "--msg"
%token<fl> yVLT_D_MTASK "--mtask"
%token<fl> yVLT_D_RULE "--rule"
%token<fl> yVLT_D_TASK "--task"
%token<fl> yVLT_D_VAR "--var"
@ -1127,7 +1118,7 @@ package_import_itemList<nodep>:
package_import_item<nodep>: // ==IEEE: package_import_item
idCC/*package_identifier*/ yP_COLONCOLON package_import_itemObj
{
if (!VN_CAST($<scp>1, Package)) {
if (!VN_IS($<scp>1, Package)) {
$$ = nullptr;
$<fl>1->v3error("Importing from missing package '" << *$<strp>1 << "'");
} else {
@ -1137,8 +1128,8 @@ package_import_item<nodep>: // ==IEEE: package_import_item
;
package_import_itemObj<strp>: // IEEE: part of package_import_item
idAny/*package_identifier*/ { $<fl>$=$<fl>1; $$=$1; }
| '*' { $<fl>$=$<fl>1; static string star="*"; $$=&star; }
idAny/*package_identifier*/ { $<fl>$ = $<fl>1; $$ = $1; }
| '*' { $<fl>$ = $<fl>1; static string star = "*"; $$ = &star; }
;
package_export_declaration<nodep>: // IEEE: package_export_declaration
@ -1166,7 +1157,8 @@ module_declaration: // ==IEEE: module_declaration
modFront importsAndParametersE portsStarE ';'
/*cont*/ module_itemListE yENDMODULE endLabelE
{ $1->modTrace(GRAMMARP->allTracingOn($1->fileline())); // Stash for implicit wires, etc
if ($2) $1->addStmtp($2); if ($3) $1->addStmtp($3);
if ($2) $1->addStmtp($2);
if ($3) $1->addStmtp($3);
if ($5) $1->addStmtp($5);
GRAMMARP->m_modp = nullptr;
SYMP->popScope($1);
@ -1174,7 +1166,8 @@ module_declaration: // ==IEEE: module_declaration
| udpFront parameter_port_listE portsStarE ';'
/*cont*/ module_itemListE yENDPRIMITIVE endLabelE
{ $1->modTrace(false); // Stash for implicit wires, etc
if ($2) $1->addStmtp($2); if ($3) $1->addStmtp($3);
if ($2) $1->addStmtp($2);
if ($3) $1->addStmtp($3);
if ($5) $1->addStmtp($5);
GRAMMARP->m_tracingParse = true;
GRAMMARP->m_modp = nullptr;
@ -1285,7 +1278,7 @@ list_of_ports<nodep>: // IEEE: list_of_ports + list_of_port_declarations
portAndTagE<nodep>:
/* empty */
{ int p = PINNUMINC();
const string name = "__pinNumber" + cvtToStr(p);
const string name = "__pinNumber" + cvtToStr(p);
$$ = new AstPort{CRELINE(), p, name};
AstVar* varp = new AstVar{CRELINE(), AstVarType::PORT, name, VFlagChildDType{},
new AstBasicDType{CRELINE(), LOGIC_IMPLICIT}};
@ -1377,13 +1370,13 @@ port<nodep>: // ==IEEE: port
{ $$=$2; /*VARDTYPE-same*/ $$->addNextNull(VARDONEP($$,$3,$4)); }
//
| portDirNetE data_type portSig variable_dimensionListE sigAttrListE '=' constExpr
{ $$=$3; VARDTYPE($2); if (AstVar* vp=VARDONEP($$,$4,$5)) { $$->addNextNull(vp); vp->valuep($7); } }
{ $$=$3; VARDTYPE($2); if (AstVar* vp = VARDONEP($$, $4, $5)) { $$->addNextNull(vp); vp->valuep($7); } }
| portDirNetE yVAR data_type portSig variable_dimensionListE sigAttrListE '=' constExpr
{ $$=$4; VARDTYPE($3); if (AstVar* vp=VARDONEP($$,$5,$6)) { $$->addNextNull(vp); vp->valuep($8); } }
{ $$=$4; VARDTYPE($3); if (AstVar* vp = VARDONEP($$, $5, $6)) { $$->addNextNull(vp); vp->valuep($8); } }
| portDirNetE yVAR implicit_typeE portSig variable_dimensionListE sigAttrListE '=' constExpr
{ $$=$4; VARDTYPE($3); if (AstVar* vp=VARDONEP($$,$5,$6)) { $$->addNextNull(vp); vp->valuep($8); } }
{ $$=$4; VARDTYPE($3); if (AstVar* vp = VARDONEP($$, $5, $6)) { $$->addNextNull(vp); vp->valuep($8); } }
| portDirNetE /*implicit*/ portSig variable_dimensionListE sigAttrListE '=' constExpr
{ $$=$2; /*VARDTYPE-same*/ if (AstVar* vp=VARDONEP($$,$3,$4)) { $$->addNextNull(vp); vp->valuep($6); } }
{ $$=$2; /*VARDTYPE-same*/ if (AstVar* vp = VARDONEP($$, $3, $4)) { $$->addNextNull(vp); vp->valuep($6); } }
;
portDirNetE: // IEEE: part of port, optional net type and/or direction
@ -1474,7 +1467,7 @@ interface_or_generate_item<nodep>: // ==IEEE: interface_or_generate_item
anonymous_program<nodep>: // ==IEEE: anonymous_program
// // See the spec - this doesn't change the scope, items still go up "top"
yPROGRAM ';' anonymous_program_itemListE yENDPROGRAM
{ BBUNSUP($<fl>1, "Unsupported: Anonymous programs"); $$ = nullptr; }
{ $$ = nullptr; BBUNSUP($<fl>1, "Unsupported: Anonymous programs"); }
;
anonymous_program_itemListE<nodep>: // IEEE: { anonymous_program_item }
@ -1501,7 +1494,8 @@ program_declaration: // IEEE: program_declaration + program_nonansi_header + pr
pgmFront parameter_port_listE portsStarE ';'
/*cont*/ program_itemListE yENDPROGRAM endLabelE
{ $1->modTrace(GRAMMARP->allTracingOn($1->fileline())); // Stash for implicit wires, etc
if ($2) $1->addStmtp($2); if ($3) $1->addStmtp($3);
if ($2) $1->addStmtp($2);
if ($3) $1->addStmtp($3);
if ($5) $1->addStmtp($5);
GRAMMARP->m_modp = nullptr;
SYMP->popScope($1);
@ -1628,7 +1622,8 @@ list_of_genvar_identifiers<nodep>: // IEEE: list_of_genvar_identifiers (for decl
genvar_identifierDecl<varp>: // IEEE: genvar_identifier (for declaration)
id/*new-genvar_identifier*/ sigAttrListE
{ VARRESET_NONLIST(GENVAR); VARDTYPE(new AstBasicDType($<fl>1,AstBasicDTypeKwd::INTEGER));
{ VARRESET_NONLIST(GENVAR);
VARDTYPE(new AstBasicDType($<fl>1, AstBasicDTypeKwd::INTEGER));
$$ = VARDONEA($<fl>1, *$1, nullptr, $2); }
;
@ -1725,11 +1720,11 @@ varParamReset:
port_direction: // ==IEEE: port_direction + tf_port_direction
// // IEEE 19.8 just "input" FIRST forces type to wire - we'll ignore that here
// // Only used for ANSI declarations
yINPUT { GRAMMARP->m_pinAnsi=true; VARIO(INPUT); }
| yOUTPUT { GRAMMARP->m_pinAnsi=true; VARIO(OUTPUT); }
| yINOUT { GRAMMARP->m_pinAnsi=true; VARIO(INOUT); }
| yREF { GRAMMARP->m_pinAnsi=true; VARIO(REF); }
| yCONST__REF yREF { GRAMMARP->m_pinAnsi=true; VARIO(CONSTREF); }
yINPUT { GRAMMARP->m_pinAnsi = true; VARIO(INPUT); }
| yOUTPUT { GRAMMARP->m_pinAnsi = true; VARIO(OUTPUT); }
| yINOUT { GRAMMARP->m_pinAnsi = true; VARIO(INOUT); }
| yREF { GRAMMARP->m_pinAnsi = true; VARIO(REF); }
| yCONST__REF yREF { GRAMMARP->m_pinAnsi = true; VARIO(CONSTREF); }
;
port_directionReset: // IEEE: port_direction that starts a port_declaraiton
@ -1765,7 +1760,7 @@ port_declaration<nodep>: // ==IEEE: port_declaration
/*mid*/ { VARDTYPE_NDECL(new AstBasicDType($<fl>3, LOGIC_IMPLICIT, $3)); }
/*cont*/ list_of_variable_decl_assignments { $$ = $5; }
| port_directionReset port_declNetE /*implicit*/
/*mid*/ { VARDTYPE_NDECL(nullptr);/*default_nettype*/}
/*mid*/ { VARDTYPE_NDECL(nullptr); /*default_nettype*/ }
/*cont*/ list_of_variable_decl_assignments { $$ = $4; }
// // IEEE: interface_declaration
// // Looks just like variable declaration unless has a period
@ -1826,7 +1821,7 @@ simple_type<dtypep>: // ==IEEE: simple_type
// // Even though we looked up the type and have a AstNode* to it,
// // we can't fully resolve it because it may have been just a forward definition.
| packageClassScopeE idType
{ AstRefDType* refp = new AstRefDType($<fl>2, *$2, $1, nullptr);
{ AstRefDType* const refp = new AstRefDType{$<fl>2, *$2, $1, nullptr};
$$ = refp; }
//
// // { generate_block_identifer ... } '.'
@ -1844,10 +1839,10 @@ data_type<dtypep>: // ==IEEE: data_type
// // IEEE: ps_covergroup_identifier
// // Don't distinguish between types and classes so all these combined
| packageClassScopeE idType packed_dimensionListE
{ AstRefDType* refp = new AstRefDType($<fl>2, *$2, $1, nullptr);
{ AstRefDType* const refp = new AstRefDType{$<fl>2, *$2, $1, nullptr};
$$ = GRAMMARP->createArray(refp, $3, true); }
| packageClassScopeE idType parameter_value_assignmentClass packed_dimensionListE
{ AstRefDType* refp = new AstRefDType($<fl>2, *$2, $1, $3);
{ AstRefDType* const refp = new AstRefDType{$<fl>2, *$2, $1, $3};
$$ = GRAMMARP->createArray(refp, $4, true); }
;
@ -1904,11 +1899,11 @@ struct_unionDecl<uorstructp>: // IEEE: part of data_type
ySTRUCT packedSigningE '{'
/*mid*/ { $<uorstructp>$ = new AstStructDType($1, $2); SYMP->pushNew($<uorstructp>$); }
/*cont*/ struct_union_memberList '}'
{ $$=$<uorstructp>4; $$->addMembersp($5); SYMP->popScope($$); }
{ $$ = $<uorstructp>4; $$->addMembersp($5); SYMP->popScope($$); }
| yUNION taggedE packedSigningE '{'
/*mid*/ { $<uorstructp>$ = new AstUnionDType($1, $3); SYMP->pushNew($<uorstructp>$); }
/*cont*/ struct_union_memberList '}'
{ $$=$<uorstructp>5; $$->addMembersp($6); SYMP->popScope($$); }
{ $$ = $<uorstructp>5; $$->addMembersp($6); SYMP->popScope($$); }
;
struct_union_memberList<nodep>: // IEEE: { struct_union_member }
@ -2493,13 +2488,14 @@ loop_generate_construct<nodep>: // ==IEEE: loop_generate_construct
AstBegin* lowerBegp = VN_CAST($9, Begin);
UASSERT_OBJ(!($9 && !lowerBegp), $9, "Child of GENFOR should have been begin");
if (!lowerBegp) lowerBegp = new AstBegin($1, "", nullptr, true, false); // Empty body
AstNode* lowerNoBegp = lowerBegp->stmtsp();
if (!lowerBegp) lowerBegp = new AstBegin{$1, "", nullptr, true, false}; // Empty body
AstNode* const lowerNoBegp = lowerBegp->stmtsp();
if (lowerNoBegp) lowerNoBegp->unlinkFrBackWithNext();
//
AstBegin* blkp = new AstBegin($1, lowerBegp->name(), nullptr, true, true);
AstBegin* const blkp = new AstBegin{$1, lowerBegp->name(), nullptr, true, true};
// V3LinkDot detects BEGIN(GENFOR(...)) as a special case
AstNode* initp = $3; AstNode* varp = $3;
AstNode* initp = $3;
AstNode* const varp = $3;
if (VN_IS(varp, Var)) { // Genvar
initp = varp->nextp();
initp->unlinkFrBackWithNext(); // Detach 2nd from varp, make 1st init
@ -2550,8 +2546,8 @@ case_generate_itemListE<nodep>: // IEEE: [{ case_generate_itemList }]
;
case_generate_itemList<nodep>: // IEEE: { case_generate_itemList }
~c~case_generate_item { $$=$1; }
| ~c~case_generate_itemList ~c~case_generate_item { $$=$1; $1->addNext($2); }
~c~case_generate_item { $$ = $1; }
| ~c~case_generate_itemList ~c~case_generate_item { $$ = $1; $1->addNext($2); }
;
//UNSUPc_case_generate_itemList<nodep>: // IEEE: { case_generate_item } (for checkers)
@ -2559,9 +2555,9 @@ case_generate_itemList<nodep>: // IEEE: { case_generate_itemList }
//UNSUP ;
case_generate_item<nodep>: // ==IEEE: case_generate_item
caseCondList colon generate_block_or_null { $$ = new AstCaseItem($2,$1,$3); }
| yDEFAULT colon generate_block_or_null { $$ = new AstCaseItem($1,nullptr,$3); }
| yDEFAULT generate_block_or_null { $$ = new AstCaseItem($1,nullptr,$2); }
caseCondList colon generate_block_or_null { $$ = new AstCaseItem{$2, $1, $3}; }
| yDEFAULT colon generate_block_or_null { $$ = new AstCaseItem{$1, nullptr, $3}; }
| yDEFAULT generate_block_or_null { $$ = new AstCaseItem{$1, nullptr, $2}; }
;
//UNSUPc_case_generate_item<nodep>: // IEEE: case_generate_item (for checkers)
@ -2635,8 +2631,8 @@ netSig<varp>: // IEEE: net_decl_assignment - one element from list_of_port_id
;
netId<strp>:
id/*new-net*/ { $$ = $1; $<fl>$=$<fl>1; }
| idSVKwd { $$ = $1; $<fl>$=$<fl>1; }
id/*new-net*/ { $$ = $1; $<fl>$ = $<fl>1; }
| idSVKwd { $$ = $1; $<fl>$ = $<fl>1; }
;
sigAttrListE<nodep>:
@ -2677,7 +2673,7 @@ rangeList<rangep>: // IEEE: {packed_dimension}
//UNSUPbit_selectE<fl>: // IEEE: constant_bit_select (IEEE included empty)
//UNSUP /* empty */ { $$ = nullptr; }
//UNSUP | '[' constExpr ']' { $<fl>$=$<fl>1; $$ = "["+$2+"]"; }
//UNSUP | '[' constExpr ']' { $<fl>$ = $<fl>1; $$ = "[" + $2 + "]"; }
//UNSUP ;
// IEEE: select
@ -2712,12 +2708,13 @@ param_assignment<varp>: // ==IEEE: param_assignment
id/*new-parameter*/ variable_dimensionListE sigAttrListE exprOrDataTypeEqE
{ // To handle #(type A=int, B=A) and properly imply B
// as a type (for parsing) we need to detect "A" is a type
if (AstNodeDType* refp = VN_CAST($4, NodeDType)) {
if (VSymEnt* foundp = SYMP->symCurrentp()->findIdFallback(refp->name())) {
if (AstNodeDType* const refp = VN_CAST($4, NodeDType)) {
if (VSymEnt* const foundp = SYMP->symCurrentp()->findIdFallback(refp->name())) {
UINFO(9, "declaring type via param assignment" << foundp->nodep() << endl);
VARDTYPE(new AstParseTypeDType($<fl>1))
VARDTYPE(new AstParseTypeDType{$<fl>1})
SYMP->reinsert(foundp->nodep()->cloneTree(false), nullptr, *$1); }}
$$ = VARDONEA($<fl>1, *$1, $2, $3); if ($4) $$->valuep($4); }
$$ = VARDONEA($<fl>1, *$1, $2, $3);
if ($4) $$->valuep($4); }
;
list_of_param_assignments<varp>: // ==IEEE: list_of_param_assignments
@ -2765,8 +2762,9 @@ instDecl<nodep>:
// // Currently disambiguated from data_declaration based on
// // VARs being type, and cells non-type.
// // IEEE requires a '(' to disambiguate, we need TODO force this
id parameter_value_assignmentE {INSTPREP($<fl>1,*$1,$2);} instnameList ';'
{ $$ = $4; GRAMMARP->m_impliedDecl=false;
id parameter_value_assignmentE {INSTPREP($<fl>1, *$1, $2);} instnameList ';'
{ $$ = $4;
GRAMMARP->m_impliedDecl = false;
if (GRAMMARP->m_instParamp) {
VL_DO_CLEAR(GRAMMARP->m_instParamp->deleteTree(),
GRAMMARP->m_instParamp = nullptr);
@ -2851,10 +2849,10 @@ cellparamItemE<pinp>: // IEEE: named_parameter_assignment + empty
| yP_DOTSTAR { $$ = new AstPin($1,PINNUMINC(),".*",nullptr); }
| '.' idSVKwd { $$ = new AstPin($<fl>2,PINNUMINC(), *$2,
new AstParseRef($<fl>2,VParseRefExp::PX_TEXT,*$2,nullptr,nullptr));
$$->svImplicit(true);}
$$->svImplicit(true); }
| '.' idAny { $$ = new AstPin($<fl>2,PINNUMINC(), *$2,
new AstParseRef($<fl>2,VParseRefExp::PX_TEXT,*$2,nullptr,nullptr));
$$->svImplicit(true);}
$$->svImplicit(true); }
| '.' idAny '(' ')' { $$ = new AstPin($<fl>2,PINNUMINC(),*$2,nullptr); }
// // mintypmax is expanded here, as it might be a UDP or gate primitive
// // data_type for 'parameter type' hookups
@ -3057,7 +3055,7 @@ block_item_declaration<nodep>: // ==IEEE: block_item_declaration
stmtList<nodep>:
stmtBlock { $$ = $1; }
| stmtList stmtBlock { $$ = ($2==nullptr)?($1):($1->addNext($2)); }
| stmtList stmtBlock { $$ = $2 ? $1->addNext($2) : $1; }
;
stmt<nodep>: // IEEE: statement_or_null == function_statement_or_null
@ -3101,26 +3099,27 @@ statement_item<nodep>: // IEEE: statement_item
if ($1 == uniq_UNIQUE0) $2->unique0Pragma(true);
if ($1 == uniq_PRIORITY) $2->priorityPragma(true); }
//UNSUP caseStart caseAttrE yMATCHES case_patternListE yENDCASE { }
| unique_priorityE caseStart caseAttrE yINSIDE case_insideListE yENDCASE { $$ = $2; if ($5) $2->addItemsp($5);
if (!$2->caseSimple()) $2->v3error("Illegal to have inside on a casex/casez");
$2->caseInsideSet();
if ($1 == uniq_UNIQUE) $2->uniquePragma(true);
if ($1 == uniq_UNIQUE0) $2->unique0Pragma(true);
if ($1 == uniq_PRIORITY) $2->priorityPragma(true); }
| unique_priorityE caseStart caseAttrE yINSIDE case_insideListE yENDCASE
{ $$ = $2; if ($5) $2->addItemsp($5);
if (!$2->caseSimple()) $2->v3error("Illegal to have inside on a casex/casez");
$2->caseInsideSet();
if ($1 == uniq_UNIQUE) $2->uniquePragma(true);
if ($1 == uniq_UNIQUE0) $2->unique0Pragma(true);
if ($1 == uniq_PRIORITY) $2->priorityPragma(true); }
//
// // IEEE: conditional_statement
| unique_priorityE yIF '(' expr ')' stmtBlock %prec prLOWER_THAN_ELSE
{ AstIf* newp = new AstIf($2,$4,$6,nullptr);
$$ = newp;
if ($1 == uniq_UNIQUE) newp->uniquePragma(true);
if ($1 == uniq_UNIQUE0) newp->unique0Pragma(true);
if ($1 == uniq_PRIORITY) newp->priorityPragma(true); }
{ AstIf* const newp = new AstIf{$2, $4, $6, nullptr};
$$ = newp;
if ($1 == uniq_UNIQUE) newp->uniquePragma(true);
if ($1 == uniq_UNIQUE0) newp->unique0Pragma(true);
if ($1 == uniq_PRIORITY) newp->priorityPragma(true); }
| unique_priorityE yIF '(' expr ')' stmtBlock yELSE stmtBlock
{ AstIf* newp = new AstIf($2,$4,$6,$8);
$$ = newp;
if ($1 == uniq_UNIQUE) newp->uniquePragma(true);
if ($1 == uniq_UNIQUE0) newp->unique0Pragma(true);
if ($1 == uniq_PRIORITY) newp->priorityPragma(true); }
{ AstIf* const newp = new AstIf{$2, $4, $6, $8};
$$ = newp;
if ($1 == uniq_UNIQUE) newp->uniquePragma(true);
if ($1 == uniq_UNIQUE0) newp->unique0Pragma(true);
if ($1 == uniq_PRIORITY) newp->priorityPragma(true); }
//
| finc_or_dec_expression ';' { $$ = $1; }
// // IEEE: inc_or_dec_expression
@ -3133,15 +3132,15 @@ statement_item<nodep>: // IEEE: statement_item
// // so parse as if task
// // Alternative would be shim with new AstVoidStmt.
| yVOID yP_TICK '(' task_subroutine_callNoMethod ')' ';'
{ $$ = $4;
FileLine* newfl = new FileLine($$->fileline());
newfl->warnOff(V3ErrorCode::IGNOREDRETURN, true);
$$->fileline(newfl); }
{ $$ = $4;
FileLine* const newfl = new FileLine{$$->fileline()};
newfl->warnOff(V3ErrorCode::IGNOREDRETURN, true);
$$->fileline(newfl); }
| yVOID yP_TICK '(' expr '.' task_subroutine_callNoMethod ')' ';'
{ $$ = new AstDot($5, false, $4, $6);
FileLine* newfl = new FileLine($6->fileline());
newfl->warnOff(V3ErrorCode::IGNOREDRETURN, true);
$6->fileline(newfl); }
{ $$ = new AstDot{$5, false, $4, $6};
FileLine* const newfl = new FileLine{$6->fileline()};
newfl->warnOff(V3ErrorCode::IGNOREDRETURN, true);
$6->fileline(newfl); }
// // Expr included here to resolve our not knowing what is a method call
// // Expr here must result in a subroutine_call
| task_subroutine_callNoMethod ';' { $$ = $1; }
@ -3171,8 +3170,8 @@ statement_item<nodep>: // IEEE: statement_item
//
// // IEEE: loop_statement
| yFOREVER stmtBlock { $$ = new AstWhile($1,new AstConst($1, AstConst::BitTrue()), $2); }
| yREPEAT '(' expr ')' stmtBlock { $$ = new AstRepeat($1,$3,$5);}
| yWHILE '(' expr ')' stmtBlock { $$ = new AstWhile($1,$3,$5);}
| yREPEAT '(' expr ')' stmtBlock { $$ = new AstRepeat{$1, $3, $5}; }
| yWHILE '(' expr ')' stmtBlock { $$ = new AstWhile{$1, $3, $5}; }
// // for's first ';' is in for_initialization
| statementFor { $$ = $1; }
| yDO stmtBlock yWHILE '(' expr ')' ';' { if ($2) {
@ -3275,11 +3274,15 @@ foperator_assignment<nodep>: // IEEE: operator_assignment (for first part of exp
inc_or_dec_expression<nodep>: // ==IEEE: inc_or_dec_expression
// // Need fexprScope instead of variable_lvalue to prevent conflict
~l~exprScope yP_PLUSPLUS { $<fl>$=$<fl>1; $$ = new AstPostAdd($2, new AstConst($2, AstConst::StringToParse(), "'b1"), $1, $1->cloneTree(true)); }
| ~l~exprScope yP_MINUSMINUS { $<fl>$=$<fl>1; $$ = new AstPostSub($2, new AstConst($2, AstConst::StringToParse(), "'b1"), $1, $1->cloneTree(true)); }
~l~exprScope yP_PLUSPLUS
{ $<fl>$ = $<fl>1; $$ = new AstPostAdd{$2, new AstConst{$2, AstConst::StringToParse(), "'b1"}, $1, $1->cloneTree(true)}; }
| ~l~exprScope yP_MINUSMINUS
{ $<fl>$ = $<fl>1; $$ = new AstPostSub{$2, new AstConst{$2, AstConst::StringToParse(), "'b1"}, $1, $1->cloneTree(true)}; }
// // Need expr instead of variable_lvalue to prevent conflict
| yP_PLUSPLUS expr { $<fl>$=$<fl>1; $$ = new AstPreAdd($1, new AstConst($1, AstConst::StringToParse(), "'b1"), $2, $2->cloneTree(true)); }
| yP_MINUSMINUS expr { $<fl>$=$<fl>1; $$ = new AstPreSub($1, new AstConst($1, AstConst::StringToParse(), "'b1"), $2, $2->cloneTree(true)); }
| yP_PLUSPLUS expr
{ $<fl>$ = $<fl>1; $$ = new AstPreAdd{$1, new AstConst{$1, AstConst::StringToParse(), "'b1"}, $2, $2->cloneTree(true)}; }
| yP_MINUSMINUS expr
{ $<fl>$ = $<fl>1; $$ = new AstPreSub{$1, new AstConst{$1, AstConst::StringToParse(), "'b1"}, $2, $2->cloneTree(true)}; }
;
finc_or_dec_expression<nodep>: // ==IEEE: inc_or_dec_expression
@ -3352,26 +3355,26 @@ case_insideListE<caseitemp>: // IEEE: [ { case_inside_item } ]
;
case_itemList<caseitemp>: // IEEE: { case_item + ... }
caseCondList colon stmtBlock { $$ = new AstCaseItem($2,$1,$3); }
| yDEFAULT colon stmtBlock { $$ = new AstCaseItem($1,nullptr,$3); }
| yDEFAULT stmtBlock { $$ = new AstCaseItem($1,nullptr,$2); }
| case_itemList caseCondList colon stmtBlock { $$ = $1;$1->addNext(new AstCaseItem($3,$2,$4)); }
| case_itemList yDEFAULT stmtBlock { $$ = $1;$1->addNext(new AstCaseItem($2,nullptr,$3)); }
| case_itemList yDEFAULT colon stmtBlock { $$ = $1;$1->addNext(new AstCaseItem($2,nullptr,$4)); }
caseCondList colon stmtBlock { $$ = new AstCaseItem{$2, $1, $3}; }
| yDEFAULT colon stmtBlock { $$ = new AstCaseItem{$1, nullptr, $3}; }
| yDEFAULT stmtBlock { $$ = new AstCaseItem{$1, nullptr, $2}; }
| case_itemList caseCondList colon stmtBlock { $$ = $1; $1->addNext(new AstCaseItem{$3, $2, $4}); }
| case_itemList yDEFAULT stmtBlock { $$ = $1; $1->addNext(new AstCaseItem{$2, nullptr, $3}); }
| case_itemList yDEFAULT colon stmtBlock { $$ = $1; $1->addNext(new AstCaseItem{$2, nullptr, $4}); }
;
case_inside_itemList<caseitemp>: // IEEE: { case_inside_item + open_range_list ... }
open_range_list colon stmtBlock { $$ = new AstCaseItem($2,$1,$3); }
| yDEFAULT colon stmtBlock { $$ = new AstCaseItem($1,nullptr,$3); }
| yDEFAULT stmtBlock { $$ = new AstCaseItem($1,nullptr,$2); }
| case_inside_itemList open_range_list colon stmtBlock { $$ = $1;$1->addNext(new AstCaseItem($3,$2,$4)); }
| case_inside_itemList yDEFAULT stmtBlock { $$ = $1;$1->addNext(new AstCaseItem($2,nullptr,$3)); }
| case_inside_itemList yDEFAULT colon stmtBlock { $$ = $1;$1->addNext(new AstCaseItem($2,nullptr,$4)); }
open_range_list colon stmtBlock { $$ = new AstCaseItem{$2, $1, $3}; }
| yDEFAULT colon stmtBlock { $$ = new AstCaseItem{$1, nullptr, $3}; }
| yDEFAULT stmtBlock { $$ = new AstCaseItem{$1, nullptr, $2}; }
| case_inside_itemList open_range_list colon stmtBlock { $$ = $1; $1->addNext(new AstCaseItem{$3, $2, $4}); }
| case_inside_itemList yDEFAULT stmtBlock { $$ = $1; $1->addNext(new AstCaseItem{$2, nullptr, $3}); }
| case_inside_itemList yDEFAULT colon stmtBlock { $$ = $1; $1->addNext(new AstCaseItem{$2, nullptr, $4}); }
;
open_range_list<nodep>: // ==IEEE: open_range_list + open_value_range
open_value_range { $$ = $1; }
| open_range_list ',' open_value_range { $$ = $1;$1->addNext($3); }
| open_range_list ',' open_value_range { $$ = $1; $1->addNext($3); }
;
open_value_range<nodep>: // ==IEEE: open_value_range
@ -3390,7 +3393,7 @@ value_range<nodep>: // ==IEEE: value_range
caseCondList<nodep>: // IEEE: part of case_item
expr { $$ = $1; }
| caseCondList ',' expr { $$ = $1;$1->addNext($3); }
| caseCondList ',' expr { $$ = $1; $1->addNext($3); }
;
patternNoExpr<nodep>: // IEEE: pattern **Excluding Expr*
@ -3411,8 +3414,9 @@ patternList<nodep>: // IEEE: part of pattern
;
patternOne<nodep>: // IEEE: part of pattern
expr { if ($1) { $$ = new AstPatMember($1->fileline(),$1,nullptr,nullptr); } else { $$=nullptr; } }
| expr '{' argsExprList '}' { $$ = new AstPatMember($2,$3,nullptr,$1); }
expr
{ if ($1) $$ = new AstPatMember{$1->fileline(), $1, nullptr, nullptr}; else $$ = nullptr; }
| expr '{' argsExprList '}' { $$ = new AstPatMember{$2, $3, nullptr, $1}; }
| patternNoExpr { $$ = $1; }
;
@ -3434,7 +3438,7 @@ patternKey<nodep>: // IEEE: merge structure_pattern_key, array_pattern_key, ass
// // id/*member*/ is part of constExpr below
//UNSUP constExpr { $$ = $1; }
// // IEEE: assignment_pattern_key
//UNSUP simple_type { $1->v3error("Unsupported: '{} with data type as key"); $$=$1; }
//UNSUP simple_type { $1->v3error("Unsupported: '{} with data type as key"); $$ = $1; }
// // simple_type reference looks like constExpr
// // Verilator:
// // The above expressions cause problems because "foo" may be a constant identifier
@ -3518,7 +3522,7 @@ for_step_assignment<nodep>: // ==IEEE: for_step_assignment
loop_variables<nodep>: // IEEE: loop_variables
varRefBase { $$ = $1; }
| loop_variables ',' varRefBase { $$ = $1;$1->addNext($3); }
| loop_variables ',' varRefBase { $$ = $1; $1->addNext($3); }
;
//************************************************
@ -3704,7 +3708,7 @@ system_t_call<nodep>: // IEEE: system_tf_call (as task)
| yD_WRITEMEMH '(' expr ',' idClassSel ',' expr ',' expr ')' { $$ = new AstWriteMem($1, true, $3, $5, $7, $9); }
//
| yD_CAST '(' expr ',' expr ')'
{ FileLine* fl_nowarn = new FileLine($1);
{ FileLine* const fl_nowarn = new FileLine{$1};
fl_nowarn->warnOff(V3ErrorCode::WIDTH, true);
$$ = new AstAssertIntrinsic(fl_nowarn, new AstCastDynamic(fl_nowarn, $5, $3), nullptr, nullptr, true); }
//
@ -3892,8 +3896,10 @@ task_declaration<ftaskp>: // ==IEEE: task_declaration
;
task_prototype<ftaskp>: // ==IEEE: task_prototype
yTASK taskId '(' tf_port_listE ')' { $$=$2; $$->addStmtsp($4); $$->prototype(true); SYMP->popScope($$); }
| yTASK taskId { $$=$2; $$->prototype(true); SYMP->popScope($$); }
yTASK taskId '(' tf_port_listE ')'
{ $$ = $2; $$->addStmtsp($4); $$->prototype(true); SYMP->popScope($$); }
| yTASK taskId
{ $$ = $2; $$->prototype(true); SYMP->popScope($$); }
;
function_declaration<ftaskp>: // IEEE: function_declaration + function_body_declaration
@ -3910,13 +3916,17 @@ function_declaration<ftaskp>: // IEEE: function_declaration + function_body_decl
;
function_prototype<ftaskp>: // IEEE: function_prototype
yFUNCTION funcId '(' tf_port_listE ')' { $$=$2; $$->addStmtsp($4); $$->prototype(true); SYMP->popScope($$); }
| yFUNCTION funcId { $$=$2; $$->prototype(true); SYMP->popScope($$); }
yFUNCTION funcId '(' tf_port_listE ')'
{ $$ = $2; $$->addStmtsp($4); $$->prototype(true); SYMP->popScope($$); }
| yFUNCTION funcId
{ $$ = $2; $$->prototype(true); SYMP->popScope($$); }
;
class_constructor_prototype<ftaskp>: // ==IEEE: class_constructor_prototype
yFUNCTION funcIdNew '(' tf_port_listE ')' ';' { $$ = $2; $$->addStmtsp($4); $$->prototype(true); SYMP->popScope($$); }
| yFUNCTION funcIdNew ';' { $$ = $2; $$->prototype(true); SYMP->popScope($$); }
yFUNCTION funcIdNew '(' tf_port_listE ')' ';'
{ $$ = $2; $$->addStmtsp($4); $$->prototype(true); SYMP->popScope($$); }
| yFUNCTION funcIdNew ';'
{ $$ = $2; $$->prototype(true); SYMP->popScope($$); }
;
funcIsolateE<cint>:
@ -4137,7 +4147,7 @@ dpi_import_export<nodep>: // ==IEEE: dpi_import_export
dpi_importLabelE<strp>: // IEEE: part of dpi_import_export
/* empty */ { static string s; $$ = &s; }
| idAny/*c_identifier*/ '=' { $$ = $1; $<fl>$=$<fl>1; }
| idAny/*c_identifier*/ '=' { $$ = $1; $<fl>$ = $<fl>1; }
;
dpi_tf_import_propertyE<iprop>: // IEEE: [ dpi_function_import_property + dpi_task_import_property ]
@ -4189,7 +4199,7 @@ expr<nodep>: // IEEE: part of expression/constant_expression/primary
| yP_XNOR ~r~expr %prec prREDUCTION { $$ = new AstLogNot($1, new AstRedXor($1, $2)); }
//
// // IEEE: inc_or_dec_expression
| ~l~inc_or_dec_expression { $<fl>$=$<fl>1; $$ = $1; }
| ~l~inc_or_dec_expression { $<fl>$ = $<fl>1; $$ = $1; }
//
// // IEEE: '(' operator_assignment ')'
// // Need exprScope of variable_lvalue to prevent conflict
@ -4239,7 +4249,7 @@ expr<nodep>: // IEEE: part of expression/constant_expression/primary
// // Conflicts with constraint_expression:"expr yP_MINUSGT constraint_set"
// // To duplicating expr for constraints, just allow the more general form
// // Later Ast processing must ignore constraint terms where inappropriate
//UNSUP ~l~expr yP_MINUSGT constraint_set { $<fl>$=$<fl>1; $$ = $1+$2+$3; }
//UNSUP ~l~expr yP_MINUSGT constraint_set { $<fl>$ = $<fl>1; $$ = $1 + $2 + $3; }
//UNSUP remove line below
| ~l~expr yP_MINUSGT ~r~expr { $$ = new AstLogIf($2, $1, $3); }
//
@ -4368,10 +4378,10 @@ fexpr<nodep>: // For use as first part of statement (disambiguates <=)
//UNSUP // // IEEE: '(' event_expression ')'
//UNSUP // // expr:'(' x ')' conflicts with event_expression:'(' event_expression ')'
//UNSUP // // so we use a special expression class
//UNSUP | '(' event_expression ')' { $<fl>$=$<fl>1; $$ = "(...)"; }
//UNSUP | '(' event_expression ')' { $<fl>$ = $<fl>1; $$ = "(...)"; }
//UNSUP // // IEEE: From normal expr: '(' expr ':' expr ':' expr ')'
//UNSUP // // But must avoid conflict
//UNSUP | '(' event_expression ':' expr ':' expr ')' { $<fl>$=$<fl>1; $$ = "(...)"; }
//UNSUP | '(' event_expression ':' expr ':' expr ')' { $<fl>$ = $<fl>1; $$ = "(...)"; }
//UNSUP ;
exprNoStr<nodep>: // expression with string removed
@ -4421,7 +4431,7 @@ fexprOkLvalue<nodep>: // exprOkLValue, For use as first part of statement (disa
//UNSUP ;
fexprLvalue<nodep>: // For use as first part of statement (disambiguates <=)
fexprOkLvalue { $<fl>$=$<fl>1; $$ = $1; }
fexprOkLvalue { $<fl>$ = $<fl>1; $$ = $1; }
;
exprScope<nodep>: // scope and variable for use to inside an expression
@ -4472,7 +4482,7 @@ exprStrText<nodep>:
cStrList<nodep>:
exprStrText { $$ = $1; }
| exprStrText ',' cStrList { $$ = $1;$1->addNext($3); }
| exprStrText ',' cStrList { $$ = $1; $1->addNext($3); }
;
cateList<nodep>:
@ -4488,7 +4498,7 @@ exprListE<nodep>:
exprList<nodep>:
expr { $$ = $1; }
| exprList ',' expr { $$ = $1;$1->addNext($3); }
| exprList ',' expr { $$ = $1; $1->addNext($3); }
;
exprDispList<nodep>: // exprList for within $display
@ -4501,7 +4511,7 @@ exprDispList<nodep>: // exprList for within $display
vrdList<nodep>:
idClassSel { $$ = $1; }
| vrdList ',' idClassSel { $$ = $1;$1->addNext($3); }
| vrdList ',' idClassSel { $$ = $1; $1->addNext($3); }
;
commaVRDListE<nodep>:
@ -4852,24 +4862,24 @@ junkToSemi:
// IDs
id<strp>:
yaID__ETC { $$ = $1; $<fl>$=$<fl>1; }
| idRandomize { $$ = $1; $<fl>$=$<fl>1; }
yaID__ETC { $$ = $1; $<fl>$ = $<fl>1; }
| idRandomize { $$ = $1; $<fl>$ = $<fl>1; }
;
idAny<strp>: // Any kind of identifier
yaID__ETC { $$ = $1; $<fl>$=$<fl>1; }
| yaID__aTYPE { $$ = $1; $<fl>$=$<fl>1; }
| idRandomize { $$ = $1; $<fl>$=$<fl>1; }
yaID__ETC { $$ = $1; $<fl>$ = $<fl>1; }
| yaID__aTYPE { $$ = $1; $<fl>$ = $<fl>1; }
| idRandomize { $$ = $1; $<fl>$ = $<fl>1; }
;
idType<strp>: // IEEE: class_identifier or other type identifier
// // Used where reference is needed
yaID__aTYPE { $$ = $1; $<fl>$=$<fl>1; }
yaID__aTYPE { $$ = $1; $<fl>$ = $<fl>1; }
;
idCC<strp>: // IEEE: class/package then ::
// lexer matches this: yaID_LEX [ '#' '(' ... ')' ] yP_COLONCOLON
yaID__CC { $$ = $1; $<fl>$=$<fl>1; }
yaID__CC { $$ = $1; $<fl>$ = $<fl>1; }
;
idRandomize<strp>: // Keyword as an identifier
@ -4878,8 +4888,10 @@ idRandomize<strp>: // Keyword as an identifier
idSVKwd<strp>: // Warn about non-forward compatible Verilog 2001 code
// // yBIT, yBYTE won't work here as causes conflicts
yDO { static string s = "do" ; $$ = &s; ERRSVKWD($1,*$$); $<fl>$=$<fl>1; }
| yFINAL { static string s = "final"; $$ = &s; ERRSVKWD($1,*$$); $<fl>$=$<fl>1; }
yDO
{ static string s = "do" ; $$ = &s; ERRSVKWD($1,*$$); $<fl>$ = $<fl>1; }
| yFINAL
{ static string s = "final"; $$ = &s; ERRSVKWD($1,*$$); $<fl>$ = $<fl>1; }
;
variable_lvalue<nodep>: // IEEE: variable_lvalue or net_lvalue
@ -4993,7 +5005,8 @@ str<strp>: // yaSTRING but with \{escapes} need decoded
;
strAsInt<nodep>:
yaSTRING { $$ = new AstConst($<fl>1, AstConst::VerilogStringLiteral(), GRAMMARP->deQuote($<fl>1, *$1));}
yaSTRING
{ $$ = new AstConst{$<fl>1, AstConst::VerilogStringLiteral(), GRAMMARP->deQuote($<fl>1, *$1)}; }
;
strAsIntIgnore<nodep>: // strAsInt, but never matches for when expr shouldn't parse strings
@ -5001,13 +5014,13 @@ strAsIntIgnore<nodep>: // strAsInt, but never matches for when expr shouldn't p
;
strAsText<nodep>:
yaSTRING { $$ = GRAMMARP->createTextQuoted($<fl>1,*$1);}
yaSTRING { $$ = GRAMMARP->createTextQuoted($<fl>1, *$1); }
;
endLabelE<strp>:
/* empty */ { $$ = nullptr; $<fl>$=nullptr; }
| ':' idAny { $$ = $2; $<fl>$=$<fl>2; }
| ':' yNEW__ETC { static string n = "new"; $$ = &n; $<fl>$=$<fl>2; }
/* empty */ { $$ = nullptr; $<fl>$ = nullptr; }
| ':' idAny { $$ = $2; $<fl>$ = $<fl>2; }
| ':' yNEW__ETC { static string n = "new"; $$ = &n; $<fl>$ = $<fl>2; }
;
//************************************************
@ -5075,7 +5088,7 @@ clocking_declaration<nodep>: // IEEE: clocking_declaration (INCOMPLETE)
//UNSUP ;
//UNSUPclocking_skewE: // IEEE: [clocking_skew]
//UNSUP /* empty */ { $$ = nullptr;}
//UNSUP /* empty */ { $$ = nullptr; }
//UNSUP | clocking_skew { $$ = $1; }
//UNSUP ;
@ -5480,21 +5493,21 @@ pexpr<nodep>: // IEEE: property_expr (The name pexpr is important as regexps j
//UNSUP // // As sequence_expr includes expression_or_dist, and boolean_abbrev includes sequence_abbrev:
//UNSUP // // '(' sequence_expr {',' sequence_match_item } ')' [ boolean_abbrev ]
//UNSUP // // "'(' sexpr ')' boolean_abbrev" matches "[sexpr:'(' expr ')'] boolean_abbrev" so we can simply drop it
//UNSUP | '(' ~p~sexpr ')' { $<fl>$=$<fl>1; $$=$1+$2+$3; }
//UNSUP | '(' ~p~sexpr ')' { $<fl>$ = $<fl>1; $$ = ...; }
//UNSUP | '(' ~p~sexpr ',' sequence_match_itemList ')' { }
//UNSUP //
//UNSUP // // AND/OR are between pexprs OR sexprs
//UNSUP | ~p~sexpr yAND ~p~sexpr { $<fl>$=$<fl>1; $$=$1+$2+$3; }
//UNSUP | ~p~sexpr yOR ~p~sexpr { $<fl>$=$<fl>1; $$=$1+$2+$3; }
//UNSUP | ~p~sexpr yAND ~p~sexpr { $<fl>$ = $<fl>1; $$ = ...; }
//UNSUP | ~p~sexpr yOR ~p~sexpr { $<fl>$ = $<fl>1; $$ = ...; }
//UNSUP // // Intersect always has an sexpr rhs
//UNSUP | ~p~sexpr yINTERSECT sexpr { $<fl>$=$<fl>1; $$=$1+$2+$3; }
//UNSUP | ~p~sexpr yINTERSECT sexpr { $<fl>$ = $<fl>1; $$ = ...; }
//UNSUP //
//UNSUP | yFIRST_MATCH '(' sexpr ')' { }
//UNSUP | yFIRST_MATCH '(' sexpr ',' sequence_match_itemList ')' { }
//UNSUP | ~p~sexpr/*sexpression_or_dist*/ yTHROUGHOUT sexpr { }
//UNSUP // // Below pexpr's are really sequence_expr, but avoid conflict
//UNSUP // // IEEE: sexpr yWITHIN sexpr
//UNSUP | ~p~sexpr yWITHIN sexpr { $<fl>$=$<fl>1; $$=$1+$2+$3; }
//UNSUP | ~p~sexpr yWITHIN sexpr { $<fl>$ = $<fl>1; $$ = ...; }
//UNSUP // // Note concurrent_assertion had duplicate rule for below
//UNSUP | clocking_event ~p~sexpr %prec prSEQ_CLOCKING { }
//UNSUP //
@ -6307,8 +6320,8 @@ dist_list<nodep>: // ==IEEE: dist_list
dist_item<nodep>: // ==IEEE: dist_item + dist_weight
value_range { $$ = $1; /* Same as := 1 */ }
| value_range yP_COLONEQ expr { $$ = $1; nullptr; /*UNSUP-no-UVM*/ }
| value_range yP_COLONDIV expr { $$ = $1; nullptr; /*UNSUP-no-UVM*/ }
| value_range yP_COLONEQ expr { $$ = $1; /*UNSUP-no-UVM*/ }
| value_range yP_COLONDIV expr { $$ = $1; /*UNSUP-no-UVM*/ }
;
//UNSUPextern_constraint_declaration: // ==IEEE: extern_constraint_declaration
@ -6350,11 +6363,11 @@ vltItem:
| vltOffFront yVLT_D_FILE yaSTRING yVLT_D_LINES yaINTNUM '-' yaINTNUM
{ V3Config::addIgnore($1, false, *$3, $5->toUInt(), $7->toUInt()+1); }
| vltOffFront yVLT_D_FILE yaSTRING yVLT_D_MATCH yaSTRING
{ if (($1==V3ErrorCode::I_COVERAGE) || ($1==V3ErrorCode::I_TRACING)) {
$<fl>1->v3error("Argument -match only supported for lint_off");
} else {
V3Config::addWaiver($1,*$3,*$5);
}}
{ if (($1 == V3ErrorCode::I_COVERAGE) || ($1 == V3ErrorCode::I_TRACING)) {
$<fl>1->v3error("Argument -match only supported for lint_off");
} else {
V3Config::addWaiver($1,*$3,*$5);
}}
| vltOnFront { V3Config::addIgnore($1, true, "*", 0, 0); }
| vltOnFront yVLT_D_FILE yaSTRING
{ V3Config::addIgnore($1, true, *$3, 0, 0); }
@ -6382,6 +6395,8 @@ vltItem:
{ V3Config::addCaseParallel(*$3, 0); }
| yVLT_PARALLEL_CASE yVLT_D_FILE yaSTRING yVLT_D_LINES yaINTNUM
{ V3Config::addCaseParallel(*$3, $5->toUInt()); }
| yVLT_PROFILE_DATA yVLT_D_MODEL yaSTRING yVLT_D_MTASK yaSTRING yVLT_D_COST yaINTNUM
{ V3Config::addProfileData($<fl>1, *$3, *$5, $7->toUQuad()); }
;
vltOffFront<errcodeen>:
@ -6416,7 +6431,7 @@ vltDModuleE<strp>:
;
vltDFTaskE<strp>:
/* empty */ { static string empty = ""; $$ = &empty; }
/* empty */ { static string empty; $$ = &empty; }
| yVLT_D_FUNCTION str { $$ = $2; }
| yVLT_D_TASK str { $$ = $2; }
;
@ -6427,7 +6442,7 @@ vltInlineFront<cbool>:
;
vltVarAttrVarE<strp>:
/* empty */ { static string empty = ""; $$ = &empty; }
/* empty */ { static string empty; $$ = &empty; }
| yVLT_D_VAR str { $$ = $2; }
;

View File

@ -41,8 +41,6 @@ CPPFLAGS += $(CPPFLAGS_ADD)
ifeq ($(CFG_WITH_LONGTESTS),yes)
ifeq ($(DRIVER_STD),newest)
CPPFLAGS += $(CFG_CXXFLAGS_STD_NEWEST)
else ifeq ($(DRIVER_STD),oldest)
CPPFLAGS += $(CFG_CXXFLAGS_STD_OLDEST)
endif
endif

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@ $Self->{clean_command} = 'rm -rf ../examples/*/build ../examples/*/obj*';
my @examples = sort(glob("../examples/*"));
for my $example (@examples) {
run(cmd=>["$ENV{MAKE} -C $example"]);
run(cmd => ["$ENV{MAKE} -C $example"]);
}
ok(1);

View File

@ -24,7 +24,7 @@ file_grep_any(\@files, qr/dly__t__DOT__v1/i);
file_grep_any(\@files, qr/dly__t__DOT__v2/i);
execute(
check_finished=>1,
check_finished => 1,
);
ok(1);

View File

@ -19,7 +19,7 @@ if ($Self->{vlt_all}) {
}
execute(
check_finished=>1,
check_finished => 1,
);
ok(1);

View File

@ -28,7 +28,7 @@ foreach my $file (
}
execute(
check_finished=>1,
check_finished => 1,
);
ok(1);

View File

@ -33,7 +33,7 @@ if ($Self->{nc}) {
}
run(logfile => "$Self->{obj_dir}/${name}__nccover.log",
tee => 0,
cmd => [($ENV{VERILATOR_ICCR}||'iccr'),
cmd => [($ENV{VERILATOR_ICCR} || 'iccr'),
"-test ${name} ${cf}"]);
}

View File

@ -16,7 +16,7 @@ top_filename("t/t_gen_alw.v");
init_benchmarksim();
# As an example, compile and simulate the top file with varying optimization level
my @l_opt = (1,2,3);
my @l_opt = (1, 2, 3);
foreach my $l_opt (@l_opt) {
compile(
@ -29,7 +29,7 @@ foreach my $l_opt (@l_opt) {
);
}
my $fh = IO::File->new("<".benchmarksim_filename()) or error("Benchmark data file not found");
my $fh = IO::File->new("<" . benchmarksim_filename()) or error("Benchmark data file not found");
my $lines = 0;
while (defined(my $line = $fh->getline)) {
if ($line =~ /^#/) { next; }
@ -37,16 +37,17 @@ while (defined(my $line = $fh->getline)) {
error("Expected header but found $line") if $line ne "evals, time[s]\n";
} else {
my @data = grep {$_ != ""} ($line =~ /(\d*\.?\d*)/g);
error("Expected 2 tokens on line ".$lines." but got ".scalar(@data)) if scalar(@data) != 2;
error("Expected 2 tokens on line " . $lines . " but got " . scalar(@data)) if scalar(@data) != 2;
my $cycles = $data[0];
my $time = $data[1];
error("Invalid data on line ".$lines) if $cycles <= 0.0 || $time <= 0.0;
error("Invalid data on line " . $lines) if $cycles <= 0.0 || $time <= 0.0;
}
$lines += 1;
}
my $n_lines_expected = scalar(@l_opt) + 1;
error("Expected ".$n_lines_expected." lines but found ".$lines) if int($lines) != int($n_lines_expected);
error("Expected " . $n_lines_expected . " lines but found " . $lines)
if int($lines) != int($n_lines_expected);
1;
ok(1);

View File

@ -0,0 +1,7 @@
%Warning-SELRANGE: t/t_bigmem_bad.v:14:19: Selection index out of range: (nodep->declElWidth() == 0) -1:0 outside 268435455:0
: ... In instance t_bigmem
14 | if (wen) mem[addr] <= data;
| ^
... For warning description see https://verilator.org/warn/SELRANGE?v=latest
... Use "/* verilator lint_off SELRANGE */" and lint_on around source to disable this message.
%Error: Exiting due to

19
test_regress/t/t_bigmem_bad.pl Executable file
View File

@ -0,0 +1,19 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2021 by filamoon. This program is free software; you can
# redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(linter => 1);
lint(
fails => 1,
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,16 @@
// This test shall generate a warning, but not an internal error.
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2021 by Zhanglei Wang.
// SPDX-License-Identifier: CC0-1.0
module t_bigmem(
input wire clk,
input wire [27:0] addr,
input wire [255:0] data,
input wire wen
);
reg [(1<<28)-1:0][255:0] mem;
always @(posedge clk) begin
if (wen) mem[addr] <= data;
end
endmodule

View File

@ -11,13 +11,13 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(vlt => 1);
if (!$Self->cfg_with_ccache) {
skip("Requires configuring with ccache");
skip("Requires configuring with ccache");
}
top_filename("t_a1_first_cc.v");
# This test requires rebuilding the object files to check the ccache log
foreach my $filename (glob ("$Self->{obj_dir}/*.o")) {
foreach my $filename (glob("$Self->{obj_dir}/*.o")) {
print "rm $filename\n" if $Self->{verbose};
unlink $filename;
}

View File

@ -20,7 +20,7 @@ execute(
if ($Self->{vlt_all}) {
file_grep($Self->{stats}, qr/Optimizations, Gate assign merged\s+(\d+)/i, 28);
};
}
ok(1);
1;

View File

@ -12,8 +12,8 @@ scenarios(vlt_all => 1);
# This test makes randomly named .cpp/.h files, which tend to collect, so remove them first
foreach my $filename (glob ("$Self->{obj_dir}/*_PS*.cpp"
." $Self->{obj_dir}/*_PS*.h"
." $Self->{obj_dir}/*.d" )) {
. " $Self->{obj_dir}/*_PS*.h"
. " $Self->{obj_dir}/*.d" )) {
print "rm $filename\n" if $Self->{verbose};
unlink $filename;
}

View File

@ -12,8 +12,8 @@ scenarios(vlt_all => 1);
# This test makes randomly named .cpp/.h files, which tend to collect, so remove them first
foreach my $filename (glob ("$Self->{obj_dir}/*_PS*.cpp"
." $Self->{obj_dir}/*_PS*.h"
." $Self->{obj_dir}/*.d" )) {
. " $Self->{obj_dir}/*_PS*.h"
. " $Self->{obj_dir}/*.d" )) {
print "rm $filename\n" if $Self->{verbose};
unlink $filename;
}

View File

@ -1,3 +1,4 @@
''{b:'h1, i:'h2a, carray4:'{'h11, 'h22, 'h33, 'h44} }'
''{b:'h1, i:'h2a, carray4:'{'h911, 'h922, 'h933, 'h944} }'
''{b:'h1, i:'h2a, carray4:'{'h11, 'h22, 'h33, 'h44} , name:"object_name"}'
''{b:'h1, i:'h2a, carray4:'{'h911, 'h922, 'h933, 'h944} , name:"object_name"}'
DEBUG: object_name (@0) message
*-* All Finished *-*

View File

@ -15,6 +15,10 @@ class Cls;
bit b;
int i;
bit [15:0] carray4 [4];
string name;
task debug();
$display("DEBUG: %s (@%0t) %s", this.name, $realtime, "message");
endtask
endclass
module t (/*AUTOARG*/);
@ -23,6 +27,7 @@ module t (/*AUTOARG*/);
c = new;
c.b = '1;
c.i = 42;
c.name = "object_name";
c.carray4[0] = 16'h11;
c.carray4[1] = 16'h22;
@ -33,6 +38,8 @@ module t (/*AUTOARG*/);
c.carray4 = '{16'h911, 16'h922, 16'h933, 16'h944};
$display("'%p'", c);
c.debug();
$write("*-* All Finished *-*\n");
$finish;
end

View File

@ -12,8 +12,8 @@ scenarios(vlt_all => 1);
# This test makes randomly named .cpp/.h files, which tend to collect, so remove them first
foreach my $filename (glob ("$Self->{obj_dir}/*_PS*.cpp"
." $Self->{obj_dir}/*_PS*.h"
." $Self->{obj_dir}/*.d" )) {
. " $Self->{obj_dir}/*_PS*.h"
. " $Self->{obj_dir}/*.d")) {
print "rm $filename\n" if $Self->{verbose};
unlink $filename;
}

View File

@ -26,10 +26,27 @@ class ClsArg;
endfunction
endclass
class Cls2Arg;
int imembera;
int imemberb;
function new(int i, int j);
imembera = i + 1;
imemberb = j + 2;
endfunction
function Cls2Arg clone();
Cls2Arg ret;
ret = new(imembera, imemberb);
return ret;
endfunction
endclass
module t (/*AUTOARG*/);
initial begin
ClsNoArg c1;
ClsArg c2;
ClsArg c2;
Cls2Arg c3;
Cls2Arg c4;
c1 = new;
if (c1.imembera != 5) $stop;
@ -42,6 +59,14 @@ module t (/*AUTOARG*/);
if (c2.imembera != 6) $stop;
if (c2.geta() != 6) $stop;
c3 = new(4, 5);
if (c3.imembera != 5) $stop;
if (c3.imemberb != 7) $stop;
c4 = c3.clone();
if (c4.imembera != 6) $stop;
if (c4.imemberb != 9) $stop;
$write("*-* All Finished *-*\n");
$finish;
end

View File

@ -1,12 +1,13 @@
%Error: t/t_class_new_bad.v:31:16: Too many arguments in function call to FUNC 'new'
: ... In instance t
31 | c1 = new(3);
| ^
%Error: t/t_class_new_bad.v:32:16: Too many arguments in function call to FUNC 'new'
: ... In instance t
32 | c2 = new(3);
| ^
%Error: t/t_class_new_bad.v:33:12: Missing argument on non-defaulted argument 'i' in function call to FUNC 'new'
: ... In instance t
33 | c3 = new();
| ^~~
%Error: Internal Error: t/t_class_new_bad.v:33:12: ../V3Broken.cpp:#: Width != WidthMin
33 | c3 = new();
| ^~~
%Error: Exiting due to

View File

@ -12,8 +12,8 @@ scenarios(vlt_all => 1);
# This test makes randomly named .cpp/.h files, which tend to collect, so remove them first
foreach my $filename (glob ("$Self->{obj_dir}/*_PS*.cpp"
." $Self->{obj_dir}/*_PS*.h"
." $Self->{obj_dir}/*.d" )) {
. " $Self->{obj_dir}/*_PS*.h"
. " $Self->{obj_dir}/*.d")) {
print "rm $filename\n" if $Self->{verbose};
unlink $filename;
}

View File

@ -12,8 +12,8 @@ scenarios(vlt_all => 1);
# This test makes randomly named .cpp/.h files, which tend to collect, so remove them first
foreach my $filename (glob ("$Self->{obj_dir}/*_PS*.cpp"
." $Self->{obj_dir}/*_PS*.h"
." $Self->{obj_dir}/*.d" )) {
. " $Self->{obj_dir}/*_PS*.h"
. " $Self->{obj_dir}/*.d")) {
print "rm $filename\n" if $Self->{verbose};
unlink $filename;
}

View File

@ -13,7 +13,7 @@ scenarios(simulator => 1);
compile(
make_top_shell => 0,
make_main => 0,
verilator_flags2 => ["--exe","$Self->{t_dir}/$Self->{name}.cpp"],
verilator_flags2 => ["--exe", "$Self->{t_dir}/$Self->{name}.cpp"],
vcs_flags2 => ['-assert'],
);

View File

@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(simulator => 1);
compile(
verilator_flags2=>["-no-order-clock-delay"],
verilator_flags2 => ["-no-order-clock-delay"],
);
execute(

View File

@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(simulator => 1);
compile(
verilator_flags2=>["-Wno-UNOPTTHREADS", "--stats"],
verilator_flags2 => ["-Wno-UNOPTTHREADS", "--stats"],
);
execute(

View File

@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(simulator => 1);
compile(
verilator_flags2=>["-Wno-UNOPTTHREADS", "--stats", "--coverage", "--trace"],
verilator_flags2 => ["-Wno-UNOPTTHREADS", "--stats", "--coverage", "--trace"],
);
execute(

View File

@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(simulator => 1);
compile(
verilator_flags2=>["-Wno-UNOPTTHREADS", "--stats"],
verilator_flags2 => ["-Wno-UNOPTTHREADS", "--stats"],
);
execute(

View File

@ -12,7 +12,7 @@ scenarios(simulator => 1);
compile(
v_flags2 => ["t/$Self->{name}.cpp"],
verilator_flags2=>["-Wno-UNOPTTHREADS", "--stats"],
verilator_flags2 => ["-Wno-UNOPTTHREADS", "--stats"],
);
execute(

View File

@ -13,7 +13,7 @@ scenarios(vlt => 1);
compile(
v_flags2 => ["--coverage t/t_cover_lib_c.cpp"],
verilator_flags2 => ["--exe -Wall -Wno-DECLFILENAME"],
make_flags => 'CPPFLAGS_ADD=-DTEST_OBJ_DIR="'.$Self->{obj_dir}.'"',
make_flags => 'CPPFLAGS_ADD=-DTEST_OBJ_DIR="' . $Self->{obj_dir} . '"',
make_top_shell => 0,
make_main => 0,
);

View File

@ -15,7 +15,7 @@ top_filename("t/t_cover_lib.v");
compile(
v_flags2 => ["--coverage t/t_cover_lib_c.cpp"],
verilator_flags2 => ["--exe -Wall -Wno-DECLFILENAME"],
make_flags => 'CPPFLAGS_ADD=-DTEST_OBJ_DIR="'.$Self->{obj_dir}.'"',
make_flags => 'CPPFLAGS_ADD=-DTEST_OBJ_DIR="' . $Self->{obj_dir} . '"',
make_top_shell => 0,
make_main => 0,
);

View File

@ -24,7 +24,7 @@ execute(
# Check that the hierarchy doesn't include __PVT__
# Otherwise our coverage reports would look really ugly
if ($Self->{vlt_all}) {
file_grep($Self->{coverage_filename}, qr/(top\.t\.sub.*.cyc_eq_5)/)
file_grep($Self->{coverage_filename}, qr/(top\.t\.sub.*.cyc_eq_5)/);
}
ok(1);

View File

@ -21,6 +21,5 @@ if ($Self->{vlt_all}) {
file_grep($Self->{stats}, qr/Optimizations, Gate sigs deduped\s+(\d+)/i, 4);
}
ok(1);
1;

View File

@ -0,0 +1,11 @@
Verilator Tree Dump (format 0x3900) from <e0> to <e663>
NETLIST 0x555556bb6000 <e1#> {a0aa} $root [1ps/1ps]
1: MODULE 0x555556bc0120 <e661#> {d19ai} t L2 [1ps]
1:2: PORT 0x555556bc60d0 <e8#> {d21ae} clk
1:2: VAR 0x555556bbe180 <e572#> {d23ak} @dt=0@ clk INPUT PORT
1:2:1: BASICDTYPE 0x555556bc61a0 <e12#> {d23ak} @dt=this@(nw0) LOGIC_IMPLICIT kwd=LOGIC_IMPLICIT
3: TYPETABLE 0x555556bbc000 <e2#> {a0aa}
logic -> BASICDTYPE 0x555556c71a00 <e426#> {d55ap} @dt=this@(G/nw1) logic [GENERIC] kwd=logic
3: CONSTPOOL 0x555556bbe000 <e6#> {a0aa}
3:1: MODULE 0x555556bc0000 <e4#> {a0aa} @CONST-POOL@ L0 [NONE]
3:1:2: SCOPE 0x555556bb60f0 <e5#> {a0aa} @CONST-POOL@ [abovep=0] [cellp=0] [modp=0x555556bc0000]

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