Convert verilator_gantt to python
This commit is contained in:
parent
c678e7ec3e
commit
4b274a8d4d
|
|
@ -353,6 +353,7 @@ clang-format:
|
||||||
PY_PROGRAMS = \
|
PY_PROGRAMS = \
|
||||||
bin/verilator_ccache_report \
|
bin/verilator_ccache_report \
|
||||||
bin/verilator_difftree \
|
bin/verilator_difftree \
|
||||||
|
bin/verilator_gantt \
|
||||||
bin/verilator_profcfunc \
|
bin/verilator_profcfunc \
|
||||||
examples/xml_py/vl_file_copy \
|
examples/xml_py/vl_file_copy \
|
||||||
examples/xml_py/vl_hier_graph \
|
examples/xml_py/vl_hier_graph \
|
||||||
|
|
|
||||||
|
|
@ -1,163 +1,135 @@
|
||||||
#!/usr/bin/env perl
|
#!/usr/bin/env python3
|
||||||
# See copyright, etc in below POD section.
|
# pylint: disable=C0103,C0114,C0116,C0301,R0914,R0912,R0915,W0511,eval-used
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
use warnings;
|
import argparse
|
||||||
use strict;
|
import collections
|
||||||
use Getopt::Long;
|
import math
|
||||||
use IO::File;
|
import re
|
||||||
use Pod::Usage;
|
import statistics
|
||||||
use vars qw($Debug);
|
# from pprint import pprint
|
||||||
|
|
||||||
$Debug = 0;
|
Threads = collections.defaultdict(lambda: {})
|
||||||
my $Opt_File;
|
Mtasks = collections.defaultdict(lambda: {})
|
||||||
my $Opt_Time_Per_Char = 0; # rdtsc ticks per char in gantt chart, 0=auto
|
Global = {
|
||||||
my $opt_vcd = "profile_threads.vcd";
|
'args': {},
|
||||||
|
'cpuinfo': collections.defaultdict(lambda: {}),
|
||||||
our %Threads;
|
'rdtsc_cycle_time': 0,
|
||||||
our %Mtasks;
|
'stats': {}
|
||||||
our %Global;
|
|
||||||
|
|
||||||
autoflush STDOUT 1;
|
|
||||||
autoflush STDERR 1;
|
|
||||||
Getopt::Long::config("no_auto_abbrev");
|
|
||||||
if (! GetOptions(
|
|
||||||
"help" => \&usage,
|
|
||||||
"scale=i" => \$Opt_Time_Per_Char,
|
|
||||||
"debug" => sub { $Debug = 1; },
|
|
||||||
"vcd=s" => \$opt_vcd,
|
|
||||||
"no-vcd!" => sub { $opt_vcd = undef; },
|
|
||||||
"<>" => \¶meter,
|
|
||||||
)) {
|
|
||||||
die "%Error: Bad usage, try 'verilator_gantt --help'\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$Opt_File = "profile_threads.dat" if !defined $Opt_File;
|
######################################################################
|
||||||
|
|
||||||
process($Opt_File);
|
|
||||||
write_vcd($opt_vcd) if defined $opt_vcd;
|
|
||||||
exit(0);
|
|
||||||
|
|
||||||
#######################################################################
|
def process(filename):
|
||||||
|
read_data(filename)
|
||||||
|
report()
|
||||||
|
|
||||||
sub usage {
|
|
||||||
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
|
|
||||||
exit(1); # Unreachable
|
|
||||||
}
|
|
||||||
|
|
||||||
sub parameter {
|
def read_data(filename):
|
||||||
my $param = shift;
|
with open(filename) as fh:
|
||||||
if (!defined $Opt_File) {
|
re_prof = re.compile(
|
||||||
$Opt_File = $param;
|
r'^VLPROF mtask\s(\d+)\sstart\s(\d+)\send\s(\d+)\selapsed\s(\d+)\spredict_time\s(\d+)\scpu\s(\d+)\son thread (\d+)'
|
||||||
} else {
|
)
|
||||||
die "%Error: Unknown parameter: $param\n";
|
re_arg1 = re.compile(r'VLPROF arg\s+(\S+)\+([0-9.])\s*')
|
||||||
}
|
re_arg2 = re.compile(r'VLPROF arg\s+(\S+)\s+([0-9.])\s*$')
|
||||||
}
|
re_stat = re.compile(r'VLPROF stat\s+(\S+)\s+([0-9.]+)')
|
||||||
|
re_time = re.compile(r'rdtsc time = (\d+) ticks')
|
||||||
|
re_proc_cpu = re.compile(r'VLPROFPROC processor\s*:\s*(\d+)\s*$')
|
||||||
|
re_proc_dat = re.compile(r'VLPROFPROC ([a-z_ ]+)\s*:\s*(.*)$')
|
||||||
|
cpu = None
|
||||||
|
|
||||||
#######################################################################
|
for line in fh:
|
||||||
|
if re_prof.match(line):
|
||||||
|
match = re_prof.match(line)
|
||||||
|
mtask = int(match.group(1))
|
||||||
|
start = int(match.group(2))
|
||||||
|
end = int(match.group(3))
|
||||||
|
elapsed_time = int(match.group(4))
|
||||||
|
predict_time = int(match.group(5))
|
||||||
|
cpu = int(match.group(6))
|
||||||
|
thread = int(match.group(7))
|
||||||
|
if start not in Threads[thread]:
|
||||||
|
Threads[thread][start] = {}
|
||||||
|
Threads[thread][start]['mtask'] = mtask
|
||||||
|
Threads[thread][start]['end'] = end
|
||||||
|
Threads[thread][start]['cpu'] = cpu
|
||||||
|
|
||||||
sub process {
|
if 'elapsed' not in Mtasks[mtask]:
|
||||||
my $filename = shift;
|
Mtasks[mtask] = {'end': 0, 'elapsed': 0}
|
||||||
|
Mtasks[mtask]['elapsed'] += elapsed_time
|
||||||
|
Mtasks[mtask]['predict'] = predict_time
|
||||||
|
Mtasks[mtask]['end'] = max(Mtasks[mtask]['end'], end)
|
||||||
|
elif re.match(r'^VLPROFTHREAD', line):
|
||||||
|
None # pylint: disable=pointless-statement
|
||||||
|
elif re_arg1.match(line):
|
||||||
|
match = re_arg1.match(line)
|
||||||
|
Global['args'][match.group(1)] = match.group(2)
|
||||||
|
elif re_arg2.match(line):
|
||||||
|
match = re_arg2.match(line)
|
||||||
|
Global['args'][match.group(1)] = match.group(2)
|
||||||
|
elif re_stat.match(line):
|
||||||
|
match = re_stat.match(line)
|
||||||
|
Global['stats'][match.group(1)] = match.group(2)
|
||||||
|
elif re_proc_cpu.match(line):
|
||||||
|
match = re_proc_cpu.match(line)
|
||||||
|
cpu = int(match.group(1))
|
||||||
|
elif cpu and re_proc_dat.match(line):
|
||||||
|
match = re_proc_dat.match(line)
|
||||||
|
term = match.group(1)
|
||||||
|
value = match.group(2)
|
||||||
|
term = re.sub(r'\s+$', '', term)
|
||||||
|
term = re.sub(r'\s+', '_', term)
|
||||||
|
value = re.sub(r'\s+$', '', value)
|
||||||
|
Global['cpuinfo'][cpu][term] = value
|
||||||
|
elif re.match(r'^#', line):
|
||||||
|
None # pylint: disable=pointless-statement
|
||||||
|
elif Args.debug:
|
||||||
|
print("-Unk: %s" % line)
|
||||||
|
# TODO -- this is parsing text printed by a client.
|
||||||
|
# Really, verilator proper should generate this
|
||||||
|
# if it's useful...
|
||||||
|
if re_time.match(line):
|
||||||
|
Global['rdtsc_cycle_time'] = re_time.group(1)
|
||||||
|
|
||||||
read_data($filename);
|
|
||||||
report();
|
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
def re_match_result(regexp, line, result_to):
|
||||||
|
result_to = re.match(regexp, line)
|
||||||
|
return result_to
|
||||||
|
|
||||||
sub read_data {
|
|
||||||
my $filename = shift;
|
|
||||||
|
|
||||||
%Global = (rdtsc_cycle_time => 0);
|
######################################################################
|
||||||
|
|
||||||
my $fh = IO::File->new("<$filename") or die "%Error: $! $filename,";
|
|
||||||
|
|
||||||
my $cpu;
|
def report():
|
||||||
while (my $line = $fh->getline) {
|
print("Verilator Gantt report")
|
||||||
if ($line =~ m/VLPROF mtask\s(\d+)\sstart\s(\d+)\send\s(\d+)\selapsed\s(\d+)\spredict_time\s(\d+)\scpu\s(\d+)\son thread (\d+)/) {
|
|
||||||
my $mtask = $1;
|
|
||||||
my $start = $2;
|
|
||||||
my $end = $3;
|
|
||||||
my $elapsed_time = $4;
|
|
||||||
my $predict_time = $5;
|
|
||||||
my $cpu = $6;
|
|
||||||
my $thread = $7;
|
|
||||||
$Threads{$thread}{$start}{mtask} = $mtask;
|
|
||||||
$Threads{$thread}{$start}{end} = $end;
|
|
||||||
$Threads{$thread}{$start}{cpu} = $cpu;
|
|
||||||
|
|
||||||
if (!exists $Mtasks{$mtask}{elapsed}) {
|
print("\nArgument settings:")
|
||||||
$Mtasks{$mtask}{elapsed} = 0;
|
for arg in sorted(Global['args'].keys()):
|
||||||
}
|
plus = "+" if re.match(r'^\+', arg) else " "
|
||||||
$Mtasks{$mtask}{elapsed} += $elapsed_time;
|
print(" %s%s%s" % (arg, plus, Global['args'][arg]))
|
||||||
$Mtasks{$mtask}{predict} = $predict_time;
|
|
||||||
$Mtasks{$mtask}{end} = max($Mtasks{$mtask}{end}, $end);
|
|
||||||
}
|
|
||||||
elsif ($line =~ /^VLPROFTHREAD/) {}
|
|
||||||
elsif ($line =~ m/VLPROF arg\s+(\S+)\+([0-9.])\s*$/
|
|
||||||
|| $line =~ m/VLPROF arg\s+(\S+)\s+([0-9.])\s*$/) {
|
|
||||||
$Global{args}{$1} = $2;
|
|
||||||
}
|
|
||||||
elsif ($line =~ m/VLPROF stat\s+(\S+)\s+([0-9.]+)/) {
|
|
||||||
$Global{stats}{$1} = $2;
|
|
||||||
}
|
|
||||||
elsif ($line =~ m/^VLPROFPROC processor\s*:\s*(\d+)\s*$/) {
|
|
||||||
$cpu = $1;
|
|
||||||
}
|
|
||||||
elsif (defined $cpu && $line =~ m/^VLPROFPROC ([a-z_ ]+)\s*:\s*(.*)$/) {
|
|
||||||
my ($term, $value) = ($1, $2);
|
|
||||||
$term =~ s/\s+$//;
|
|
||||||
$term =~ s/\s+/_/;
|
|
||||||
$value =~ s/\s+$//;
|
|
||||||
$Global{cpuinfo}{$cpu}{$term} = $value;
|
|
||||||
}
|
|
||||||
elsif ($line =~ /^#/) {}
|
|
||||||
elsif ($Debug) {
|
|
||||||
chomp $line;
|
|
||||||
print "Unk: $line\n";
|
|
||||||
}
|
|
||||||
# TODO -- this is parsing text printed by a client.
|
|
||||||
# Really, verilator proper should generate this
|
|
||||||
# if it's useful...
|
|
||||||
if ($line =~ m/rdtsc time = (\d+) ticks/) {
|
|
||||||
$Global{rdtsc_cycle_time} = $1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
nthreads = len(Threads)
|
||||||
|
Global['cpus'] = {}
|
||||||
sub report {
|
for thread in Threads:
|
||||||
print "Verilator Gantt report\n";
|
|
||||||
|
|
||||||
print "\nArgument settings:\n";
|
|
||||||
foreach my $arg (sort keys %{$Global{args}}) {
|
|
||||||
my $plus = ($arg =~ /^\+/) ? "+" : " ";
|
|
||||||
printf " %s%s%d\n", $arg, $plus, $Global{args}{$arg};
|
|
||||||
}
|
|
||||||
|
|
||||||
my $nthreads = scalar keys %Threads;
|
|
||||||
$Global{cpus} = {};
|
|
||||||
foreach my $thread (keys %Threads) {
|
|
||||||
# Make potentially multiple characters per column
|
# Make potentially multiple characters per column
|
||||||
foreach my $start (keys %{$Threads{$thread}}) {
|
for start in Threads[thread]:
|
||||||
my $cpu = $Threads{$thread}{$start}{cpu};
|
cpu = Threads[thread][start]['cpu']
|
||||||
my $elapsed = $Threads{$thread}{$start}{end} - $start;
|
elapsed = Threads[thread][start]['end'] - start
|
||||||
$Global{cpus}{$cpu}{cpu_time} += $elapsed;
|
if cpu not in Global['cpus']:
|
||||||
}
|
Global['cpus'][cpu] = {'cpu_time': 0}
|
||||||
}
|
Global['cpus'][cpu]['cpu_time'] += elapsed
|
||||||
|
|
||||||
my $mt_mtask_time = 0;
|
mt_mtask_time = 0
|
||||||
my $long_mtask_time = 0;
|
long_mtask_time = 0
|
||||||
my $last_end = 0;
|
last_end = 0
|
||||||
foreach my $mtask (keys %Mtasks) {
|
for mtask in Mtasks:
|
||||||
$mt_mtask_time += $Mtasks{$mtask}{elapsed};
|
mt_mtask_time += Mtasks[mtask]['elapsed']
|
||||||
$last_end = max($last_end, $Mtasks{$mtask}{end});
|
last_end = max(last_end, Mtasks[mtask]['end'])
|
||||||
$long_mtask_time = max($long_mtask_time, $Mtasks{$mtask}{elapsed});
|
long_mtask_time = max(long_mtask_time, Mtasks[mtask]['elapsed'])
|
||||||
}
|
Global['last_end'] = last_end
|
||||||
$Global{last_end} = $last_end;
|
|
||||||
|
|
||||||
report_graph();
|
report_graph()
|
||||||
|
|
||||||
# If we know cycle time in the same (rdtsc) units,
|
# If we know cycle time in the same (rdtsc) units,
|
||||||
# this will give us an actual utilization number,
|
# this will give us an actual utilization number,
|
||||||
|
|
@ -167,402 +139,356 @@ sub report {
|
||||||
# serial mode, to estimate the overhead of data sharing,
|
# serial mode, to estimate the overhead of data sharing,
|
||||||
# which will show up in the total elapsed time. (Overhead
|
# which will show up in the total elapsed time. (Overhead
|
||||||
# of synchronization and scheduling should not.)
|
# of synchronization and scheduling should not.)
|
||||||
print "\nAnalysis:\n";
|
print("\nAnalysis:")
|
||||||
printf " Total threads = %d\n", $nthreads;
|
print(" Total threads = %d" % nthreads)
|
||||||
printf " Total mtasks = %d\n", scalar(keys %Mtasks);
|
print(" Total mtasks = %d" % len(Mtasks))
|
||||||
my $ncpus = scalar(keys %{$Global{cpus}});
|
ncpus = len(Global['cpus'])
|
||||||
printf " Total cpus used = %d\n", $ncpus;
|
print(" Total cpus used = %d" % ncpus)
|
||||||
printf " Total yields = %d\n", $Global{stats}{yields};
|
print(" Total yields = %d" % int(Global['stats']['yields']))
|
||||||
printf " Total eval time = %d rdtsc ticks\n", $Global{last_end};
|
print(" Total eval time = %d rdtsc ticks" % Global['last_end'])
|
||||||
printf " Longest mtask time = %d rdtsc ticks\n", $long_mtask_time;
|
print(" Longest mtask time = %d rdtsc ticks" % long_mtask_time)
|
||||||
printf " All-thread mtask time = %d rdtsc ticks\n", $mt_mtask_time;
|
print(" All-thread mtask time = %d rdtsc ticks" % mt_mtask_time)
|
||||||
my $long_efficiency = $long_mtask_time/($Global{last_end} || 1);
|
long_efficiency = long_mtask_time / (Global.get('last_end', 1))
|
||||||
printf " Longest-thread efficiency = %0.1f%%\n", $long_efficiency*100;
|
print(" Longest-thread efficiency = %0.1f%%" % (long_efficiency * 100.0))
|
||||||
my $mt_efficiency = $mt_mtask_time/($Global{last_end}*$nthreads || 1);
|
mt_efficiency = mt_mtask_time / (Global.get('last_end', 1) * nthreads or 1)
|
||||||
printf " All-thread efficiency = %0.1f%%\n", $mt_efficiency*100;
|
print(" All-thread efficiency = %0.1f%%" % (mt_efficiency * 100.0))
|
||||||
printf " All-thread speedup = %0.1f\n", $mt_efficiency*$nthreads;
|
print(" All-thread speedup = %0.1f" % (mt_efficiency * nthreads))
|
||||||
if ($Global{rdtsc_cycle_time} > 0) {
|
if Global['rdtsc_cycle_time'] > 0:
|
||||||
my $ut = $mt_mtask_time / $Global{rdtsc_cycle_time};
|
ut = mt_mtask_time / Global['rdtsc_cycle_time']
|
||||||
print "tot_mtask_cpu=$mt_mtask_time cyc=$Global{rdtsc_cycle_time} ut=$ut\n";
|
print("tot_mtask_cpu=" + mt_mtask_time + " cyc=" +
|
||||||
}
|
Global['rdtsc_cycle_time'] + " ut=" + ut)
|
||||||
|
|
||||||
my @p2e_ratios;
|
p2e_ratios = []
|
||||||
my $min_p2e = 1000000;
|
min_p2e = 1000000
|
||||||
my $min_mtask;
|
min_mtask = None
|
||||||
my $max_p2e = -1000000;
|
max_p2e = -1000000
|
||||||
my $max_mtask;
|
max_mtask = None
|
||||||
foreach my $mtask (sort keys %Mtasks) {
|
|
||||||
if ($Mtasks{$mtask}{elapsed} > 0) {
|
|
||||||
if ($Mtasks{$mtask}{predict} == 0) {
|
|
||||||
$Mtasks{$mtask}{predict} = 1; # don't log(0) below
|
|
||||||
}
|
|
||||||
my $p2e_ratio = log( $Mtasks{$mtask}{predict} / $Mtasks{$mtask}{elapsed} );
|
|
||||||
#print "log(p2e $mtask) = $p2e_ratio (predict $Mtasks{$mtask}{predict}, elapsed $Mtasks{$mtask}{elapsed})\n";
|
|
||||||
push @p2e_ratios, $p2e_ratio;
|
|
||||||
|
|
||||||
if ($p2e_ratio > $max_p2e) {
|
for mtask in sorted(Mtasks.keys()):
|
||||||
$max_p2e = $p2e_ratio;
|
if Mtasks[mtask]['elapsed'] > 0:
|
||||||
$max_mtask = $mtask;
|
if Mtasks[mtask]['predict'] == 0:
|
||||||
}
|
Mtasks[mtask]['predict'] = 1 # don't log(0) below
|
||||||
if ($p2e_ratio < $min_p2e) {
|
p2e_ratio = math.log(Mtasks[mtask]['predict'] /
|
||||||
$min_p2e = $p2e_ratio;
|
Mtasks[mtask]['elapsed'])
|
||||||
$min_mtask = $mtask;
|
p2e_ratios.append(p2e_ratio)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print "\nStatistics:\n";
|
if p2e_ratio > max_p2e:
|
||||||
printf " min log(p2e) = %0.3f", $min_p2e;
|
max_p2e = p2e_ratio
|
||||||
print " from mtask $min_mtask (predict $Mtasks{$min_mtask}{predict},";
|
max_mtask = mtask
|
||||||
print " elapsed $Mtasks{$min_mtask}{elapsed})\n";
|
if p2e_ratio < min_p2e:
|
||||||
printf " max log(p2e) = %0.3f", $max_p2e;
|
min_p2e = p2e_ratio
|
||||||
print " from mtask $max_mtask (predict $Mtasks{$max_mtask}{predict},";
|
min_mtask = mtask
|
||||||
print " elapsed $Mtasks{$max_mtask}{elapsed})\n";
|
|
||||||
|
|
||||||
my $stddev = stddev(\@p2e_ratios);
|
print("\nStatistics:")
|
||||||
my $mean = mean(\@p2e_ratios);
|
print(" min log(p2e) = %0.3f" % min_p2e, end="")
|
||||||
printf " mean = %0.3f\n", $mean;
|
print(" from mtask %d (predict %d," %
|
||||||
printf " stddev = %0.3f\n", $stddev;
|
(min_mtask, Mtasks[min_mtask]['predict']),
|
||||||
printf " e ^ stddev = %0.3f\n", exp($stddev);
|
end="")
|
||||||
|
print(" elapsed %d)" % Mtasks[min_mtask]['elapsed'])
|
||||||
|
print(" max log(p2e) = %0.3f" % max_p2e, end="")
|
||||||
|
print(" from mtask %d (predict %d," %
|
||||||
|
(max_mtask, Mtasks[max_mtask]['predict']),
|
||||||
|
end="")
|
||||||
|
print(" elapsed %d)" % Mtasks[max_mtask]['elapsed'])
|
||||||
|
|
||||||
report_cpus();
|
stddev = statistics.pstdev(p2e_ratios)
|
||||||
|
mean = statistics.mean(p2e_ratios)
|
||||||
|
print(" mean = %0.3f" % mean)
|
||||||
|
print(" stddev = %0.3f" % stddev)
|
||||||
|
print(" e ^ stddev = %0.3f" % math.exp(stddev))
|
||||||
|
|
||||||
if ($nthreads > $ncpus) {
|
report_cpus()
|
||||||
print "\n";
|
|
||||||
print "%Warning: There were fewer CPUs ($ncpus) then threads ($nthreads).\n";
|
|
||||||
print " : See docs on use of numactl.\n";
|
|
||||||
} else {
|
|
||||||
if ($Global{cpu_socket_cores_warning}) {
|
|
||||||
print "\n";
|
|
||||||
print "%Warning: Multiple threads scheduled on same hyperthreaded core.\n";
|
|
||||||
print " : See docs on use of numactl.\n";
|
|
||||||
}
|
|
||||||
if ($Global{cpu_sockets_warning}) {
|
|
||||||
print "\n";
|
|
||||||
print "%Warning: Threads scheduled on multiple sockets.\n";
|
|
||||||
print " : See docs on use of numactl.\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
print "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
sub report_cpus {
|
if nthreads > ncpus:
|
||||||
print "\nCPUs:\n";
|
print()
|
||||||
# Test - show all cores
|
print("%%Warning: There were fewer CPUs (%d) then threads (%d)." %
|
||||||
# for (my $i=0; $i<73; ++$i) { $Global{cpus}{$i} ||= {cpu_time => 0}; }
|
(ncpus, nthreads))
|
||||||
|
print(" : See docs on use of numactl.")
|
||||||
|
else:
|
||||||
|
if 'cpu_socket_cores_warning' in Global:
|
||||||
|
print()
|
||||||
|
print(
|
||||||
|
"%Warning: Multiple threads scheduled on same hyperthreaded core."
|
||||||
|
)
|
||||||
|
print(" : See docs on use of numactl.")
|
||||||
|
if 'cpu_sockets_warning' in Global:
|
||||||
|
print()
|
||||||
|
print("%Warning: Threads scheduled on multiple sockets.")
|
||||||
|
print(" : See docs on use of numactl.")
|
||||||
|
print()
|
||||||
|
|
||||||
$Global{cpu_sockets} ||= {};
|
|
||||||
$Global{cpu_socket_cores} ||= {};
|
|
||||||
|
|
||||||
foreach my $cpu (sort {$a <=> $b} keys %{$Global{cpus}}) {
|
def report_cpus():
|
||||||
printf " cpu %d: ", $cpu;
|
print("\nCPUs:")
|
||||||
printf "cpu_time=%d", $Global{cpus}{$cpu}{cpu_time};
|
|
||||||
|
|
||||||
my $socket = $Global{cpuinfo}{$cpu}{physical_id};
|
Global['cpu_sockets'] = collections.defaultdict(lambda: 0)
|
||||||
$Global{cpu_sockets}{$socket}++ if defined $socket;
|
Global['cpu_socket_cores'] = collections.defaultdict(lambda: 0)
|
||||||
printf " socket=%d", $socket if defined $socket;
|
|
||||||
|
|
||||||
my $core = $Global{cpuinfo}{$cpu}{core_id};
|
for cpu in sorted(Global['cpus'].keys()):
|
||||||
$Global{cpu_socket_cores}{$socket."__".$core}++ if defined $socket && defined $core;
|
print(" cpu %d: " % cpu, end='')
|
||||||
printf " core=%d", $core if defined $core;
|
print("cpu_time=%d" % Global['cpus'][cpu]['cpu_time'], end='')
|
||||||
|
|
||||||
my $model = $Global{cpuinfo}{$cpu}{model_name};
|
socket = None
|
||||||
printf " %s", $model if defined $model;
|
if cpu in Global['cpuinfo']:
|
||||||
print "\n";
|
socket = int(Global['cpuinfo'][cpu]['physical_id'])
|
||||||
}
|
Global['cpu_sockets'][socket] += 1
|
||||||
|
print(" socket=%d" % socket, end='')
|
||||||
|
|
||||||
$Global{cpu_sockets_warning} = 1
|
core = int(Global['cpuinfo'][cpu]['core_id'])
|
||||||
if (scalar keys %{$Global{cpu_sockets}} > 1);
|
Global['cpu_socket_cores'][str(socket) + "__" + str(core)] += 1
|
||||||
foreach my $scn (values %{$Global{cpu_socket_cores}}) {
|
print(" core=%d" % core, end='')
|
||||||
$Global{cpu_socket_cores_warning} = 1 if $scn > 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub report_graph {
|
model = Global['cpuinfo'][cpu]['model_name']
|
||||||
my $time_per = $Opt_Time_Per_Char;
|
if model:
|
||||||
if ($time_per == 0) {
|
print(" %s" % model, end='')
|
||||||
$time_per = ($Global{last_end} / 40); # Start with 40 columns
|
print()
|
||||||
while ($time_per > 10) {
|
|
||||||
my ($graph, $conflicts) = _make_graph($time_per);
|
if len(Global['cpu_sockets']) > 1:
|
||||||
last if !$conflicts;
|
Global['cpu_sockets_warning'] = True
|
||||||
$time_per = int($time_per/2);
|
for scn in Global['cpu_socket_cores'].values():
|
||||||
}
|
if scn > 1:
|
||||||
|
Global['cpu_socket_cores_warning'] = True
|
||||||
|
|
||||||
|
|
||||||
|
def report_graph():
|
||||||
|
time_per = Args.scale
|
||||||
|
if time_per == 0:
|
||||||
|
time_per = Global['last_end'] / 40 # Start with 40 columns
|
||||||
|
while time_per > 10:
|
||||||
|
(graph, conflicts) = _make_graph(time_per)
|
||||||
|
if not conflicts:
|
||||||
|
break
|
||||||
|
time_per = int(time_per / 2)
|
||||||
# One more step so we can fit more labels
|
# One more step so we can fit more labels
|
||||||
$time_per = int($time_per/2);
|
time_per = int(time_per / 2)
|
||||||
$time_per ||= 1;
|
if time_per <= 0:
|
||||||
}
|
time_per = 1
|
||||||
|
|
||||||
my ($graph, $conflicts) = _make_graph($time_per);
|
(graph, conflicts) = _make_graph(time_per)
|
||||||
|
|
||||||
print "\nThread gantt graph:\n";
|
print("\nThread gantt graph:")
|
||||||
print " Legend: One character width = $time_per rdtsc ticks\n";
|
print(" Legend: One character width = %s rdtsc ticks" % time_per)
|
||||||
print " Legend: '&' = multiple mtasks in this period (character width)\n";
|
print(" Legend: '&' = multiple mtasks in this period (character width)")
|
||||||
|
|
||||||
my $scale = " <-".$Global{last_end}." rdtsc total";
|
scale = " <-%d rdtsc total" % Global['last_end']
|
||||||
for (my $col = length($scale); # -2 for '->' below
|
for col in range(len(scale), int(0.99 + (Global['last_end'] / time_per))): # pylint: disable=unused-variable
|
||||||
$col < ($Global{last_end}/$time_per); ++$col) {
|
scale += "-"
|
||||||
$scale .= "-";
|
print(" " + scale + "->")
|
||||||
}
|
|
||||||
print " $scale->\n";
|
|
||||||
|
|
||||||
foreach my $thread (sort keys %{$graph}) {
|
for thread in sorted(graph.keys()):
|
||||||
print " t: ";
|
print(" t: ", end="")
|
||||||
_print_graph_line($graph->{$thread}, '');
|
_print_graph_line(graph[thread], '')
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _make_graph {
|
|
||||||
my $time_per = shift;
|
|
||||||
|
|
||||||
my $graph = {}; # {thread}{column}{char=>'x' or chars=>#}
|
def _make_graph(time_per):
|
||||||
my $conflicts = 0;
|
|
||||||
foreach my $thread (keys %Threads) {
|
# [thread][column] = char or #
|
||||||
|
graph = collections.defaultdict(
|
||||||
|
lambda: collections.defaultdict(lambda: ''))
|
||||||
|
|
||||||
|
conflicts = 0
|
||||||
|
for thread in Threads:
|
||||||
# Make potentially multiple characters per column
|
# Make potentially multiple characters per column
|
||||||
foreach my $start (sort {$a <=> $b} keys %{$Threads{$thread}}) {
|
multi_at_col = collections.defaultdict(lambda: '')
|
||||||
my $end = $Threads{$thread}{$start}{end};
|
for start in sorted(Threads[thread].keys()):
|
||||||
my $mtask = $Threads{$thread}{$start}{mtask};
|
end = Threads[thread][start]['end']
|
||||||
my $cpu = $Threads{$thread}{$start}{cpu};
|
# mtask = Threads[thread][start]['mtask']
|
||||||
|
cpu = Threads[thread][start]['cpu']
|
||||||
|
|
||||||
my $startcol = _time_col($time_per, $start);
|
startcol = _time_col(time_per, start)
|
||||||
my $endcol = _time_col($time_per, $end);
|
endcol = _time_col(time_per, end)
|
||||||
|
|
||||||
|
label = "["
|
||||||
|
label += str(cpu) # Maybe make optional in future
|
||||||
|
width = endcol - startcol + 1
|
||||||
|
while len(label) < (width - 1): # -1 for ']'
|
||||||
|
label += "-"
|
||||||
|
label += "]"
|
||||||
|
multi_at_col[startcol] += label
|
||||||
|
|
||||||
my $label = "[";
|
|
||||||
$label .= "$cpu"; # Maybe make optional in future
|
|
||||||
my $width = $endcol - $startcol + 1;
|
|
||||||
while (length($label) < ($width-1)) { # -1 for ']'
|
|
||||||
$label .= "-";
|
|
||||||
}
|
|
||||||
$label .= "]";
|
|
||||||
$graph->{$thread}[$startcol]{char} .= $label;
|
|
||||||
}
|
|
||||||
if ($Debug) {
|
|
||||||
print "# Multicol: "; _print_graph_line($graph->{$thread}, '|');
|
|
||||||
}
|
|
||||||
# Expand line to one char per column
|
# Expand line to one char per column
|
||||||
for (my $col = 0; $col <= $#{$graph->{$thread}}; ++$col) {
|
for col in multi_at_col:
|
||||||
if (my $chars = $graph->{$thread}[$col]{char}) {
|
chars = multi_at_col[col]
|
||||||
my $ok = 1;
|
ok = True
|
||||||
for (my $coladd = 1; $coladd<length($chars); ++$coladd) {
|
for coladd in range(0, len(chars)):
|
||||||
if ($graph->{$thread}[$col + $coladd]{char}) {
|
if col + coladd in graph[thread]:
|
||||||
$ok = 0; last;
|
ok = False
|
||||||
}
|
break
|
||||||
}
|
if not ok:
|
||||||
if (!$ok) {
|
if re.search(r'\[.*\[', chars): # Two begins or more
|
||||||
if ($chars =~ /\[.*\[/) { # Two begins or more
|
conflicts += 1
|
||||||
$conflicts++;
|
graph[thread][col] = "&"
|
||||||
$graph->{$thread}[$col]{char} = "&";
|
else:
|
||||||
} else {
|
graph[thread][col] = "["
|
||||||
$graph->{$thread}[$col]{char} = "[";
|
for coladd in range(1, len(chars)):
|
||||||
}
|
if col + coladd in graph[thread]:
|
||||||
for (my $coladd = 1; $coladd<length($chars); ++$coladd) {
|
break
|
||||||
if ($graph->{$thread}[$col + $coladd]{char}) {
|
graph[thread][col + coladd] = 'x'
|
||||||
last;
|
else:
|
||||||
} else {
|
coladd = 0
|
||||||
$graph->{$thread}[$col + $coladd]{char} = 'x';
|
for char in chars:
|
||||||
}
|
graph[thread][col + coladd] = char
|
||||||
}
|
coladd += 1
|
||||||
} else {
|
|
||||||
my $coladd = 0;
|
if Args.debug:
|
||||||
foreach my $char (split //, $chars) {
|
print("# Conflicts %d" % conflicts)
|
||||||
$graph->{$thread}[$col+$coladd]{char} = $char;
|
return (graph, conflicts)
|
||||||
++$coladd;
|
|
||||||
}
|
|
||||||
}
|
def _print_graph_line(graph_thread, sep):
|
||||||
}
|
at = 0
|
||||||
|
for col in sorted(graph_thread.keys()):
|
||||||
|
while at < col:
|
||||||
|
print(' ', end="")
|
||||||
|
at += 1
|
||||||
|
c = graph_thread[col]
|
||||||
|
print(c + sep, end="")
|
||||||
|
at += len(c)
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def _time_col(time_per, time):
|
||||||
|
return int(time / time_per)
|
||||||
|
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
|
||||||
|
def write_vcd(filename):
|
||||||
|
print("Writing %s" % filename)
|
||||||
|
with open(filename, "w") as fh:
|
||||||
|
vcd = {
|
||||||
|
'values':
|
||||||
|
collections.defaultdict(lambda: {}), # {<time>}{<code>} = value
|
||||||
|
'sigs': {
|
||||||
|
'threads': {},
|
||||||
|
'cpus': {},
|
||||||
|
'mtasks': {},
|
||||||
|
'Stats': {}
|
||||||
|
} # {<module>}{<sig}} = code
|
||||||
}
|
}
|
||||||
if ($Debug) {
|
code = 0
|
||||||
print "# Singlcol: "; _print_graph_line($graph->{$thread}, '|');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
print "# Conflicts $conflicts\n" if $Debug;
|
|
||||||
return ($graph, $conflicts);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _print_graph_line {
|
parallelism = collections.defaultdict(lambda: 0)
|
||||||
my $graph_thread = shift;
|
for thread in sorted(Threads.keys()):
|
||||||
my $sep = shift;
|
sig = "thread%d_mtask" % thread
|
||||||
for (my $col = 0; $col <= $#{$graph_thread}; ++$col) {
|
if sig not in vcd['sigs']['threads']:
|
||||||
my $c = $graph_thread->[$col]{char}; $c=' ' if !defined $c;
|
vcd['sigs']['threads'][sig] = code
|
||||||
print $c, $sep;
|
code += 1
|
||||||
}
|
mcode = vcd['sigs']['threads'][sig]
|
||||||
print "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _time_col {
|
for start in sorted(Threads[thread]):
|
||||||
my $time_per = shift;
|
end = Threads[thread][start]['end']
|
||||||
my $time = shift;
|
mtask = Threads[thread][start]['mtask']
|
||||||
return int($time/$time_per);
|
cpu = Threads[thread][start]['cpu']
|
||||||
}
|
vcd['values'][start][mcode] = mtask
|
||||||
|
vcd['values'][end][mcode] = None
|
||||||
|
parallelism[start] += 1
|
||||||
|
parallelism[end] -= 1
|
||||||
|
|
||||||
#######################################################################
|
sig = "cpu%d_thread" % cpu
|
||||||
|
if sig not in vcd['sigs']['cpus']:
|
||||||
|
vcd['sigs']['cpus'][sig] = code
|
||||||
|
code += 1
|
||||||
|
ccode = vcd['sigs']['cpus'][sig]
|
||||||
|
vcd['values'][start][ccode] = thread
|
||||||
|
vcd['values'][end][ccode] = None
|
||||||
|
|
||||||
sub write_vcd {
|
sig = "mtask%d_cpu" % mtask
|
||||||
my $filename = shift;
|
if sig not in vcd['sigs']['mtasks']:
|
||||||
print "Writing $filename\n";
|
vcd['sigs']['mtasks'][sig] = code
|
||||||
my $fh = IO::File->new(">$filename") or die "%Error: $! $filename,";
|
code += 1
|
||||||
my $vcd = {values => {}, # {<time>}{<code>} = value
|
ccode = vcd['sigs']['mtasks'][sig]
|
||||||
sigs => {}, # {<module>}{<sig}} = code
|
vcd['values'][start][ccode] = cpu
|
||||||
code => 0,
|
vcd['values'][end][ccode] = None
|
||||||
};
|
|
||||||
|
|
||||||
my %parallelism;
|
# Parallelism graph
|
||||||
foreach my $thread (keys %Threads) {
|
vcd['sigs']['Stats']["parallelism"] = code
|
||||||
my $mcode = ($vcd->{sigs}{threads}{"thread${thread}_mtask"} ||= $vcd->{code}++);
|
pcode = code
|
||||||
foreach my $start (sort {$a <=> $b} keys %{$Threads{$thread}}) {
|
code += 1
|
||||||
my $end = $Threads{$thread}{$start}{end};
|
|
||||||
my $mtask = $Threads{$thread}{$start}{mtask};
|
|
||||||
my $cpu = $Threads{$thread}{$start}{cpu};
|
|
||||||
$vcd->{values}{$start}{$mcode} = $mtask;
|
|
||||||
$vcd->{values}{$end}{$mcode} = undef;
|
|
||||||
$parallelism{$start}++;
|
|
||||||
$parallelism{$end}--;
|
|
||||||
|
|
||||||
my $ccode = $vcd->{sigs}{cpus}{"cpu${cpu}_thread"} ||= $vcd->{code}++;
|
value = 0
|
||||||
$vcd->{values}{$start}{$ccode} = $thread;
|
for time in sorted(parallelism.keys()):
|
||||||
$vcd->{values}{$end}{$ccode} = undef;
|
value += parallelism[time]
|
||||||
|
vcd['values'][time][pcode] = value
|
||||||
|
|
||||||
my $mcode = $vcd->{sigs}{mtasks}{"mtask${mtask}_cpu"} ||= $vcd->{code}++;
|
fh.write("$version Generated by verilator_gantt $end\n")
|
||||||
$vcd->{values}{$start}{$mcode} = $cpu;
|
fh.write("$timescale 1ns $end\n")
|
||||||
$vcd->{values}{$end}{$mcode} = undef;
|
fh.write("\n")
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
my $pcode = ($vcd->{sigs}{Stats}{"parallelism"} ||= $vcd->{code}++);
|
|
||||||
my $value = 0;
|
|
||||||
foreach my $time (sort {$a<=>$b} keys %parallelism) {
|
|
||||||
$value += $parallelism{$time};
|
|
||||||
$vcd->{values}{$time}{$pcode} = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$fh->print('$version Generated by verilator_gantt $end'."\n");
|
all_codes = {}
|
||||||
$fh->print('$timescale 1ns $end'."\n");
|
fh.write(" $scope module gantt $end\n")
|
||||||
$fh->print("\n");
|
for module in sorted(vcd['sigs'].keys()):
|
||||||
|
fh.write(" $scope module %s $end\n" % module)
|
||||||
|
for sig in sorted(vcd['sigs'][module].keys()):
|
||||||
|
code = vcd['sigs'][module][sig]
|
||||||
|
fh.write(" $var wire 32 v%x %s [31:0] $end\n" % (code, sig))
|
||||||
|
all_codes[code] = 1
|
||||||
|
fh.write(" $upscope $end\n")
|
||||||
|
fh.write(" $upscope $end\n")
|
||||||
|
fh.write("$enddefinitions $end\n")
|
||||||
|
fh.write("\n")
|
||||||
|
|
||||||
my %all_codes;
|
first = True
|
||||||
$fh->print(' $scope module gantt $end'."\n");
|
for time in sorted(vcd['values']):
|
||||||
foreach my $module (sort keys %{$vcd->{sigs}}) {
|
if first:
|
||||||
$fh->printf(' $scope module %s $end'."\n", $module);
|
first = False
|
||||||
foreach my $sig (sort keys %{$vcd->{sigs}{$module}}) {
|
# Start with Z for any signals without time zero data
|
||||||
my $code = $vcd->{sigs}{$module}{$sig};
|
for code in sorted(all_codes.keys()):
|
||||||
$fh->printf(' $var wire 32 v%x %s [31:0] $end'."\n",
|
if code not in vcd['values'][time]:
|
||||||
$code, $sig);
|
vcd['values'][time][code] = None
|
||||||
$all_codes{$code} = 1;
|
fh.write("#%d\n" % time)
|
||||||
}
|
for code in sorted(vcd['values'][time].keys()):
|
||||||
$fh->print(' $upscope $end'."\n");
|
value = vcd['values'][time][code]
|
||||||
}
|
if value is None:
|
||||||
$fh->print(' $upscope $end'."\n");
|
fh.write("bz v%x\n" % code)
|
||||||
$fh->print('$enddefinitions $end'."\n");
|
else:
|
||||||
$fh->print("\n");
|
fh.write("b%s v%x\n" % (format(value, 'b'), code))
|
||||||
|
|
||||||
my $first = 1;
|
|
||||||
foreach my $time (sort {$a <=> $b} keys %{$vcd->{values}}) {
|
|
||||||
if ($first) {
|
|
||||||
$first = 0;
|
|
||||||
# Start with Z for any signals without time zero data
|
|
||||||
foreach my $code (keys %all_codes) {
|
|
||||||
if (!defined $vcd->{values}{$time}{$code}) {
|
|
||||||
$vcd->{values}{$time}{$code} = undef;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$fh->printf("#%d\n", $time);
|
|
||||||
foreach my $code (sort keys %{$vcd->{values}{$time}}) {
|
|
||||||
my $value = $vcd->{values}{$time}{$code};
|
|
||||||
if (defined $value) {
|
|
||||||
$fh->printf("b%b v%x\n", $value, $code);
|
|
||||||
} else {
|
|
||||||
$fh->printf("bz v%x\n", $code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
######################################################################
|
||||||
# Similar to Statistics::Basic functions, but avoid a package dependency
|
|
||||||
|
|
||||||
sub max {
|
parser = argparse.ArgumentParser(
|
||||||
my $n = $_[0]; shift;
|
allow_abbrev=False,
|
||||||
while (defined $_[0]) {
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
$n = $_[0] if !defined $n || $_[0] > $n;
|
description="""Create Gantt chart of multi-threaded execution""",
|
||||||
shift;
|
epilog=
|
||||||
}
|
"""Verilator_gantt creates a visual representation to help analyze Verilator
|
||||||
return $n;
|
#xmultithreaded simulation performance, by showing when each macro-task
|
||||||
}
|
#xstarts and ends, and showing when each thread is busy or idle.
|
||||||
|
|
||||||
sub mean {
|
|
||||||
my $arrayref = shift;
|
|
||||||
my $n = 0;
|
|
||||||
my $sum = 0;
|
|
||||||
foreach my $v (@$arrayref) {
|
|
||||||
$sum += $v;
|
|
||||||
$n++;
|
|
||||||
}
|
|
||||||
return undef if !$n;
|
|
||||||
return $sum/$n;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub stddev {
|
|
||||||
my $arrayref = shift;
|
|
||||||
my $n = 0;
|
|
||||||
my $sum = 0;
|
|
||||||
my $sumsq = 0;
|
|
||||||
foreach my $v (@$arrayref) {
|
|
||||||
$sum += $v;
|
|
||||||
$sumsq += $v**2;
|
|
||||||
$n++;
|
|
||||||
}
|
|
||||||
return undef if !$n;
|
|
||||||
return sqrt(($sumsq/$n) - ($sum/$n)**2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
__END__
|
|
||||||
|
|
||||||
=pod
|
|
||||||
|
|
||||||
=head1 NAME
|
|
||||||
|
|
||||||
verilator_gantt - Create Gantt chart of multi-threaded execution
|
|
||||||
|
|
||||||
=head1 SYNOPSIS
|
|
||||||
|
|
||||||
Verilator_gantt creates a visual representation to help analyze Verilator
|
|
||||||
multithreaded simulation performance, by showing when each macro-task
|
|
||||||
starts and ends, and showing when each thread is busy or idle.
|
|
||||||
|
|
||||||
For documentation see
|
For documentation see
|
||||||
L<https://verilator.org/guide/latest/exe_verilator_gantt.html>.
|
https://verilator.org/guide/latest/exe_verilator_gantt.html
|
||||||
|
|
||||||
=head1 ARGUMENT SUMMARY
|
|
||||||
|
|
||||||
<filename> Filename to read data from, default "profile_threads.dat".
|
|
||||||
--help Displays this message and program version and exits.
|
|
||||||
--scale I<n> Number of characters per time step.
|
|
||||||
--no-vcd Do not create a VCD file.
|
|
||||||
--vcd <filename> Set output filename for vcd dump, default "verilator_gantt.vcd."
|
|
||||||
|
|
||||||
=head1 DISTRIBUTION
|
|
||||||
|
|
||||||
The latest version is available from L<https://verilator.org>.
|
|
||||||
|
|
||||||
Copyright 2018-2021 by Wilson Snyder. This program is free software; you
|
Copyright 2018-2021 by Wilson Snyder. This program is free software; you
|
||||||
can redistribute it and/or modify it under the terms of either the GNU
|
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
|
Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
Version 2.0.
|
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_true', help='enable debug')
|
||||||
|
parser.add_argument('--scale',
|
||||||
|
help='number of time units per character in graph',
|
||||||
|
type=int,
|
||||||
|
default=0)
|
||||||
|
parser.add_argument('--no-vcd',
|
||||||
|
help='disable creating vcd',
|
||||||
|
action='store_true')
|
||||||
|
parser.add_argument('--vcd',
|
||||||
|
help='filename for vcd outpue',
|
||||||
|
default='profile_threads.vcd')
|
||||||
|
parser.add_argument('filename',
|
||||||
|
help='input profile_threads.dat filename to process',
|
||||||
|
default='profile_threads.dat')
|
||||||
|
|
||||||
C<verilator>
|
Args = parser.parse_args()
|
||||||
|
|
||||||
and L<https://verilator.org/guide/latest/exe_verilator_gantt.html> for
|
process(Args.filename)
|
||||||
detailed documentation.
|
if not Args.no_vcd:
|
||||||
|
write_vcd(Args.vcd)
|
||||||
=cut
|
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
### Local Variables:
|
# Local Variables:
|
||||||
### compile-command: "$V4/bin/verilator_gantt $V4/test_regress/obj_vltmt/t_gantt/vlt_sim.log"
|
# compile-command: "./verilator_gantt ../test_regress/t/t_gantt_io.dat"
|
||||||
### End:
|
# End:
|
||||||
|
|
|
||||||
|
|
@ -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.
|
# Configuration file for Verilator's Sphinx documentation builder.
|
||||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,11 @@ Argument settings:
|
||||||
--threads 2
|
--threads 2
|
||||||
|
|
||||||
Thread gantt graph:
|
Thread gantt graph:
|
||||||
Legend: One character width = 200 rdtsc ticks
|
Legend: One character width = 100 rdtsc ticks
|
||||||
Legend: '&' = multiple mtasks in this period (character width)
|
Legend: '&' = multiple mtasks in this period (character width)
|
||||||
<-16065 rdtsc total------------------------------------------------------------>
|
<-16065 rdtsc total-------------------------------------------------------------------------------------------------------------------------------------------->
|
||||||
t: [1] [1] [1] [1]
|
t: [1] [1] [1] [1]
|
||||||
t: [xx[x[16--] [16] [16] [16] [[x[[16]
|
t: [16-] [16][16-------] [16--] [16] [16-] [1[] [16[xxx
|
||||||
|
|
||||||
Analysis:
|
Analysis:
|
||||||
Total threads = 2
|
Total threads = 2
|
||||||
|
|
@ -26,7 +26,7 @@ Analysis:
|
||||||
|
|
||||||
Statistics:
|
Statistics:
|
||||||
min log(p2e) = -3.332 from mtask 5 (predict 30, elapsed 840)
|
min log(p2e) = -3.332 from mtask 5 (predict 30, elapsed 840)
|
||||||
max log(p2e) = -1.764 from mtask 11 (predict 30, elapsed 175)
|
max log(p2e) = -1.764 from mtask 7 (predict 30, elapsed 175)
|
||||||
mean = -2.365
|
mean = -2.365
|
||||||
stddev = 0.562
|
stddev = 0.562
|
||||||
e ^ stddev = 1.754
|
e ^ stddev = 1.754
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue