292 lines
9.3 KiB
Perl
292 lines
9.3 KiB
Perl
|
|
#!/usr/bin/env perl
|
||
|
|
#
|
||
|
|
# Script to handle regression for VPI routines
|
||
|
|
#
|
||
|
|
use lib './perl-lib';
|
||
|
|
|
||
|
|
use Environment;
|
||
|
|
|
||
|
|
$| = 1; # This turns off buffered I/O
|
||
|
|
|
||
|
|
# We support a --suffix= and --with-valgrind flags.
|
||
|
|
use Getopt::Long;
|
||
|
|
$sfx = ""; # Default suffix.
|
||
|
|
$with_valg = 0; # Default valgrind usage (keep this off).
|
||
|
|
if (!GetOptions("suffix=s" => \$sfx,
|
||
|
|
"with-valgrind" => \$with_valg,
|
||
|
|
"help" => \&usage)) {
|
||
|
|
die "Error: Invalid argument(s).\n";
|
||
|
|
}
|
||
|
|
|
||
|
|
sub usage {
|
||
|
|
warn "$0 usage:\n\n" .
|
||
|
|
" --suffix=<suffix> # The Icarus executables suffix, " .
|
||
|
|
"default \"\".\n" .
|
||
|
|
" --with-valgrind # Run the test suite with valgrind, " .
|
||
|
|
"default \"off\".\n" .
|
||
|
|
" <regression file> # The regression file, " .
|
||
|
|
"default \"./vpi_regress.list\".\n\n";
|
||
|
|
exit;
|
||
|
|
}
|
||
|
|
|
||
|
|
$regress_fn = "./vpi_regress.list"; # Default regression list.
|
||
|
|
|
||
|
|
# Is there a command line argument (alternate regression list)?
|
||
|
|
if ($#ARGV != -1) {
|
||
|
|
$regress_fn = $ARGV[0];
|
||
|
|
-e $regress_fn or
|
||
|
|
die "Error: command line regression file $regress_fn doesn't exist.\n";
|
||
|
|
-f $regress_fn or
|
||
|
|
die "Error: command line regression file $regress_fn is not a file.\n";
|
||
|
|
-r $regress_fn or
|
||
|
|
die "Error: command line regression file $regress_fn is not ".
|
||
|
|
"readable.\n";
|
||
|
|
|
||
|
|
if ($#ARGV > 0) {
|
||
|
|
warn "Warning: only using first file argument to script.\n";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#
|
||
|
|
# Main script
|
||
|
|
#
|
||
|
|
my $sys = $ENV{MSYSTEM} ? 'msys2' : 'other';
|
||
|
|
my $ver = &get_ivl_version($sfx);
|
||
|
|
my $msg = $with_valg ? " (with valgrind)" : "";
|
||
|
|
print ("Running VPI tests for Icarus Verilog version: $ver$msg.\n");
|
||
|
|
print "-" x 76 . "\n";
|
||
|
|
&read_regression_list;
|
||
|
|
&execute_regression;
|
||
|
|
|
||
|
|
#
|
||
|
|
# parses the regression list file
|
||
|
|
#
|
||
|
|
# (from left-to-right in regression file):
|
||
|
|
#
|
||
|
|
# test_name type c/c++_code gold_file opt_c/c++_defines
|
||
|
|
#
|
||
|
|
sub read_regression_list {
|
||
|
|
my ($line, @fields, $tname, $tver, %nameidx);
|
||
|
|
open (REGRESS_LIST, "<$regress_fn") or
|
||
|
|
die "Error: unable to open $regress_fn for reading.\n";
|
||
|
|
|
||
|
|
while ($line = <REGRESS_LIST>) {
|
||
|
|
chomp $line;
|
||
|
|
next if ($line =~ /^\s*#/); # Skip comments.
|
||
|
|
next if ($line =~ /^\s*$/); # Skip blank lines.
|
||
|
|
|
||
|
|
$line =~ s/#.*$//; # Strip in line comments.
|
||
|
|
$line =~ s/\s+$//; # Strip trailing white space.
|
||
|
|
|
||
|
|
# You must specify at least the first four fields, cargs is optional
|
||
|
|
# and gets the rest of the fields if present.
|
||
|
|
@fields = split(' ', $line, 5);
|
||
|
|
if (@fields < 3) {
|
||
|
|
die "Error: $fields[0] must have at least 4 fields.\n";
|
||
|
|
}
|
||
|
|
|
||
|
|
$tname = $fields[0];
|
||
|
|
# Check for a version or system specific line.
|
||
|
|
if ($tname =~ /:/) {
|
||
|
|
($tver, $tname) = split(':', $tname);
|
||
|
|
next if (exists($ccode{$tname})); # Skip if already defined.
|
||
|
|
next if ($tver ne "v$ver" && $tver ne $sys); # Skip if this is not our version or system.
|
||
|
|
# Get the test type and any iverilog arguments.
|
||
|
|
if ($fields[1] =~ ',') {
|
||
|
|
($testtype, $args{$tname}) = split(',', $fields[1], 2);
|
||
|
|
# For now we just support args to iverilog.
|
||
|
|
if ($args{$tname} =~ ',') {
|
||
|
|
$args{$tname} = join(' ', split(',', $args{$tname}));
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
$testtype = $fields[1];
|
||
|
|
$args{$tname} = "";
|
||
|
|
}
|
||
|
|
# This version of the program does not implement something
|
||
|
|
# required to run this test.
|
||
|
|
if ($testtype eq "NI") {
|
||
|
|
$args{$tname} = "";
|
||
|
|
$ccode{$tname} = "";
|
||
|
|
$goldfile{$tname} = "";
|
||
|
|
$cargs{$tname} = "";
|
||
|
|
} else {
|
||
|
|
$ccode{$tname} = $fields[2];
|
||
|
|
$goldfile{$tname} = $fields[3];
|
||
|
|
$cargs{$tname} = $fields[4];
|
||
|
|
}
|
||
|
|
# print "Read $tver:$tname=$ccode{$tname}, $goldfile{$tname}, ".
|
||
|
|
# "$args{$tname}, $cargs{$tname}\n";
|
||
|
|
} else {
|
||
|
|
next if (exists($ccode{$tname})); # Skip if already defined.
|
||
|
|
# Get the test type and any iverilog arguments.
|
||
|
|
if ($fields[1] =~ ',') {
|
||
|
|
($testtype, $args{$tname}) = split(',', $fields[1], 2);
|
||
|
|
# For now we just support args to iverilog.
|
||
|
|
if ($args{$tname} =~ ',') {
|
||
|
|
$args{$tname} = join(' ', split(',', $args{$tname}));
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
$args{$tname} = "";
|
||
|
|
}
|
||
|
|
$ccode{$tname} = $fields[2];
|
||
|
|
$goldfile{$tname} = $fields[3];
|
||
|
|
$cargs{$tname} = $fields[4];
|
||
|
|
# print "Read $tname=$ccode{$tname}, $goldfile{$tname}, ".
|
||
|
|
# "$args{$tname}, $cargs{$tname}\n";
|
||
|
|
}
|
||
|
|
# If there wasn't a cargs field make it a null string.
|
||
|
|
$cargs{$tname} = "" if (!defined($cargs{$tname}));
|
||
|
|
|
||
|
|
# If the name exists this is a replacement so skip the original one.
|
||
|
|
if (exists($nameidx{$tname})) {
|
||
|
|
splice(@testlist, $nameidx{$tname}, 1, "");
|
||
|
|
}
|
||
|
|
push (@testlist,$tname);
|
||
|
|
$nameidx{$tname} = @testlist - 1;
|
||
|
|
}
|
||
|
|
close (REGRESS_LIST);
|
||
|
|
}
|
||
|
|
|
||
|
|
#
|
||
|
|
# execute_regression sequentially compiles and executes each test in
|
||
|
|
# the regression. It then checks that the output matched the gold file.
|
||
|
|
#
|
||
|
|
sub execute_regression {
|
||
|
|
my ($tname, $total, $passed, $failed, $not_impl, $len, $cmd);
|
||
|
|
|
||
|
|
$total = 0;
|
||
|
|
$passed = 0;
|
||
|
|
$failed = 0;
|
||
|
|
$not_impl = 0;
|
||
|
|
$len = 0;
|
||
|
|
|
||
|
|
foreach $tname (@testlist) {
|
||
|
|
$len = length($tname) if (length($tname) > $len);
|
||
|
|
}
|
||
|
|
|
||
|
|
# Make sure we have a log directory.
|
||
|
|
if (! -d 'vpi_log') {
|
||
|
|
mkdir 'vpi_log' or die "Error: unable to create vpi_log directory.\n";
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach $tname (@testlist) {
|
||
|
|
next if ($tname eq ""); # Skip test that have been replaced.
|
||
|
|
|
||
|
|
$total++;
|
||
|
|
printf "%${len}s: ", $tname;
|
||
|
|
if (-e "vpi_log/$tname.log") {
|
||
|
|
unlink "vpi_log/$tname.log" or
|
||
|
|
die "Error: unable to remove old log file ".
|
||
|
|
"vpi_log/$tname.log.\n";
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($ccode{$tname} eq "") {
|
||
|
|
print "Not Implemented.\n";
|
||
|
|
$not_impl++;
|
||
|
|
next;
|
||
|
|
}
|
||
|
|
|
||
|
|
$cmd = "iverilog-vpi$sfx --name=$tname $cargs{$tname} " .
|
||
|
|
"vpi/$ccode{$tname} > vpi_log/$tname.log 2>&1";
|
||
|
|
if (system("$cmd")) {
|
||
|
|
print "==> Failed - running iverilog-vpi.\n";
|
||
|
|
$failed++;
|
||
|
|
next;
|
||
|
|
}
|
||
|
|
|
||
|
|
$cmd = $with_valg ? "valgrind --trace-children=yes " : "";
|
||
|
|
if ($ver < 11) {
|
||
|
|
$cmd .= "iverilog$sfx $args{$tname} -o vsim vpi/$tname.v >> " .
|
||
|
|
"vpi_log/$tname.log 2>&1";
|
||
|
|
} else {
|
||
|
|
$cmd .= "iverilog$sfx $args{$tname} -L . -m $tname -o vsim vpi/$tname.v >> " .
|
||
|
|
"vpi_log/$tname.log 2>&1";
|
||
|
|
}
|
||
|
|
if (system("$cmd")) {
|
||
|
|
print "==> Failed - running iverilog.\n";
|
||
|
|
$failed++;
|
||
|
|
next;
|
||
|
|
}
|
||
|
|
|
||
|
|
$cmd = $with_valg ? "valgrind --leak-check=full " .
|
||
|
|
"--show-reachable=yes " : "";
|
||
|
|
if ($ver < 11) {
|
||
|
|
$cmd .= "vvp$sfx -M . -m $tname vsim >> vpi_log/$tname.log 2>&1";
|
||
|
|
} else {
|
||
|
|
$cmd .= "vvp$sfx vsim >> vpi_log/$tname.log 2>&1";
|
||
|
|
}
|
||
|
|
if (system("$cmd")) {
|
||
|
|
print "==> Failed - running vvp.\n";
|
||
|
|
$failed++;
|
||
|
|
next;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (diff("vpi_gold/$goldfile{$tname}", "vpi_log/$tname.log")) {
|
||
|
|
print "==> Failed - output does not match gold file.\n";
|
||
|
|
$failed++;
|
||
|
|
next;
|
||
|
|
}
|
||
|
|
|
||
|
|
print "Passed.\n";
|
||
|
|
$passed++;
|
||
|
|
|
||
|
|
} continue {
|
||
|
|
# We have to use system and not unlink here since these files
|
||
|
|
# were created by this process and it doesn't seem to know they
|
||
|
|
# are not being used.
|
||
|
|
if ($tname ne "" and $ccode{$tname} ne "") {
|
||
|
|
my $doto = $ccode{$tname};
|
||
|
|
$doto =~ s/\.(c|cc|cpp)$/.o/;
|
||
|
|
system("rm -f $doto $tname.vpi vsim") and
|
||
|
|
die "Error: failed to remove temporary files.\n";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
print "=" x 76 . "\n";
|
||
|
|
print "Test results: Total=$total, Passed=$passed, Failed=$failed,".
|
||
|
|
" Not Implemented=$not_impl\n";
|
||
|
|
exit $failed;
|
||
|
|
}
|
||
|
|
|
||
|
|
#
|
||
|
|
# We only need a simple diff, but we need to strip \r at the end of line.
|
||
|
|
#
|
||
|
|
sub diff {
|
||
|
|
my ($gold, $log) = @_;
|
||
|
|
my ($diff, $gline, $lline);
|
||
|
|
$diff = 0;
|
||
|
|
|
||
|
|
open (GOLD, "<$gold") or die "Error: unable to open $gold for reading.\n";
|
||
|
|
open (LOG, "<$log") or die "Error: unable to open $log for reading.\n";
|
||
|
|
|
||
|
|
# Loop on the gold file lines.
|
||
|
|
foreach $gline (<GOLD>) {
|
||
|
|
if (eof LOG) {
|
||
|
|
$diff = 1;
|
||
|
|
last;
|
||
|
|
}
|
||
|
|
$lline = <LOG>;
|
||
|
|
# Skip lines from valgrind ^==\d+== or ^**\d+**
|
||
|
|
while ($lline =~ m/^(==|\*\*)\d+(==|\*\*)/) {
|
||
|
|
$lline = <LOG>;
|
||
|
|
}
|
||
|
|
$gline =~ s/\r\n$/\n/; # Strip <CR> at the end of line.
|
||
|
|
$lline =~ s/\r\n$/\n/; # Strip <CR> at the end of line.
|
||
|
|
if ($gline ne $lline) {
|
||
|
|
$diff = 1;
|
||
|
|
last;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Check to see if the log file has extra lines.
|
||
|
|
while (!eof LOG and !$diff) {
|
||
|
|
$lline = <LOG>;
|
||
|
|
$diff = 1 if ($lline !~ m/^(==|\*\*)\d+(==|\*\*)/);
|
||
|
|
}
|
||
|
|
|
||
|
|
close (LOG);
|
||
|
|
close (GOLD);
|
||
|
|
|
||
|
|
return $diff
|
||
|
|
}
|