iverilog/ivtest/vpi_reg.pl

292 lines
9.3 KiB
Perl
Raw Normal View History

#!/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
}