2006-08-26 11:35:28 +00:00
|
|
|
#!/usr/bin/perl -w
|
2009-01-21 21:56:50 +00:00
|
|
|
# See copyright, etc in below POD section.
|
2006-08-26 11:35:28 +00:00
|
|
|
######################################################################
|
|
|
|
|
|
|
|
require 5.006_001;
|
|
|
|
BEGIN {
|
|
|
|
if (my $Project=($ENV{DIRPROJECT}||$ENV{PROJECT})) {
|
|
|
|
# Magic to allow author testing of perl packages in local directory
|
|
|
|
require "$Project/hw/utils/perltools/boot.pl";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
use Getopt::Long;
|
|
|
|
use IO::File;
|
|
|
|
use Pod::Usage;
|
|
|
|
use Data::Dumper;
|
|
|
|
use strict;
|
|
|
|
use vars qw ($Debug %Vars $Driver $Fork);
|
2008-09-23 14:02:31 +00:00
|
|
|
use POSIX qw(strftime);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
$::Driver = 1;
|
|
|
|
|
2007-05-16 18:19:23 +00:00
|
|
|
eval "use Parallel::Forker; \$Fork=Parallel::Forker->new(use_sig_child=>1);";
|
|
|
|
$Fork = Forker->new(use_sig_child=>1) if !$Fork;
|
2006-08-26 11:35:28 +00:00
|
|
|
$SIG{CHLD} = sub { $Fork->sig_child() if $Fork; };
|
|
|
|
$SIG{TERM} = sub { $Fork->kill_tree_all('TERM') if $Fork; die "Quitting...\n"; };
|
|
|
|
|
|
|
|
#======================================================================
|
|
|
|
|
|
|
|
#======================================================================
|
|
|
|
# main
|
|
|
|
|
|
|
|
autoflush STDOUT 1;
|
|
|
|
autoflush STDERR 1;
|
|
|
|
|
|
|
|
our @Orig_ARGV = @ARGV;
|
|
|
|
our @Orig_ARGV_Sw; foreach (@Orig_ARGV) { push @Orig_ARGV_Sw, $_ if /^-/ && !/^-j/; }
|
|
|
|
|
|
|
|
$Debug = 0;
|
2008-11-21 19:38:12 +00:00
|
|
|
my $opt_benchmark;
|
2006-08-26 11:35:28 +00:00
|
|
|
my @opt_tests;
|
|
|
|
my $opt_nc;
|
|
|
|
my $opt_vcs;
|
|
|
|
my $opt_v3;
|
|
|
|
my $opt_stop;
|
|
|
|
my $opt_optimize;
|
|
|
|
my $opt_gdb;
|
|
|
|
my $opt_jobs = 1;
|
2006-12-19 14:09:57 +00:00
|
|
|
my $opt_verbose;
|
2006-08-26 11:35:28 +00:00
|
|
|
my $Opt_Verilated_Debug;
|
2009-01-21 21:56:50 +00:00
|
|
|
our @Opt_Driver_Verilator_Flags;
|
|
|
|
|
|
|
|
Getopt::Long::config ("pass_through");
|
2006-08-26 11:35:28 +00:00
|
|
|
if (! GetOptions (
|
|
|
|
"help" => \&usage,
|
|
|
|
"debug" => \&debug,
|
|
|
|
"vcs!" => \$opt_vcs,
|
|
|
|
"verilated_debug!" => \$Opt_Verilated_Debug,
|
|
|
|
"j=i" => \$opt_jobs,
|
|
|
|
"v3!" => \$opt_v3,
|
|
|
|
"nc!" => \$opt_nc,
|
2008-11-21 19:38:12 +00:00
|
|
|
"benchmark:i" => sub { $opt_benchmark = $_[1] ? $_[1] : 1; },
|
2006-08-26 11:35:28 +00:00
|
|
|
"gdb!" => \$opt_gdb,
|
|
|
|
"optimize:s" => \$opt_optimize,
|
2008-11-21 19:38:12 +00:00
|
|
|
"stop!" => \$opt_stop,
|
2006-12-19 14:09:57 +00:00
|
|
|
"verbose!" => \$opt_verbose,
|
2006-08-26 11:35:28 +00:00
|
|
|
"<>" => \¶meter,
|
|
|
|
)) {
|
2009-01-21 21:56:50 +00:00
|
|
|
die "%Error: Bad usage, try '$0 --help'\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2008-09-23 14:02:31 +00:00
|
|
|
|
|
|
|
$opt_jobs = calc_jobs() if defined $opt_jobs && $opt_jobs==0;
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
$Fork->max_proc($opt_jobs);
|
|
|
|
|
|
|
|
if (!$opt_vcs && !$opt_nc && !$opt_v3) {
|
|
|
|
$opt_v3 = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($#opt_tests<0) {
|
2008-09-23 14:02:31 +00:00
|
|
|
push @opt_tests, glob ("t/t_*.pl");
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mkdir "obj_dir";
|
|
|
|
mkdir "logs";
|
|
|
|
|
|
|
|
my $okcnt=0; my $failcnt=0;
|
|
|
|
my @fails;
|
|
|
|
|
|
|
|
foreach my $testpl (@opt_tests) {
|
|
|
|
one_test(pl_filename => $testpl, vcs=>1) if $opt_vcs;
|
|
|
|
one_test(pl_filename => $testpl, nc=>1) if $opt_nc;
|
|
|
|
one_test(pl_filename => $testpl, 'v3'=>1) if $opt_v3;
|
|
|
|
}
|
|
|
|
|
|
|
|
$Fork->wait_all(); # Wait for all children to finish
|
|
|
|
|
|
|
|
sub one_test {
|
|
|
|
my @params = @_;
|
|
|
|
$Fork->schedule
|
|
|
|
(
|
|
|
|
run_on_start => sub {
|
|
|
|
print ("="x70,"\n");
|
|
|
|
my $test = new VTest(@params);
|
|
|
|
$test->oprint("="x50,"\n");
|
|
|
|
unlink $test->{status_filename};
|
2008-11-25 02:38:45 +00:00
|
|
|
$test->prep;
|
2006-08-26 11:35:28 +00:00
|
|
|
$test->read;
|
|
|
|
if ($test->ok) {
|
|
|
|
$test->oprint("Test PASSED\n");
|
|
|
|
} else {
|
|
|
|
$test->error("Missing ok\n") if !$test->errors;
|
|
|
|
$test->oprint("%Error: $test->{errors}\n");
|
|
|
|
}
|
|
|
|
$test->write_status;
|
|
|
|
},
|
|
|
|
run_on_finish => sub {
|
|
|
|
my $test = new VTest(@params);
|
|
|
|
$test->read_status;
|
|
|
|
if ($test->ok) {
|
|
|
|
$okcnt++;
|
|
|
|
} else {
|
|
|
|
$test->oprint("FAILED: ","*"x60,"\n");
|
|
|
|
push @fails, "\t#".$test->soprint("%Error: $test->{errors}\n");
|
2006-09-05 20:06:23 +00:00
|
|
|
my $j = ($opt_jobs>1?" -j 2":"");
|
2008-09-23 14:02:31 +00:00
|
|
|
push @fails, "\t\tmake$j && test_regress/"
|
|
|
|
.$test->{pl_filename}." ".join(' ',@Orig_ARGV_Sw)."\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
$failcnt++;
|
|
|
|
if ($opt_stop) { die "%Error: --stop and errors found\n"; }
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)->ready();
|
|
|
|
}
|
|
|
|
|
2008-09-23 14:02:31 +00:00
|
|
|
report(\@fails, undef);
|
|
|
|
report(\@fails, "obj_dir/driver_".strftime("%Y%m%d_%H%M%S.log", localtime));
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
exit(10) if $failcnt;
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
|
|
|
sub usage {
|
|
|
|
pod2usage(-verbose=>2, -exitval => 2);
|
|
|
|
exit (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
sub debug {
|
|
|
|
$Debug = 1;
|
2009-01-21 21:56:50 +00:00
|
|
|
push @Opt_Driver_Verilator_Flags, "--debug";
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
2009-01-21 21:56:50 +00:00
|
|
|
our $_Parameter_Next_Level;
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
sub parameter {
|
|
|
|
my $param = shift;
|
2009-01-21 21:56:50 +00:00
|
|
|
if ($_Parameter_Next_Level) {
|
|
|
|
($param =~ /^(\d+)$/)
|
|
|
|
or die "%Error: Expected number following $_Parameter_Next_Level: $param\n";
|
|
|
|
push @Opt_Driver_Verilator_Flags, $param;
|
|
|
|
$_Parameter_Next_Level = undef;
|
|
|
|
}
|
|
|
|
elsif ($param =~ /\.pl/) {
|
2006-08-26 11:35:28 +00:00
|
|
|
push @opt_tests, $param;
|
2009-01-21 21:56:50 +00:00
|
|
|
}
|
|
|
|
elsif ($param =~ /^--debugi/) {
|
|
|
|
push @Opt_Driver_Verilator_Flags, $param;
|
|
|
|
$_Parameter_Next_Level = $param;
|
|
|
|
}
|
|
|
|
else {
|
2009-04-24 14:32:11 +00:00
|
|
|
die "%Error: Unknown parameter: $param\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
}
|
2008-06-10 01:25:10 +00:00
|
|
|
|
2008-09-23 14:02:31 +00:00
|
|
|
sub calc_jobs {
|
|
|
|
my $ok = eval "
|
|
|
|
use Unix::Processors;
|
|
|
|
return Unix::Processors->new->max_online;
|
|
|
|
";
|
|
|
|
$ok && !$@ or die "%Error: Can't use -j: $@\n";
|
|
|
|
print "driver.pl: Found $ok cores, using -j ",$ok+1,"\n";
|
|
|
|
return $ok + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub report {
|
|
|
|
my $fails = shift;
|
|
|
|
my $filename = shift;
|
|
|
|
|
|
|
|
my $fh = \*STDOUT;
|
|
|
|
if ($filename) {
|
|
|
|
$fh = IO::File->new(">$filename") or die "%Error: $! writing $filename,";
|
|
|
|
}
|
|
|
|
|
|
|
|
$fh->print("\n");
|
|
|
|
$fh->print("="x70,"\n");
|
|
|
|
$fh->print("TESTS Passed $okcnt Failed $failcnt\n");
|
|
|
|
foreach my $f (@$fails) {
|
|
|
|
chomp $f;
|
|
|
|
$fh->print("$f\n");
|
|
|
|
}
|
|
|
|
$fh->print("TESTS Passed $okcnt Failed $failcnt\n");
|
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
#######################################################################
|
|
|
|
#######################################################################
|
|
|
|
#######################################################################
|
|
|
|
#######################################################################
|
|
|
|
# Test class
|
|
|
|
|
|
|
|
package VTest;
|
|
|
|
use Data::Dumper;
|
|
|
|
use Carp;
|
|
|
|
use Cwd;
|
|
|
|
|
2008-11-25 02:38:45 +00:00
|
|
|
use vars qw ($Self $Self);
|
2006-08-26 11:35:28 +00:00
|
|
|
use strict;
|
|
|
|
|
|
|
|
sub new {
|
|
|
|
my $class = shift;
|
2008-11-25 02:38:45 +00:00
|
|
|
my $self = {@_};
|
|
|
|
|
|
|
|
$self->{name} ||= $1 if $self->{pl_filename} =~ m!.*/([^/]*)\.pl$!;
|
|
|
|
$self->{obj_dir} ||= "obj_dir/$self->{name}";
|
|
|
|
$self->{t_dir} ||= cwd()."/t"; # Used both absolutely and under obj_dir
|
|
|
|
|
|
|
|
$self = {
|
2006-08-26 11:35:28 +00:00
|
|
|
name => undef, # Set below, name of this test
|
|
|
|
mode => "",
|
|
|
|
pl_filename => undef, # Name of .pl file to get setup from
|
|
|
|
make_top_shell => 1, # Make a default __top.v file
|
|
|
|
make_main => 1, # Make __main.cpp
|
2008-11-19 00:37:26 +00:00
|
|
|
sim_time => 1000,
|
2008-11-21 19:38:12 +00:00
|
|
|
benchmark => $opt_benchmark,
|
2006-08-26 11:35:28 +00:00
|
|
|
# All compilers
|
2006-12-19 14:09:57 +00:00
|
|
|
v_flags => [split(/\s+/,(" -f input.vc --debug-check"
|
2007-01-17 21:19:29 +00:00
|
|
|
.($opt_verbose ? " +define+TEST_VERBOSE=1":"")
|
2008-11-21 19:38:12 +00:00
|
|
|
.($opt_benchmark ? " +define+TEST_BENCHMARK=$opt_benchmark":"")
|
2006-12-19 14:09:57 +00:00
|
|
|
))],
|
2006-08-26 11:35:28 +00:00
|
|
|
v_flags2 => [], # Overridden in some sim files
|
|
|
|
v_other_filenames => [], # After the filename so we can spec multiple files
|
|
|
|
# VCS
|
|
|
|
vcs => 0,
|
|
|
|
vcs_flags => [split(/\s+/,"+cli -I +define+vcs+1 -q +v2k")],
|
2007-01-26 14:59:30 +00:00
|
|
|
vcs_flags2 => [], # Overridden in some sim files
|
2006-08-26 11:35:28 +00:00
|
|
|
# NC
|
|
|
|
nc => 0,
|
2008-11-21 19:38:12 +00:00
|
|
|
nc_flags => [split(/\s+/,"+licqueue +nowarn+LIBNOU +define+nc=1 -q +assert +sv -c")],
|
2007-01-26 14:59:30 +00:00
|
|
|
nc_flags2 => [], # Overridden in some sim files
|
2008-11-21 19:38:12 +00:00
|
|
|
ncrun_flags => [split(/\s+/,"+licqueue -q +assert +sv -R")],
|
2006-08-26 11:35:28 +00:00
|
|
|
# Verilator
|
|
|
|
'v3' => 0,
|
2008-12-05 15:54:14 +00:00
|
|
|
verilator_flags => ["-cc",
|
|
|
|
"-Mdir $self->{obj_dir}"],
|
2007-04-19 18:20:16 +00:00
|
|
|
verilator_flags2 => [],
|
2006-08-26 11:35:28 +00:00
|
|
|
verilator_make_gcc => 1,
|
2007-01-31 21:49:13 +00:00
|
|
|
verilated_debug => $Opt_Verilated_Debug,
|
2006-08-26 11:35:28 +00:00
|
|
|
stdout_filename => undef, # Redirect stdout
|
2008-11-25 02:38:45 +00:00
|
|
|
%$self};
|
2006-08-26 11:35:28 +00:00
|
|
|
bless $self, $class;
|
|
|
|
|
|
|
|
$self->{mode} ||= "vcs" if $self->{vcs};
|
|
|
|
$self->{mode} ||= "v3" if $self->{v3};
|
|
|
|
$self->{mode} ||= "nc" if $self->{nc};
|
|
|
|
$self->{VM_PREFIX} ||= "V".$self->{name};
|
2008-11-25 02:38:45 +00:00
|
|
|
$self->{stats} ||= "$self->{obj_dir}/V".$self->{name}."__stats.txt";
|
|
|
|
$self->{status_filename} ||= "$self->{obj_dir}/V".$self->{name}.".status";
|
|
|
|
$self->{run_log_filename} ||= "$self->{obj_dir}/vl_sim.log";
|
2008-12-12 20:34:02 +00:00
|
|
|
$self->{coverage_filename} ||= "$self->{obj_dir}/vl_coverage.pl";
|
2006-08-26 11:35:28 +00:00
|
|
|
($self->{top_filename} = $self->{pl_filename}) =~ s/\.pl$/\.v/;
|
|
|
|
if (!$self->{make_top_shell}) {
|
|
|
|
$self->{top_shell_filename} = $self->{top_filename};
|
|
|
|
} else {
|
2008-11-25 02:38:45 +00:00
|
|
|
$self->{top_shell_filename} = "$self->{obj_dir}/$self->{VM_PREFIX}__top.v";
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
return $self;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub soprint {
|
|
|
|
my $self = shift;
|
|
|
|
my $str = "$self->{mode}/$self->{name}: ".join('',@_);
|
|
|
|
$str =~ s/\n\n+$/\n/s;
|
|
|
|
return $str;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub oprint {
|
|
|
|
my $self = shift;
|
|
|
|
print $self->soprint(@_);
|
|
|
|
}
|
|
|
|
|
|
|
|
sub error {
|
|
|
|
my $self = shift;
|
|
|
|
my $msg = join('',@_);
|
|
|
|
warn "%Warning: $self->{mode}/$self->{name}: ".$msg."\n";
|
|
|
|
$self->{errors} ||= $msg;
|
|
|
|
}
|
|
|
|
|
2008-06-12 00:33:53 +00:00
|
|
|
sub skip {
|
|
|
|
my $self = shift;
|
|
|
|
my $msg = join('',@_);
|
|
|
|
warn "%Warning: Skip: $self->{mode}/$self->{name}: ".$msg."\n";
|
|
|
|
$self->{errors} ||= "Skip: ".$msg;
|
|
|
|
}
|
|
|
|
|
2008-11-25 02:38:45 +00:00
|
|
|
sub prep {
|
|
|
|
my $self = shift;
|
|
|
|
mkdir $self->{obj_dir}; # Ok if already exists
|
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
sub read {
|
|
|
|
my $self = shift;
|
|
|
|
# Read the control file
|
|
|
|
(-r $self->{pl_filename})
|
|
|
|
or return $self->error("Can't open $self->{pl_filename}\n");
|
2008-11-25 02:38:45 +00:00
|
|
|
$Self = $self;
|
|
|
|
$Self = $self;
|
2006-08-26 11:35:28 +00:00
|
|
|
delete $INC{$self->{pl_filename}};
|
|
|
|
require $self->{pl_filename};
|
|
|
|
}
|
|
|
|
|
|
|
|
sub write_status {
|
|
|
|
my $self = shift;
|
|
|
|
my $filename = $self->{status_filename};
|
2006-09-13 14:38:48 +00:00
|
|
|
my $fh = IO::File->new(">$filename") or die "%Error: $! $filename,";
|
2006-08-26 11:35:28 +00:00
|
|
|
print $fh Dumper($self);
|
|
|
|
print $fh "1;";
|
|
|
|
$fh->close();
|
|
|
|
}
|
|
|
|
|
|
|
|
sub read_status {
|
|
|
|
my $self = shift;
|
|
|
|
my $filename = $self->{status_filename};
|
|
|
|
use vars qw($VAR1);
|
|
|
|
local $VAR1;
|
|
|
|
require $filename or die "%Error: $! $filename,";
|
|
|
|
%{$self} = %{$VAR1};
|
|
|
|
}
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
# Methods invoked by tests
|
|
|
|
|
|
|
|
sub compile {
|
2008-11-25 02:38:45 +00:00
|
|
|
my $self = (ref $_[0]? shift : $Self);
|
2006-08-26 11:35:28 +00:00
|
|
|
my %param = (%{$self}, @_); # Default arguments are from $self
|
|
|
|
return 1 if $self->errors;
|
|
|
|
$self->oprint("Compile\n");
|
|
|
|
|
2008-12-05 15:54:14 +00:00
|
|
|
my $checkflags = join(' ',@{$param{v_flags}},
|
|
|
|
@{$param{v_flags2}},
|
|
|
|
@{$param{verilator_flags}},
|
|
|
|
@{$param{verilator_flags2}});
|
|
|
|
$self->{sc} = 1 if ($checkflags =~ /-sc\b/);
|
|
|
|
$self->{sp} = 1 if ($checkflags =~ /-sp\b/);
|
|
|
|
$self->{trace} = 1 if ($checkflags =~ /-trace\b/);
|
|
|
|
$self->{coverage} = 1 if ($checkflags =~ /-coverage\b/);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
if ($param{vcs}) {
|
|
|
|
$self->_make_top();
|
2008-11-25 02:38:45 +00:00
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/vcs_compile.log",
|
2006-08-26 11:35:28 +00:00
|
|
|
fails=>$param{fails},
|
2007-03-13 17:15:00 +00:00
|
|
|
cmd=>[($ENV{VERILATOR_VCS}||"vcs"),
|
2006-08-26 11:35:28 +00:00
|
|
|
@{$param{vcs_flags}},
|
2007-01-26 14:59:30 +00:00
|
|
|
@{$param{vcs_flags2}},
|
2006-08-26 11:35:28 +00:00
|
|
|
@{$param{v_flags}},
|
|
|
|
@{$param{v_flags2}},
|
|
|
|
$param{top_filename},
|
|
|
|
$param{top_shell_filename},
|
|
|
|
@{$param{v_other_filenames}},
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
if ($param{nc}) {
|
|
|
|
$self->_make_top();
|
2008-11-25 02:38:45 +00:00
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/nc_compile.log",
|
2006-08-26 11:35:28 +00:00
|
|
|
fails=>$param{fails},
|
2007-03-13 17:15:00 +00:00
|
|
|
cmd=>[($ENV{VERILATOR_NCVERILOG}||"ncverilog"),
|
2006-08-26 11:35:28 +00:00
|
|
|
@{$param{nc_flags}},
|
2007-01-26 14:59:30 +00:00
|
|
|
@{$param{nc_flags2}},
|
2006-08-26 11:35:28 +00:00
|
|
|
@{$param{v_flags}},
|
|
|
|
@{$param{v_flags2}},
|
|
|
|
$param{top_filename},
|
|
|
|
$param{top_shell_filename},
|
|
|
|
@{$param{v_other_filenames}},
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
if ($param{v3}) {
|
|
|
|
$opt_gdb="gdbrun" if defined $opt_gdb;
|
2008-07-16 17:31:21 +00:00
|
|
|
my @verilator_flags = @{$param{verilator_flags}};
|
|
|
|
unshift @verilator_flags, "--gdb $opt_gdb" if $opt_gdb;
|
2009-01-21 21:56:50 +00:00
|
|
|
unshift @verilator_flags, @Opt_Driver_Verilator_Flags;
|
2008-07-16 17:31:21 +00:00
|
|
|
unshift @verilator_flags, "--x-assign unique"; # More likely to be buggy
|
|
|
|
# unshift @verilator_flags, "--trace";
|
2006-08-26 11:35:28 +00:00
|
|
|
if (defined $opt_optimize) {
|
|
|
|
my $letters = "";
|
|
|
|
if ($opt_optimize =~ /[a-zA-Z]/) {
|
|
|
|
$letters = $opt_optimize;
|
|
|
|
} else { # Randomly turn on/off different optimizations
|
|
|
|
foreach my $l ('a'..'z') {
|
|
|
|
$letters .= ((rand() > 0.5) ? $l : uc $l);
|
|
|
|
}
|
2008-07-16 17:31:21 +00:00
|
|
|
unshift @verilator_flags, "--trace" if rand() > 0.5;
|
|
|
|
unshift @verilator_flags, "--coverage" if rand() > 0.5;
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2008-07-16 17:31:21 +00:00
|
|
|
unshift @verilator_flags, "--O".$letters;
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
my @v3args = ("perl","../bin/verilator",
|
|
|
|
"--prefix ".$self->{VM_PREFIX},
|
2008-07-16 17:31:21 +00:00
|
|
|
@verilator_flags,
|
2007-04-19 18:20:16 +00:00
|
|
|
@{$param{verilator_flags2}},
|
2006-08-26 11:35:28 +00:00
|
|
|
@{$param{v_flags}},
|
|
|
|
@{$param{v_flags2}},
|
|
|
|
$param{top_filename},
|
|
|
|
@{$param{v_other_filenames}},
|
|
|
|
($param{stdout_filename}?"> ".$param{stdout_filename}:""),
|
|
|
|
);
|
2007-12-13 13:54:04 +00:00
|
|
|
if ($self->sc_or_sp && !defined $ENV{SYSTEMC}) {
|
2006-08-26 11:35:28 +00:00
|
|
|
$self->error("Test requires SystemC; ignore error since not installed\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2008-11-25 02:38:45 +00:00
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/vl_compile.log",
|
2006-08-26 11:35:28 +00:00
|
|
|
fails=>$param{fails},
|
|
|
|
expect=>$param{expect},
|
|
|
|
cmd=>\@v3args);
|
|
|
|
return 1 if $self->errors;
|
|
|
|
|
|
|
|
if (!$param{fails} && $param{verilator_make_gcc}) {
|
|
|
|
if ($param{make_main}) {
|
|
|
|
$self->_make_main();
|
|
|
|
}
|
|
|
|
if ($self->sp) {
|
|
|
|
$self->_sp_preproc(%param);
|
|
|
|
}
|
|
|
|
$self->oprint("GCC\n");
|
2008-11-25 02:38:45 +00:00
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/vl_gcc.log",
|
|
|
|
cmd=>["cd $self->{obj_dir} && ",
|
2006-08-26 11:35:28 +00:00
|
|
|
"make", "-f".getcwd()."/Makefile_obj",
|
|
|
|
"VM_PREFIX=$self->{VM_PREFIX}",
|
2009-03-14 02:58:55 +00:00
|
|
|
"CPPFLAGS_DRIVER=-D".uc($self->{name}),
|
2006-08-26 11:35:28 +00:00
|
|
|
($param{make_main}?"":"MAKE_MAIN=0"),
|
2008-12-30 19:34:01 +00:00
|
|
|
($param{benchmark}?"OPT_FAST=-O2":""),
|
|
|
|
"$self->{VM_PREFIX}", # bypass default rule, as we don't need archive
|
2006-08-30 19:50:24 +00:00
|
|
|
($param{make_flags}||""),
|
2006-08-26 11:35:28 +00:00
|
|
|
]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub execute {
|
2008-11-25 02:38:45 +00:00
|
|
|
my $self = (ref $_[0]? shift : $Self);
|
2006-08-26 11:35:28 +00:00
|
|
|
return 1 if $self->errors;
|
|
|
|
my %param = (%{$self}, @_); # Default arguments are from $self
|
|
|
|
$self->oprint("Run\n");
|
2008-11-21 19:38:12 +00:00
|
|
|
if ($param{nc}) {
|
2008-11-25 02:38:45 +00:00
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/nc_sim.log",
|
2008-11-21 19:38:12 +00:00
|
|
|
fails=>$param{fails},
|
|
|
|
cmd=>[($ENV{VERILATOR_NCVERILOG}||"ncverilog"),
|
|
|
|
@{$param{ncrun_flags}},
|
|
|
|
]);
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
if ($param{vcs}) {
|
2006-09-13 14:38:48 +00:00
|
|
|
#my $fh = IO::File->new(">simv.key") or die "%Error: $! simv.key,";
|
2006-08-26 11:35:28 +00:00
|
|
|
#$fh->print("quit\n"); $fh->close;
|
2008-11-25 02:38:45 +00:00
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/vcs_sim.log",
|
2006-08-26 11:35:28 +00:00
|
|
|
cmd=>["./simv",],
|
|
|
|
%param,
|
|
|
|
expect=>undef, # vcs expect isn't the same
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if ($param{v3}
|
|
|
|
#&& (!$param{needs_v4} || -r "$ENV{VERILATOR_ROOT}/src/V3Gate.cpp")
|
|
|
|
) {
|
2008-11-25 02:38:45 +00:00
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/vl_sim.log",
|
|
|
|
cmd=>["$self->{obj_dir}/$param{VM_PREFIX}",
|
2006-08-26 11:35:28 +00:00
|
|
|
],
|
|
|
|
%param,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-05 15:54:14 +00:00
|
|
|
sub inline_checks {
|
|
|
|
my $self = (ref $_[0]? shift : $Self);
|
|
|
|
return 1 if $self->errors;
|
2008-12-10 22:10:03 +00:00
|
|
|
return 1 if !$self->{v3};
|
|
|
|
|
2008-12-05 15:54:14 +00:00
|
|
|
my %param = (%{$self}, @_); # Default arguments are from $self
|
|
|
|
|
|
|
|
my $covfn = $Self->{coverage_filename};
|
|
|
|
my $contents = $self->file_contents($covfn);
|
|
|
|
|
|
|
|
$self->oprint("Extract checks\n");
|
|
|
|
my $fh = IO::File->new("<$self->{top_filename}");
|
|
|
|
while (defined(my $line = $fh->getline)) {
|
|
|
|
if ($line =~ /CHECK/) {
|
2008-12-12 20:34:02 +00:00
|
|
|
if ($line =~ /CHECK_COVER *\( *([---0-9]+) *, *"([^"]+)" *, *("([^"]+)" *,|) *(\d+) *\)/) {
|
|
|
|
my $lineno=($. + $1); my $hier=$2; my $comment=$4; my $count=$5;
|
2008-12-05 15:54:14 +00:00
|
|
|
my $regexp = "\001l\002".$lineno;
|
2008-12-12 20:34:02 +00:00
|
|
|
$regexp .= ".*\001o\002".quotemeta($comment) if $comment;
|
2008-12-05 15:54:14 +00:00
|
|
|
$regexp .= ".*\001h\002".quotemeta($hier);
|
|
|
|
$regexp .= ".*' ".$count;
|
|
|
|
if ($contents !~ /$regexp/) {
|
|
|
|
$self->error("CHECK_COVER: $covfn: Regexp not found: $regexp\n".
|
|
|
|
"From $self->{top_filename}:$.: $line");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
elsif ($line =~ /CHECK_COVER_MISSING *\( *([---0-9]+) *\)/) {
|
|
|
|
my $lineno=($. + $1);
|
|
|
|
my $regexp = "\001l\002".$lineno;
|
|
|
|
if ($contents =~ /$regexp/) {
|
|
|
|
$self->error("CHECK_COVER_MISSING: $covfn: Regexp found: $regexp\n".
|
|
|
|
"From $self->{top_filename}:$.: $line");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$self->error("$self->{top_filename}:$.: Unknown CHECK request: $line");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$fh->close;
|
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
#----------------------------------------------------------------------
|
|
|
|
# Accessors
|
|
|
|
|
|
|
|
sub ok {
|
2008-11-25 02:38:45 +00:00
|
|
|
my $self = (ref $_[0]? shift : $Self);
|
2006-08-26 11:35:28 +00:00
|
|
|
$self->{ok} = $_[0] if defined $_[0];
|
|
|
|
$self->{ok} = 0 if $self->{errors};
|
|
|
|
return $self->{ok};
|
|
|
|
}
|
|
|
|
|
|
|
|
sub errors {
|
2008-11-25 02:38:45 +00:00
|
|
|
my $self = (ref $_[0]? shift : $Self);
|
2006-08-26 11:35:28 +00:00
|
|
|
return $self->{errors};
|
|
|
|
}
|
|
|
|
|
|
|
|
sub top_filename {
|
2008-11-25 02:38:45 +00:00
|
|
|
my $self = (ref $_[0]? shift : $Self);
|
2006-08-26 11:35:28 +00:00
|
|
|
$self->{top_filename} = shift if defined $_[0];
|
|
|
|
return $self->{top_filename};
|
|
|
|
}
|
|
|
|
|
|
|
|
sub sp {
|
2008-11-25 02:38:45 +00:00
|
|
|
my $self = (ref $_[0]? shift : $Self);
|
2006-08-26 11:35:28 +00:00
|
|
|
return $self->{sp};
|
|
|
|
}
|
|
|
|
|
2007-12-13 13:54:04 +00:00
|
|
|
sub sc {
|
2008-11-25 02:38:45 +00:00
|
|
|
my $self = (ref $_[0]? shift : $Self);
|
2007-12-13 13:54:04 +00:00
|
|
|
return $self->{sc};
|
|
|
|
}
|
|
|
|
|
|
|
|
sub sc_or_sp {
|
|
|
|
return sc($_[0]) || sp($_[0]);
|
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
|
|
|
sub _run {
|
2008-11-25 02:38:45 +00:00
|
|
|
my $self = (ref $_[0]? shift : $Self);
|
2008-03-18 20:26:37 +00:00
|
|
|
my %param = (tee=>1,
|
|
|
|
@_);
|
2006-08-26 11:35:28 +00:00
|
|
|
my $command = join(' ',@{$param{cmd}});
|
2008-11-21 19:38:12 +00:00
|
|
|
$command = "time $command" if $opt_benchmark;
|
2008-11-05 15:23:03 +00:00
|
|
|
print "\t$command";
|
|
|
|
print " > $param{logfile}" if $param{logfile};
|
|
|
|
print "\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
if ($param{logfile}) {
|
|
|
|
open(SAVEOUT, ">&STDOUT") or die "%Error: Can't dup stdout";
|
|
|
|
open(SAVEERR, ">&STDERR") or die "%Error: Can't dup stderr";
|
|
|
|
if (0) {close(SAVEOUT); close(SAVEERR);} # Prevent unused warning
|
2008-03-18 20:26:37 +00:00
|
|
|
if ($param{tee}) {
|
|
|
|
open(STDOUT, "|tee $param{logfile}") or die "%Error: Can't redirect stdout";
|
|
|
|
} else {
|
|
|
|
open(STDOUT, ">$param{logfile}") or die "%Error: Can't open $param{logfile}";
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
open(STDERR, ">&STDOUT") or die "%Error: Can't dup stdout";
|
|
|
|
autoflush STDOUT 1;
|
|
|
|
autoflush STDERR 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
system "$command";
|
|
|
|
my $status = $?;
|
|
|
|
flush STDOUT;
|
|
|
|
flush STDERR;
|
|
|
|
|
|
|
|
if ($param{logfile}) {
|
|
|
|
open (STDOUT, ">&SAVEOUT");
|
|
|
|
open (STDERR, ">&SAVEERR");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$param{fails} && $status) {
|
|
|
|
$self->error("Exec of $param{cmd}[0] failed\n");
|
|
|
|
}
|
|
|
|
if ($param{fails} && $status) {
|
|
|
|
print "(Exec expected to fail, and did.)\n";
|
|
|
|
}
|
|
|
|
if ($param{fails} && !$status) {
|
|
|
|
$self->error("Exec of $param{cmd}[0] ok, but expected to fail\n");
|
|
|
|
}
|
|
|
|
return if $self->errors;
|
|
|
|
|
|
|
|
# Read the log file a couple of times to allow for NFS delays
|
|
|
|
for (my $try=7; $try>=0; $try--) {
|
|
|
|
sleep 1 if ($try!=7);
|
|
|
|
my $moretry = $try!=0;
|
|
|
|
|
2006-09-13 14:38:48 +00:00
|
|
|
my $fh = IO::File->new("<$param{logfile}");
|
2006-08-26 11:35:28 +00:00
|
|
|
next if !$fh && $moretry;
|
|
|
|
local $/; undef $/;
|
|
|
|
my $wholefile = <$fh>;
|
|
|
|
$fh->close();
|
|
|
|
|
|
|
|
# Strip debugging comments
|
|
|
|
$wholefile =~ s/^- [^\n]+\n//mig;
|
|
|
|
$wholefile =~ s/^- [a-z.0-9]+:\d+:[^\n]+\n//mig;
|
|
|
|
$wholefile =~ s/^dot [^\n]+\n//mig;
|
|
|
|
|
|
|
|
# Finished?
|
|
|
|
if ($param{check_finished} && $wholefile !~ /\*\-\* All Finished \*\-\*/) {
|
|
|
|
next if $moretry;
|
|
|
|
$self->error("Missing All Finished\n");
|
|
|
|
}
|
|
|
|
if ($param{expect}) {
|
|
|
|
# Compare
|
|
|
|
my $quoted = quotemeta ($param{expect});
|
|
|
|
my $bad = ($wholefile !~ /$param{expect}/ms
|
|
|
|
&& $wholefile !~ /$quoted/ms);
|
|
|
|
if ($bad) {
|
|
|
|
#print "**BAD $self->{name} $param{logfile} MT $moretry $try\n";
|
|
|
|
next if $moretry;
|
|
|
|
$self->error("Mismatch in output from $param{cmd}[0]\n");
|
|
|
|
print "GOT:\n";
|
|
|
|
print $wholefile;
|
|
|
|
print "ENDGOT\n";
|
|
|
|
print "EXPECT:\n";
|
|
|
|
print $param{expect};
|
|
|
|
print "ENDEXPECT\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#######################################################################
|
|
|
|
# Little utilities
|
|
|
|
|
|
|
|
sub _make_main {
|
|
|
|
my $self = shift;
|
|
|
|
|
|
|
|
$self->_read_inputs();
|
|
|
|
|
2008-11-25 02:38:45 +00:00
|
|
|
my $filename = "$self->{obj_dir}/$self->{VM_PREFIX}__main.cpp";
|
2006-09-13 14:38:48 +00:00
|
|
|
my $fh = IO::File->new(">$filename") or die "%Error: $! $filename,";
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2008-08-05 18:45:20 +00:00
|
|
|
print $fh "// Test defines\n";
|
|
|
|
print $fh "#define VL_TIME_MULTIPLIER $self->{vl_time_multiplier}\n" if $self->{vl_time_multiplier};
|
|
|
|
|
|
|
|
print $fh "// Generated header\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
my $VM_PREFIX = $self->{VM_PREFIX};
|
|
|
|
print $fh "#include \"$VM_PREFIX.h\"\n";
|
2008-06-10 01:25:10 +00:00
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
print $fh "// Compile in-place for speed\n";
|
|
|
|
print $fh "#include \"verilated.cpp\"\n";
|
2007-12-13 13:54:04 +00:00
|
|
|
print $fh "#include \"systemc.h\"\n" if $self->sc;
|
2006-08-26 11:35:28 +00:00
|
|
|
print $fh "#include \"systemperl.h\"\n" if $self->sp;
|
2007-12-13 13:54:04 +00:00
|
|
|
print $fh "#include \"SpTraceVcdC.cpp\"\n" if $self->{trace} && !$self->sp;
|
|
|
|
print $fh "#include \"Sp.cpp\"\n" if $self->sp;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2006-08-30 19:50:24 +00:00
|
|
|
print $fh "$VM_PREFIX * topp;\n";
|
2007-12-13 13:54:04 +00:00
|
|
|
if (!$self->sc_or_sp) {
|
2006-08-26 11:35:28 +00:00
|
|
|
print $fh "unsigned int main_time = false;\n";
|
|
|
|
print $fh "double sc_time_stamp () {\n";
|
|
|
|
print $fh " return main_time;\n";
|
|
|
|
print $fh "}\n";
|
|
|
|
}
|
2007-12-13 13:54:04 +00:00
|
|
|
if ($self->sc_or_sp) {
|
2006-08-26 11:35:28 +00:00
|
|
|
print $fh "extern int sc_main(int argc, char **argv);\n";
|
|
|
|
print $fh "int sc_main(int argc, char **argv) {\n";
|
|
|
|
print $fh " sc_signal<bool> fastclk;\n" if $self->{inputs}{fastclk};
|
|
|
|
print $fh " sc_signal<bool> clk;\n" if $self->{inputs}{clk};
|
2008-11-19 00:37:26 +00:00
|
|
|
print $fh " sc_time sim_time ($self->{sim_time}, SC_NS);\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
} else {
|
|
|
|
print $fh "int main(int argc, char **argv, char **env) {\n";
|
2008-11-19 00:37:26 +00:00
|
|
|
print $fh " double sim_time = $self->{sim_time};\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2007-01-31 21:49:13 +00:00
|
|
|
print $fh " Verilated::debug(".($self->{verilated_debug}?1:0).");\n";
|
2007-06-15 14:39:52 +00:00
|
|
|
print $fh " Verilated::randReset(".$self->{verilated_randReset}.");\n" if defined $self->{verilated_randReset};
|
2006-08-30 19:50:24 +00:00
|
|
|
print $fh " topp = new $VM_PREFIX (\"TOP\");\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
my $set;
|
|
|
|
if ($self->sp) {
|
2006-08-30 19:50:24 +00:00
|
|
|
print $fh " SP_PIN(topp,fastclk,fastclk);\n" if $self->{inputs}{fastclk};
|
|
|
|
print $fh " SP_PIN(topp,clk,clk);\n" if $self->{inputs}{clk};
|
2006-08-26 11:35:28 +00:00
|
|
|
$set = "";
|
2007-12-13 13:54:04 +00:00
|
|
|
} elsif ($self->sc) {
|
|
|
|
print $fh " topp->fastclk(fastclk);\n" if $self->{inputs}{fastclk};
|
|
|
|
print $fh " topp->clk(clk);\n" if $self->{inputs}{clk};
|
|
|
|
$set = "";
|
2006-08-26 11:35:28 +00:00
|
|
|
} else {
|
2006-08-30 19:50:24 +00:00
|
|
|
print $fh " topp->eval();\n";
|
|
|
|
$set = "topp->";
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2007-11-27 16:52:19 +00:00
|
|
|
|
2007-12-13 13:54:04 +00:00
|
|
|
my $ctraceit = ($self->{trace} && !$self->{sp});
|
|
|
|
if ($self->{trace}) {
|
2007-11-27 16:52:19 +00:00
|
|
|
$fh->print("\n");
|
|
|
|
$fh->print("#if VM_TRACE\n");
|
|
|
|
$fh->print(" Verilated::traceEverOn(true);\n");
|
2007-12-13 13:54:04 +00:00
|
|
|
if ($self->{sp}) {
|
|
|
|
$fh->print(" SpTraceFile* tfp = new SpTraceFile;\n");
|
|
|
|
} else {
|
|
|
|
$fh->print(" SpTraceVcdCFile* tfp = new SpTraceVcdCFile;\n");
|
|
|
|
}
|
2007-11-27 16:52:19 +00:00
|
|
|
$fh->print(" topp->trace (tfp, 99);\n");
|
2008-11-25 02:38:45 +00:00
|
|
|
$fh->print(" tfp->open (\"$self->{obj_dir}/simx.vcd\");\n");
|
2007-11-27 16:52:19 +00:00
|
|
|
$fh->print("#endif\n");
|
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
print $fh " ${set}fastclk = true;\n" if $self->{inputs}{fastclk};
|
|
|
|
print $fh " ${set}clk = true;\n" if $self->{inputs}{clk};
|
|
|
|
print $fh " while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) {\n";
|
|
|
|
for (my $i=0; $i<5; $i++) {
|
|
|
|
my $action;
|
|
|
|
if ($self->{inputs}{fastclk}) {
|
|
|
|
print $fh " ${set}fastclk=!${set}fastclk;\n";
|
|
|
|
$action = 1;
|
|
|
|
}
|
|
|
|
if ($i==4 && $self->{inputs}{clk}) {
|
|
|
|
print $fh " ${set}clk=!${set}clk;\n";
|
|
|
|
$action = 1;
|
|
|
|
}
|
2007-12-13 13:54:04 +00:00
|
|
|
if ($self->sc_or_sp) {
|
2008-12-03 15:11:28 +00:00
|
|
|
print $fh "#if (SYSTEMC_VERSION>=20070314)\n";
|
|
|
|
print $fh " sc_start(1,SC_NS);\n";
|
|
|
|
print $fh "#else\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
print $fh " sc_start(1);\n";
|
2008-12-03 15:11:28 +00:00
|
|
|
print $fh "#endif\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
} else {
|
|
|
|
print $fh " main_time+=1;\n";
|
|
|
|
print $fh " ${set}eval();\n" if $action;
|
2007-12-13 13:54:04 +00:00
|
|
|
if ($ctraceit) {
|
2007-11-27 16:52:19 +00:00
|
|
|
$fh->print("#if VM_TRACE\n");
|
|
|
|
$fh->print(" tfp->dump (main_time);\n");
|
|
|
|
$fh->print("#endif //VM_TRACE\n");
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
print $fh " }\n";
|
|
|
|
print $fh " if (!Verilated::gotFinish()) {\n";
|
|
|
|
print $fh ' vl_fatal(__FILE__,__LINE__,"main", "%Error: Timeout; never got a $finish");',"\n";
|
|
|
|
print $fh " }\n";
|
2006-08-30 19:50:24 +00:00
|
|
|
print $fh " topp->final();\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
print $fh " SpCoverage::write(\"",$self->{coverage_filename},"\");\n" if $self->{coverage};
|
2007-11-27 16:52:19 +00:00
|
|
|
|
2007-12-13 13:54:04 +00:00
|
|
|
if ($self->{trace}) {
|
2007-11-27 16:52:19 +00:00
|
|
|
$fh->print("#if VM_TRACE\n");
|
|
|
|
$fh->print(" tfp->close();\n");
|
|
|
|
$fh->print("#endif //VM_TRACE\n");
|
|
|
|
}
|
|
|
|
$fh->print("\n");
|
|
|
|
|
2006-08-30 19:50:24 +00:00
|
|
|
print $fh " delete topp; topp=NULL;\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
print $fh " exit(0L);\n";
|
|
|
|
print $fh "}\n";
|
|
|
|
$fh->close();
|
|
|
|
}
|
|
|
|
|
|
|
|
#######################################################################
|
|
|
|
|
|
|
|
sub _make_top {
|
|
|
|
my $self = shift;
|
|
|
|
|
|
|
|
$self->_read_inputs();
|
|
|
|
|
2006-09-13 14:38:48 +00:00
|
|
|
my $fh = IO::File->new(">$self->{top_shell_filename}") or die "%Error: $! $self->{top_shell_filename},";
|
2006-08-26 11:35:28 +00:00
|
|
|
print $fh "module top;\n";
|
|
|
|
foreach my $inp (sort (keys %{$self->{inputs}})) {
|
|
|
|
print $fh " reg ${inp};\n";
|
|
|
|
}
|
|
|
|
# Inst
|
|
|
|
print $fh " t t (\n";
|
|
|
|
my $comma="";
|
|
|
|
foreach my $inp (sort (keys %{$self->{inputs}})) {
|
|
|
|
print $fh "\t${comma}.${inp} (${inp})\n";
|
|
|
|
$comma=",";
|
|
|
|
}
|
|
|
|
print $fh " );\n";
|
|
|
|
|
|
|
|
# Test
|
|
|
|
print $fh " initial begin\n";
|
|
|
|
print $fh " fastclk=1;\n" if $self->{inputs}{fastclk};
|
|
|
|
print $fh " clk=1;\n" if $self->{inputs}{clk};
|
2008-11-19 00:37:26 +00:00
|
|
|
print $fh " while (\$time < $self->{sim_time}) begin\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
for (my $i=0; $i<5; $i++) {
|
|
|
|
print $fh " #1;\n";
|
|
|
|
print $fh " fastclk=!fastclk;\n" if $self->{inputs}{fastclk};
|
|
|
|
print $fh " clk=!clk;\n" if $i==4 && $self->{inputs}{clk};
|
|
|
|
}
|
|
|
|
print $fh " end\n";
|
|
|
|
print $fh " end\n";
|
|
|
|
|
|
|
|
print $fh "endmodule\n";
|
|
|
|
$fh->close();
|
|
|
|
}
|
|
|
|
|
|
|
|
#######################################################################
|
|
|
|
|
|
|
|
sub _sp_preproc {
|
|
|
|
my $self = shift;
|
|
|
|
my %param = (%{$self}, @_); # Default arguments are from $self
|
|
|
|
|
|
|
|
$self->oprint("Preproc\n");
|
|
|
|
|
|
|
|
$self->_run(logfile=>"simx.log",
|
|
|
|
fails=>0,
|
2008-11-25 02:38:45 +00:00
|
|
|
cmd=>["cd $self->{obj_dir} ; sp_preproc",
|
2006-08-26 11:35:28 +00:00
|
|
|
"--preproc",
|
|
|
|
"$self->{VM_PREFIX}.sp",
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
#######################################################################
|
|
|
|
|
|
|
|
sub _read_inputs {
|
|
|
|
my $self = shift;
|
|
|
|
my $filename = $self->{top_filename};
|
|
|
|
$filename = "t/$filename" if !-r $filename;
|
2006-09-13 14:38:48 +00:00
|
|
|
my $fh = IO::File->new("<$filename") or die "%Error: $! $filename,";
|
2006-08-26 11:35:28 +00:00
|
|
|
while (defined(my $line = $fh->getline)) {
|
|
|
|
if ($line =~ /^\s*input\s*(\S+)\s*(\/[^\/]+\/|)\s*;/) {
|
|
|
|
$self->{inputs}{$1} = $1;
|
|
|
|
}
|
|
|
|
if ($line =~ /^\s*(function|task|endmodule)/) {
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$fh->close();
|
|
|
|
}
|
|
|
|
|
|
|
|
#######################################################################
|
|
|
|
# Verilator utilities
|
|
|
|
|
|
|
|
our $_Verilator_Version;
|
|
|
|
sub verilator_version {
|
|
|
|
if (!defined $_Verilator_Version) {
|
|
|
|
my @args = ("perl","../bin/verilator", "--version");
|
|
|
|
my $args = join(' ',@args);
|
|
|
|
$_Verilator_Version = `$args`;
|
|
|
|
$_Verilator_Version or die "can't fork: $! ".join(' ',@args);
|
|
|
|
chomp $_Verilator_Version;
|
|
|
|
}
|
|
|
|
return $_Verilator_Version if defined $_Verilator_Version;
|
|
|
|
}
|
|
|
|
|
|
|
|
#######################################################################
|
|
|
|
# File utilities
|
|
|
|
|
|
|
|
sub files_identical {
|
|
|
|
my $fn1 = shift;
|
|
|
|
my $fn2 = shift;
|
2006-10-11 15:34:50 +00:00
|
|
|
my $f1 = IO::File->new ("<$fn1"); if (!$f1) { warn "%Error: $! $fn1\n"; return 0; }
|
|
|
|
my $f2 = IO::File->new ("<$fn2"); if (!$f2) { warn "%Error: $! $fn2\n"; return 0; }
|
2006-08-26 11:35:28 +00:00
|
|
|
my @l1 = $f1->getlines();
|
|
|
|
my @l2 = $f2->getlines();
|
|
|
|
my $nl = $#l1; $nl = $#l2 if ($#l2 > $nl);
|
|
|
|
for (my $l=0; $l<=$nl; $l++) {
|
|
|
|
if (($l1[$l]||"") ne ($l2[$l]||"")) {
|
|
|
|
warn ("%Warning: Line ".($l+1)." mismatches; $fn1 != $fn2\n"
|
|
|
|
."F1: ".($l1[$l]||"*EOF*\n")
|
|
|
|
."F2: ".($l2[$l]||"*EOF*\n"));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2008-06-12 00:33:53 +00:00
|
|
|
sub vcd_identical {
|
2008-11-25 02:38:45 +00:00
|
|
|
my $self = (ref $_[0]? shift : $Self);
|
2008-06-12 00:33:53 +00:00
|
|
|
my $fn1 = shift;
|
|
|
|
my $fn2 = shift;
|
|
|
|
if (!-r $fn1) { $self->error("File does not exist $fn1\n"); return 0; }
|
|
|
|
if (!-r $fn2) { $self->error("File does not exist $fn2\n"); return 0; }
|
|
|
|
my $out = `vcddiff --help`;
|
|
|
|
if ($out !~ /Usage:/) { $self->skip("No vcddiff installed\n"); return 0; }
|
|
|
|
$out = `vcddiff "$fn1" "$fn2"`;
|
|
|
|
if ($out ne '') {
|
|
|
|
print $out;
|
|
|
|
$self->error("VCD miscompare $fn1 $fn2\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
sub file_grep_not {
|
2008-11-25 02:38:45 +00:00
|
|
|
my $self = (ref $_[0]? shift : $Self);
|
2006-08-26 11:35:28 +00:00
|
|
|
my $filename = shift;
|
|
|
|
my $regexp = shift;
|
|
|
|
|
|
|
|
my $contents = $self->file_contents($filename);
|
|
|
|
return if ($contents eq "_Already_Errored_");
|
|
|
|
if ($contents =~ /$regexp/) {
|
|
|
|
$self->error("File_grep_not: $filename: Regexp found: $regexp\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub file_grep {
|
2008-11-25 02:38:45 +00:00
|
|
|
my $self = (ref $_[0]? shift : $Self);
|
2006-08-26 11:35:28 +00:00
|
|
|
my $filename = shift;
|
|
|
|
my $regexp = shift;
|
|
|
|
|
|
|
|
my $contents = $self->file_contents($filename);
|
|
|
|
return if ($contents eq "_Already_Errored_");
|
|
|
|
if ($contents !~ /$regexp/) {
|
|
|
|
$self->error("File_grep: $filename: Regexp not found: $regexp\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
my %_File_Contents_Cache;
|
|
|
|
|
|
|
|
sub file_contents {
|
2008-11-25 02:38:45 +00:00
|
|
|
my $self = (ref $_[0]? shift : $Self);
|
2006-08-26 11:35:28 +00:00
|
|
|
my $filename = shift;
|
|
|
|
|
|
|
|
if (!$_File_Contents_Cache{$filename}) {
|
2006-09-13 14:38:48 +00:00
|
|
|
my $fh = IO::File->new("<$filename");
|
2006-08-26 11:35:28 +00:00
|
|
|
if (!$fh) {
|
|
|
|
$_File_Contents_Cache{$filename} = "_Already_Errored_";
|
|
|
|
$self->error("File_grep file not found: ".$filename."\n");
|
|
|
|
return $_File_Contents_Cache{$filename};
|
|
|
|
}
|
|
|
|
local $/; undef $/;
|
|
|
|
my $wholefile = <$fh>;
|
|
|
|
$fh->close();
|
|
|
|
$_File_Contents_Cache{$filename} = $wholefile;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $_File_Contents_Cache{$filename};
|
|
|
|
}
|
|
|
|
|
2006-09-13 14:38:48 +00:00
|
|
|
sub write_wholefile {
|
2008-11-25 02:38:45 +00:00
|
|
|
my $self = (ref $_[0]? shift : $Self);
|
2006-09-13 14:38:48 +00:00
|
|
|
my $filename = shift;
|
|
|
|
my $contents = shift;
|
|
|
|
my $fh = IO::File->new(">$filename") or die "%Error: $! writing $filename,";
|
|
|
|
print $fh $contents;
|
|
|
|
$fh->close;
|
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
#######################################################################
|
|
|
|
#######################################################################
|
|
|
|
#######################################################################
|
|
|
|
#######################################################################
|
|
|
|
# Forker class
|
|
|
|
|
|
|
|
package Forker;
|
|
|
|
use strict;
|
|
|
|
|
|
|
|
# This is a shell that matches Parallel::Forker.
|
|
|
|
# If that package is not installed, this runs the tests in *series*
|
|
|
|
|
|
|
|
sub new {
|
|
|
|
my $class = shift;
|
|
|
|
my $self = {@_};
|
|
|
|
bless $self, $class;
|
|
|
|
return $self;
|
|
|
|
}
|
|
|
|
sub schedule {
|
|
|
|
my $self = shift;
|
|
|
|
my %params = (@_);
|
|
|
|
&{$params{run_on_start}}();
|
|
|
|
&{$params{run_on_finish}}();
|
|
|
|
return $self;
|
|
|
|
}
|
|
|
|
sub max_proc {}
|
|
|
|
sub sig_child {}
|
|
|
|
sub kill_tree_all {}
|
|
|
|
sub wait_all {}
|
|
|
|
sub ready {}
|
|
|
|
|
|
|
|
#######################################################################
|
|
|
|
1;
|
|
|
|
package main;
|
|
|
|
__END__
|
|
|
|
|
|
|
|
=pod
|
|
|
|
|
|
|
|
=head1 NAME
|
|
|
|
|
|
|
|
driver.pl - Run regression tests
|
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
|
|
|
|
driver.pl
|
|
|
|
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
|
|
|
|
driver.pl invokes Verilator or another simulator on each little test file.
|
|
|
|
|
|
|
|
=head1 ARGUMENTS
|
|
|
|
|
|
|
|
=over 4
|
|
|
|
|
2008-11-21 19:38:12 +00:00
|
|
|
=item --benchmark [<cycles>]
|
|
|
|
|
|
|
|
Show execution times of each step. If an optional number is given,
|
|
|
|
specifies the number of simulation cycles (for tests that support it).
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
=item --gdb
|
|
|
|
|
|
|
|
Run verilator under the debugger.
|
|
|
|
|
|
|
|
=item --help
|
|
|
|
|
|
|
|
Displays this message and program version and exits.
|
|
|
|
|
|
|
|
=item --j #
|
|
|
|
|
2008-09-23 14:02:31 +00:00
|
|
|
Run number of parallel tests, or 0 to determine the count based on the
|
|
|
|
number of cores installed. Requires Parallel::Forker project.
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
=item --optimize
|
|
|
|
|
|
|
|
Randomly turn on/off different optimizations. With specific flags,
|
|
|
|
use those optimization settings
|
|
|
|
|
|
|
|
=item --nc
|
|
|
|
|
|
|
|
Run using NC-Verilog.
|
|
|
|
|
|
|
|
=item --stop
|
|
|
|
|
|
|
|
Stop on the first error
|
|
|
|
|
|
|
|
=item --vcs
|
|
|
|
|
|
|
|
Run using VCS.
|
|
|
|
|
2006-12-19 14:09:57 +00:00
|
|
|
=item --verbose
|
|
|
|
|
|
|
|
Enable test verbose messages.
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
=item --v3
|
|
|
|
|
|
|
|
Run using Verilator.
|
|
|
|
|
|
|
|
=back
|
|
|
|
|
2007-03-13 17:15:00 +00:00
|
|
|
=head1 ENVIRONMENT
|
|
|
|
|
|
|
|
=over 4
|
|
|
|
|
|
|
|
=item SYSTEMC
|
|
|
|
|
|
|
|
Root directory name of SystemC kit.
|
|
|
|
|
|
|
|
=item VERILATOR_NCVERILOG
|
|
|
|
|
|
|
|
Command to use to invoke ncverilog.
|
|
|
|
|
|
|
|
=item VERILATOR_VCS
|
|
|
|
|
|
|
|
Command to use to invoke VCS.
|
|
|
|
|
|
|
|
=back
|
|
|
|
|
2009-01-21 21:56:50 +00:00
|
|
|
=head1 DISTRIBUTION
|
|
|
|
|
|
|
|
The latest version is available from L<http://www.veripool.org/>.
|
|
|
|
|
|
|
|
Copyright 2003-2009 by Wilson Snyder. Verilator is free software; you can
|
|
|
|
redistribute it and/or modify it under the terms of either the GNU Lesser
|
2009-05-04 21:07:57 +00:00
|
|
|
General Public License Version 3 or the Perl Artistic License Version 2.0.
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
=head1 AUTHORS
|
|
|
|
|
|
|
|
Wilson Snyder <wsnyder@wsnyder.org>
|
|
|
|
|
2009-01-21 21:56:50 +00:00
|
|
|
=head1 SEE ALSO
|
|
|
|
|
|
|
|
L<verilator>
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
=cut
|
|
|
|
|
|
|
|
######################################################################
|