Merge from master for release.
This commit is contained in:
commit
0185ee5df3
22
Changes
22
Changes
|
|
@ -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
|
||||
==========================
|
||||
|
||||
|
|
|
|||
28
Makefile.in
28
Makefile.in
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
"<>" => \¶meter,
|
||||
"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
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
"<>" => \¶meter,
|
||||
)) {
|
||||
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:
|
||||
|
|
|
|||
20
configure.ac
20
configure.ac
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
.. comment: generated by t_lint_didnotconverge_bad
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
|
||||
always_comb b = ~a;
|
||||
always_comb a = b;
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
.. comment: generated by t_lint_stmtdly_bad
|
||||
.. code-block:: sv
|
||||
:emphasize-lines: 1
|
||||
|
||||
#100 $finish; //<--- Warning
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>"
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
@ -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 |
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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}`.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
""""""""""""""""""""""
|
||||
|
|
|
|||
|
|
@ -676,7 +676,6 @@ prev
|
|||
printf
|
||||
printtimescale
|
||||
profcfunc
|
||||
profcfuncs
|
||||
prototyptes
|
||||
ps
|
||||
pthread
|
||||
|
|
|
|||
|
|
@ -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*/)) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
56
src/V3Ast.h
56
src/V3Ast.h
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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(); }
|
||||
|
||||
|
|
|
|||
|
|
@ -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(");
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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, [=]() {});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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--) {
|
||||
|
|
|
|||
|
|
@ -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()); }
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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()) << "]";
|
||||
|
|
|
|||
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -590,6 +590,7 @@ static void verilate(const string& argString) {
|
|||
V3TSP::selfTest();
|
||||
V3ScoreboardBase::selfTest();
|
||||
V3Partition::selfTest();
|
||||
V3Partition::selfTestNormalizeCosts();
|
||||
V3Broken::selfTest();
|
||||
}
|
||||
|
||||
|
|
|
|||
49
src/astgen
49
src/astgen
|
|
@ -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")
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
335
src/verilog.y
335
src/verilog.y
|
|
@ -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="*"; $$=☆ }
|
||||
idAny/*package_identifier*/ { $<fl>$ = $<fl>1; $$ = $1; }
|
||||
| '*' { $<fl>$ = $<fl>1; static string 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 */ { static string empty; $$ = ∅ }
|
||||
| yVLT_D_FUNCTION str { $$ = $2; }
|
||||
| yVLT_D_TASK str { $$ = $2; }
|
||||
;
|
||||
|
|
@ -6427,7 +6442,7 @@ vltInlineFront<cbool>:
|
|||
;
|
||||
|
||||
vltVarAttrVarE<strp>:
|
||||
/* empty */ { static string empty = ""; $$ = ∅ }
|
||||
/* empty */ { static string empty; $$ = ∅ }
|
||||
| yVLT_D_VAR str { $$ = $2; }
|
||||
;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ if ($Self->{vlt_all}) {
|
|||
}
|
||||
|
||||
execute(
|
||||
check_finished=>1,
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ foreach my $file (
|
|||
}
|
||||
|
||||
execute(
|
||||
check_finished=>1,
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
|
|
|
|||
|
|
@ -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}"]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 *-*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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'],
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,5 @@ if ($Self->{vlt_all}) {
|
|||
file_grep($Self->{stats}, qr/Optimizations, Gate sigs deduped\s+(\d+)/i, 4);
|
||||
}
|
||||
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue