blob: 27761b46c4f890fe24230200374b30f653991065 [file] [log] [blame]
#! /usr/bin/env perl
use strict;
use warnings;
use Getopt::Long;
use File::Path qw(make_path remove_tree);
use File::Basename;
use List::Util qw(shuffle);
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime();
GetOptions (
"help|h" => \(my $opt_help),
"lsf" => \(my $opt_lsf),
"local|loc" => \(my $opt_local_run),
"max_job=i" => \(my $opt_max_job=0),
"queue|que=s" => \(my $opt_queue=undef),
"repeat|rep=i" => \(my $opt_repeat=0),
"testlist|list=s" => \(my @opt_testlists=()),
"logdir|dir=s" => \(my $opt_logdir=undef),
"show" => \(my $opt_show),
"no_compile|noc" => \(my $opt_no_compile),
"only_compile|oc" => \(my $opt_only_compile),
"scfg=s" => \(my $scfg = "rvv_backend"),
"report" => \(my $opt_report),
"coverage|cov" => \(my $opt_coverage),
"show_discard|disc" => \(my $opt_show_disc),
"shuffle" => \(my $opt_shuffle),
"qualify|q" => \(my $opt_qualify),
"mode=s" => \(my $opt_mode = "normal"),
);
if($opt_help) { Usage(); }
# Path config
my $workRoot = $ENV{PWD};
$workRoot =~ s%(\S+)/hdl/verilog/rvv/%$1%;
my $base = "";
if($scfg =~ m/(rvv_backend)/g) { $base = $1; }
my @testLists = map { "$workRoot/sve/${base}_tb/$_"; } ("regress.list", "regress_random.list", "regress_corner.list");
if(scalar(@opt_testlists)) { @testLists = map { "$workRoot/$_"; } @opt_testlists; }
$mon = $mon + 1;
my $logDir = "outfiles_regress/${mon}_${mday}_${hour}_${min}";
if(defined $opt_logdir) { $logDir = "$opt_logdir"; }
my $absLogDir = "$workRoot/$logDir";
my $regressLog = "$workRoot/regress.log";
open my $fh_regLog, "+>", $regressLog or die "Open $regressLog failed. $! ";
my $compilePath = "$workRoot/build/$scfg";
my $simulatePath = "$logDir/$scfg";
my $coveragePath = "$logDir/$scfg.vdb";
# Server Config
my $useLSF = $opt_lsf ? 1 : 0;
my $localRun = $opt_local_run ? 1 : 0;
my $maxJobs = $localRun ? 1 : ($opt_max_job ? $opt_max_job : 1);
my $cpuNum = 1;
my $mem = 1000;
my $host = qq("span[hosts=1] rusage[mem=$mem] select[type==rhel7]");
my $queue = "";
my $maxRunTime = 120;
if(defined $opt_queue) { $queue = $opt_queue; }
my $bsub = $localRun ? "bsub -Is -q $queue -n $cpuNum -R $host"
: "bsub -q $queue -n $cpuNum -R $host";
## for local server
my $null = ' > /dev/null 2>&1';
my $maxProcess = ($opt_max_job && $opt_max_job <= 30) ? $opt_max_job : 20;
# Execution Steps Config
my $exeCompile = 1;
$exeCompile = 0 if($opt_no_compile || $opt_report || $opt_show);
my $exeSimulate = 1;
$exeSimulate = 0 if($opt_only_compile || $opt_report || $opt_show);
my $exeReport = 1;
$exeReport = 0 if($opt_show || $opt_only_compile || $opt_show);
my $exeShow = $opt_show;
my $exeCoverage = 0;
$exeCoverage = 1 if($opt_coverage && !$opt_show);
my $exeCountDiscRate = 0;
$exeCountDiscRate = 1 if($opt_show_disc);
my $repeat = $opt_repeat ? $opt_repeat : 0;
# Others
my $startTime, my $startTimeStr;
my $finishTime, my $finishTimeStr;
my $regStatus = 0;
#------------------------------------------------------------
# Main
#------------------------------------------------------------
if(!$exeShow) {
if(-d $logDir) {
#remove_tree($logDir) or die "Remove $logDir failed $!";
} else {
make_path($logDir) or die "Make $logDir failed $!";
}
}
my @tests = ();
foreach my $testList (@testLists) {
open my $fh_testLile, '<', $testList, or die "Open $testList failed. $! ";
push @tests, map { $_ =~ m/^\s*[^#\s].*$/g; } <$fh_testLile>;
close $fh_testLile;
}
if($exeShow) {
foreach (@tests) {
printf "$_\n";
}
}
# generate command
my @cmd_comp =();
push @cmd_comp, qq(sve/rvv_backend_tb/Makefile vcs scfg=$scfg);
my @cmd_sim;
foreach my $test_name (@tests) {
if($repeat) {
foreach my $re (0..$repeat-1) {
push @cmd_sim, qq(sve/rvv_backend_tb/Makefile sim scfg=$scfg test=$test_name atn=._duplicate_${re}_ logdir=$logDir UVM_VERBOSITY=UVM_NONE);
}
} else {
push @cmd_sim, qq(sve/rvv_backend_tb/Makefile sim scfg=$scfg test=$test_name logdir=$logDir UVM_VERBOSITY=UVM_NONE);
}
}
if($opt_shuffle) { @cmd_sim = shuffle(@cmd_sim);}
my $qualifyOpt;
if($opt_qualify) {
$qualifyOpt = "PLUS_ARGS+=+qualify";
@cmd_sim = map { "$_ $qualifyOpt"; } @cmd_sim;
}
my $covOpt;
if($exeCoverage) {
$covOpt = "coverage=on";
@cmd_comp = map { "$_ $covOpt logdir=$logDir"; } @cmd_comp;
@cmd_sim = map { "$_ $covOpt"; } @cmd_sim;
} else {
$covOpt = "";
}
my $modeOpt;
if($opt_mode =~ m/slow/) {
$modeOpt = "PLUS_ARGS+=+delay_mode_all_slow";
@cmd_sim = map { "$_ $modeOpt"; } @cmd_sim;
}
if($opt_mode =~ m/fast/) {
$modeOpt = "PLUS_ARGS+=+delay_mode_all_fast";
@cmd_sim = map { "$_ $modeOpt"; } @cmd_sim;
}
$startTime = time();
$startTimeStr = localtime();
if($useLSF) {
my $regressCmdList = "./regress_cmdlist";
my $useBash = 0;
open my $fh_regCmdList, "+>", $regressCmdList, or die "Open $regressCmdList failed. $! ";
# packet tests
my @cmd_compPkt;
while (@cmd_comp) {
my $combine = '"'.join("; ", splice(@cmd_comp, 0, $maxJobs)).'"';
push @cmd_compPkt, $combine;
}
my @cmd_simPkt;
while (@cmd_sim) {
my $combine = '"'.join("; ", splice(@cmd_sim, 0, $maxJobs)).'"';
push @cmd_simPkt, $combine;
}
if($exeShow) {
foreach (@cmd_compPkt) { print "$_\n"; }
foreach (@cmd_simPkt) { print "$_\n"; }
}
# Run tests
my $jobTag = "";
my $cmd = "";
my @jobIDs;
my $jobID;
if($exeCompile) {
$jobTag = "comp_$ENV{USER}";
if($useBash) {
seek $fh_regCmdList, 0, 0;
@cmd_compPkt = map { "$bsub -W $maxRunTime -J $jobTag $_ \n"; } @cmd_compPkt;
print $fh_regCmdList @cmd_compPkt;
system("com/regress_bs $regressCmdList");
$cmd = qq($bsub -w 'ended("$jobTag")' -I 'echo Compile finished.');
system($cmd);
} else {
@cmd_compPkt = map { "$bsub -W $maxRunTime -J $jobTag $_"; } @cmd_compPkt;
foreach $cmd (@cmd_compPkt) {
print "Submit compile: $cmd\n";
$jobID = qx($cmd);
print "$jobID";
$jobID =~ s/.*?(\d+).*/$1/g;
push @jobIDs, $jobID;
sleep 1; # bsub gap
}
print "Waiting for compiles ... \n";
$cmd = qq($bsub -w 'ended("$jobTag")' -I 'echo Compile finished.');
print "$cmd\n";
system($cmd);
# Compile result check
foreach (@jobIDs) {
my $exitCode;
$exitCode = qx(bjobs -o "exit_code" $_);
chomp $exitCode;
$exitCode =~ s/.*\n?(\d+).*/$1/g;
if($exitCode) {
print $fh_regLog "Compile fail!\n";
print STDERR "Compile fail with $exitCode!\n";
exit 255;
}
}
}
}
if($exeSimulate) {
$jobTag = "sim_$ENV{USER}";
if($useBash) {
seek $fh_regCmdList, 0, 0;
@cmd_simPkt = map { "$bsub -W $maxRunTime -J $jobTag $_ \n"; } @cmd_simPkt;
print $fh_regCmdList @cmd_simPkt;
system("com/regress_bs $regressCmdList");
$cmd = qq($bsub -w 'ended("$jobTag")' -I 'echo Simulate done!');
system($cmd);
} else {
@cmd_simPkt = map { "$bsub -W $maxRunTime -J $jobTag $_"; } @cmd_simPkt;
foreach $cmd (@cmd_simPkt) {
print "Submit tests: $cmd\n";
system($cmd);
sleep 1; # bsub gap
}
print "Waiting for simulation ... \n";
$cmd = qq($bsub -w 'ended("$jobTag")' -I 'echo Simulate done!');
print "$cmd\n";
system($cmd);
}
}
if($exeCoverage) {
if($useBash) {
$cmd = qq(urg -dir $logDir/$scfg.vdb -report $logDir/urgReport);
system($cmd);
} else {
$cmd = qq(urg -dir $logDir/$scfg.vdb -report $logDir/urgReport);
print "$cmd\n";
system($cmd);
}
}
close $fh_regCmdList;
} else {
my $cmd = "";
my $status = 0;
if($exeCompile) {
print("Compile Start!\n");
if($localRun) {
foreach $cmd (@cmd_comp) {
system($cmd) if !$exeShow;
print "$cmd\n" if $exeShow;
}
} else {
$status = ForkCMD(@cmd_comp);
}
if($status) {
print "Compile failed!";
print $fh_regLog "Compile failed!";
exit 255;
} else {
print("Compile done!\n");
}
}
if($exeSimulate) {
print("Simulation Start!\n");
if($localRun) {
foreach $cmd (@cmd_sim) {
system($cmd) if !$exeShow;
print "$cmd\n" if $exeShow;
}
} else {
$status = ForkCMD(@cmd_sim);
}
print("Simulate done!\n");
}
if($exeCoverage) {
$cmd = qq(urg -dir $logDir/$scfg.vdb -report $logDir/urgReport);
system($cmd) if !$exeShow;
print "$cmd\n" if $exeShow;
}
}
$regStatus = CheckResult($logDir) if $exeReport;
$finishTime = time();
$finishTimeStr = localtime();
my $costTime = ($finishTime - $startTime) / 60.0;
print "\n";
print $fh_regLog "\n";
print "Run direction: $absLogDir\n";
print $fh_regLog "Run direction: $absLogDir\n";
print "Summary path: $regressLog\n" if $exeReport;
print $fh_regLog "Summary path: $regressLog\n" if $exeReport;
print "Coverage path: $absLogDir/urgReport\n" if $exeCoverage;
print $fh_regLog "Coverage path: $absLogDir/urgReport\n" if $exeCoverage;
print "Regression started at $startTimeStr, finished at $finishTimeStr. Cost: ".sprintf("%.2f",$costTime)."mins\n";
print $fh_regLog "Regression started at $startTimeStr, finished at $finishTimeStr. Cost: ".sprintf("%.2f",$costTime)."mins\n";
close $fh_regLog;
exit 254 if($regStatus != 0);
#------------------------------------------------------------
# Sub
#------------------------------------------------------------
sub ForkCMD {
my (@cmds) = @_;
my @childPids;
my $fail = 0;
foreach (@cmds) {
if(scalar @childPids >= $maxProcess) {
my $finishedPid = waitpid(-1, 0);
@childPids = grep { $_ != $finishedPid } @childPids;
my $exitCode = $? >> 8;
$fail++ if($exitCode);
}
my $cmd = $_.$null;
my $pid = fork();
if($pid) {
push @childPids, $pid;
} elsif ($pid == 0) {
my $status =0;
print "(PID $$) Command start: $cmd\n";
# print $fh_regLog "(PID $$) Command start: $cmd\n";
$status = system($cmd);
if ($status == 0) {
print "(PID $$) Command completed successfully: $cmd\n";
# print $fh_regLog "(PID $$) Command completed successfully: $cmd\n";
exit 0;
} else {
print "(PID $$) Command failed: $cmd\n";
# print $fh_regLog "(PID $$) Command failed: $cmd\n";
exit 1;
}
} else {
die "Failed to fork: $!";
}
}
foreach my $pid (@childPids) {
waitpid($pid, 0);
my $exitCode = $? >> 8;
$fail++ if($exitCode);
}
return $fail;
}
sub CreatCovReport{
}
sub CheckResult{
my ($logDir) = @_;
my $testNum = 0;
my $seed;
my $testName;
my $testPath;
my $rerunCmd;
my $passNum = 0;
my $failNum = 0;
my $unimNum = 0;
my $isPass = 0;
my $isFail = 0;
my $isUnim = 0;
my @failReport;
my @unimReport;
my @passReport;
my @summReport;
my @rerunCMDs;
my %testBucket;
my $uvmFirstFail = undef;
my $astFirstFail = undef;
# To count high discard rate tests
my $isHighDisc = 0;
my $highDiscNum = 0;
my $discardRate = 0;
my @highDiscReport;
my $p_uvmFail = qr/^(UVM_ERROR|UVM_FATAL)( \S+ | )(@\s*\d+:) (\S+) (\[\S+\]) (.*)$/;
my $p_astFail = qr/\(^Error.*\n.*\)/;
my $p_discardRate = qr/UVM_INFO.+\[FINAL_CHECK\] RVV.+discarded ([0-9.]+)%/;
foreach my $file (glob("$logDir/$scfg/*/test.log")) {
$testNum++;
$isFail = 0;
$isPass = 0;
$isUnim = 0;
open my $fh, '<', $file, or die "Open $file failed. $!";
my @texts = <$fh>;
foreach my $line (@texts) {
if($line =~ m/\+UVM_TESTNAME=(\S+)/g) { $testName = $1; }
if($line =~ m/====PASS====/g) { $passNum++; $isPass = 1; }
if($line =~ m/====FAIL====/g) { $failNum++; $isFail = 1; }
if($line =~ m/\+ntb_random_seed=(\d+)/g) { $seed = $1; }
if($line =~ m/automatic random seed used: (\d+)/g) { $seed = $1; }
if($line =~ m/$p_uvmFail/g) { $uvmFirstFail = "$1 $3 $5 $6"; }
if($line =~ m/$p_astFail/g) { $astFirstFail = $1; }
if($line =~ m/$p_discardRate/g) {
$discardRate = $1;
if($discardRate > 0.01) {
$isHighDisc = 1;
$highDiscNum++;
} else {
$isHighDisc = 0;
}
}
}
if(!$isPass && !$isFail) { $unimNum++; $isUnim = 1;}
if(grep {m/$testName/g} keys %testBucket) {
$testBucket{$testName}->{total} ++;
} else {
$testBucket{$testName} = {total => 1, pass => 0, fail => 0, unim => 0, highDisc => 0};
}
if($isPass) {
$testPath = "$file";
$rerunCmd = "sve/rvv_backend_tb/Makefile sim test=$testName seed=$seed scfg=$scfg";
push @passReport, "=========================================================\n";
push @passReport, "TEST: $testName\n";
push @passReport, "SEED: $seed\n";
push @passReport, "RERUN: $rerunCmd\n";
push @passReport, "LOG: $testPath\n";
push @passReport, "DISC-RATE: $discardRate%\n";
push @passReport, "RESULT: PASS\n";
push @passReport, "\n\n";
$testBucket{$testName}->{pass} ++;
}
if($isFail) {
$testPath = "$file";
$rerunCmd = "sve/rvv_backend_tb/Makefile sim test=$testName seed=$seed scfg=$scfg";
push @failReport, "=========================================================\n";
push @failReport, "TEST: $testName\n";
push @failReport, "SEED: $seed\n";
push @failReport, "RERUN: $rerunCmd\n";
push @failReport, "LOG: $testPath\n";
push @failReport, "DISC-RATE: $discardRate%\n";
push @failReport, "RESULT: FAIL\n";
push @failReport, "ERROR: $astFirstFail\n" if(defined $astFirstFail);
push @failReport, "ERROR: $uvmFirstFail\n" if(defined $uvmFirstFail);
push @failReport, "\n\n";
push @rerunCMDs, "$testName seed=$seed\n";
$testBucket{$testName}->{fail} ++;
}
if($isUnim) {
$testPath = "$file";
$rerunCmd = "sve/rvv_backend_tb/Makefile sim test=$testName seed=$seed scfg=$scfg";
push @unimReport, "=========================================================\n";
push @unimReport, "TEST: $testName\n";
push @unimReport, "SEED: $seed\n";
push @unimReport, "RERUN: $rerunCmd\n";
push @unimReport, "LOG: $testPath\n";
push @unimReport, "DISC-RATE: $discardRate%\n";
push @unimReport, "RESULT: UNIMPLEMENT\n";
push @unimReport, "ERROR: $astFirstFail\n" if(defined $astFirstFail);
push @unimReport, "ERROR: $uvmFirstFail\n" if(defined $uvmFirstFail);
push @unimReport, "\n\n";
push @rerunCMDs, "$testName seed=$seed\n";
$testBucket{$testName}->{unim} ++;
}
if($exeCountDiscRate && $isHighDisc) {
$testPath = "$file";
$rerunCmd = "sve/rvv_backend_tb/Makefile sim test=$testName seed=$seed scfg=$scfg";
push @highDiscReport, "=========================================================\n";
push @highDiscReport, "TEST: $testName\n";
push @highDiscReport, "SEED: $seed\n";
push @highDiscReport, "RERUN: $rerunCmd\n";
push @highDiscReport, "LOG: $testPath\n";
push @highDiscReport, "DISC-RATE: $discardRate%\n";
push @highDiscReport, "RESULT: HIGH-DISCARD\n";
push @highDiscReport, "\n\n";
$testBucket{$testName}->{highDisc} ++;
}
close $fh;
}
push @summReport, "\n";
foreach my $test (keys %testBucket) {
push @summReport, "-------\n";
push @summReport, "$test\n";
push @summReport, "Total Tests: $testBucket{$test}->{total}\n";
push @summReport, "Pass Tests: $testBucket{$test}->{pass}(".sprintf("%.2f%%",100*$testBucket{$test}->{pass}/$testBucket{$test}->{total}).")\n";
push @summReport, "Fail Tests: $testBucket{$test}->{fail}(".sprintf("%.2f%%",100*$testBucket{$test}->{fail}/$testBucket{$test}->{total}).")\n";
push @summReport, "Unim Tests: $testBucket{$test}->{unim}(".sprintf("%.2f%%",100*$testBucket{$test}->{unim}/$testBucket{$test}->{total}).")\n";
push @summReport, "High-discard Tests: $testBucket{$test}->{highDisc}(".sprintf("%.2f%%",100*$testBucket{$test}->{highDisc}/$testBucket{$test}->{total}).")\n" if $exeCountDiscRate;
}
push @summReport, "\n";
push @summReport, "Summary\n";
push @summReport, "-------\n";
push @summReport, "Total Tests: $testNum\n";
push @summReport, "Pass Tests: $passNum(".sprintf("%.2f%%",100*$passNum/$testNum).")\n";
push @summReport, "Fail Tests: $failNum(".sprintf("%.2f%%",100*$failNum/$testNum).")\n";
push @summReport, "Unimplemented Tests: $unimNum(".sprintf("%.2f%%",100*$unimNum/$testNum).")\n";
push @summReport, "High discard Tests: $highDiscNum(".sprintf("%.2f%%",100*$highDiscNum/$testNum).")\n" if $exeCountDiscRate;
push @summReport, "\n\n";
my $reportLog = "$logDir/$scfg/report.log";
my $rerunList = "./regress_rerun.list";
# full report
open my $fh_rptLog , '+>', $reportLog , or die "Open $reportLog failed. $! ";
print $fh_rptLog "\n** PASS TESTS : $passNum *****************************************************\n";
print $fh_rptLog @passReport;
print $fh_rptLog "\n** FAIL TESTS : $failNum *****************************************************\n";
print $fh_rptLog @failReport;
print $fh_rptLog "\n** UNIM TESTS : $unimNum *****************************************************\n";
print $fh_rptLog @unimReport;
print $fh_rptLog "\n** HIGH DISCARD TESTS : $highDiscNum *****************************************\n" if $exeCountDiscRate;
print $fh_rptLog @highDiscReport if $exeCountDiscRate;
print $fh_rptLog @summReport;
close $fh_rptLog;
# summary regress report
print $fh_regLog "\n** FAIL TESTS : $failNum *****************************************************\n";
print $fh_regLog @failReport;
print $fh_regLog "\n** UNIM TESTS : $unimNum *****************************************************\n";
print $fh_regLog @unimReport;
print $fh_regLog @summReport;
print STDOUT @summReport;
# gen rerun list
open my $fh_rerun , '+>', $rerunList, or die "Open $rerunList failed. $! ";
print $fh_rerun @rerunCMDs;
close $fh_rerun;
return $failNum;
}
sub Usage {
die <<EOU;
Help infomation:
------------- In LSF server ------------
Run in openlava (Recommand):
bsb regress -lsf -que <que-name> [options...]
------------ Normal server -------------
Local space
regress -loc [options...]
Multi-process mode (Recommand):
regress [options...]
----------- Useful commands ------------
bsb regress -lsf -que <que-name> -rep 100
regress -rep 100
Collect coverage:
bsb regress -lsf -que <que-name> -rep 100 -cov
regress -rep 100 -cov
Only generate report:
regress -report -logdir <dir>
Rerun:
bsb regress -lsf -que <que-name> -list regress_rerun.list -logdir <dir>
Options:
-help,-h Help Info.
-lsf Use openlava to run test.
-local,-loc Run command in local workspace one-by-one.
-queue,-que <str> Specify openlava execution queue.
Default local queue is ZSP_debug. Default regress queue is ZSP_regression.
-max_job <num> Max openlava jobs per bsub command.
Default is 30.
-repeat,-rep <num> Test repeat number.
-testlist,-testlist <file> Specify testlist file.
Default is ./regress.list
-logdir,-dir <dir> Specify outfile direction. (Relative address)
Default is ./outfiles_regress/mon_mday_hourmin.
-show Show all commands.
-report Only generate report.
-no_compile,-noc Run simulation with out compile.
-only_compile,-oc Only compile.
-cov,-coverage Open coverage collection.
-shuffle Shuffle tests.
-qualify, -q Run qualify tests.
EOU
}