2020-01-10 01:01:12 +00:00
|
|
|
#!/usr/bin/env perl
|
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;
|
2020-01-10 01:01:12 +00:00
|
|
|
use warnings;
|
2017-10-26 11:34:02 +00:00
|
|
|
use Cwd;
|
2021-09-08 03:50:28 +00:00
|
|
|
|
2019-09-01 15:15:42 +00:00
|
|
|
BEGIN {
|
|
|
|
if (!$ENV{VERILATOR_ROOT} && -x "../bin/verilator") {
|
2021-09-08 03:50:28 +00:00
|
|
|
$ENV{VERILATOR_ROOT} = Cwd::getcwd() . "/..";
|
2019-09-01 15:15:42 +00:00
|
|
|
}
|
2020-06-28 16:17:54 +00:00
|
|
|
$ENV{MAKE} ||= "make";
|
|
|
|
$ENV{CXX} ||= "c++";
|
2019-09-01 15:15:42 +00:00
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
use Getopt::Long;
|
|
|
|
use IO::File;
|
|
|
|
use Pod::Usage;
|
2021-09-08 03:50:28 +00:00
|
|
|
use Data::Dumper; $Data::Dumper::Sortkeys = 1;
|
2019-06-15 20:37:58 +00:00
|
|
|
use FindBin qw($RealBin);
|
2006-08-26 11:35:28 +00:00
|
|
|
use strict;
|
2019-05-08 02:34:09 +00:00
|
|
|
use vars qw($Debug %Vars $Driver $Fork);
|
2019-10-17 23:44:10 +00:00
|
|
|
use version;
|
2008-09-23 14:02:31 +00:00
|
|
|
use POSIX qw(strftime);
|
2017-07-06 23:07:23 +00:00
|
|
|
use lib ".";
|
2019-10-10 12:27:15 +00:00
|
|
|
use Time::HiRes qw(usleep);
|
Travis: Use workspaces and per job persistent ccache (#2399)
Change the Travis builds to use workspaces and persistent ccache
We proceed in 2 stages (as before, but using workspaces for
persistence):
1. In the 'build' stage, we clone the repo, build it and
save the whole checkout ($TRAVIS_BUILD_DIR) as a workspace
2. In the 'test' stage, rather than cloning the repo, multiple jobs
pull down the same workspace we built to run the tests from
This enables:
- Reuse of the build in multiple test jobs (this is what we used the Travis
cache for before)
- Each job having a separate persistent Travis cache, which now only
contains the ccache. This means all jobs, including 'build' and 'test'
jobs can make maximum use of ccache across runs. This drastically cuts
down build times when the ccache hits, which is very often the case for
'test' jobs. Also, the separate caches only store the objects build by
the particular job that owns the cache, so we can keep the per job
ccache small.
If the commit message contains '[travis ccache clear]', the ccache will
be cleared at the beginning of the build. This can be used to test build
complete within the 50 minute timeout imposed by Travis, even without a
persistent ccache.
2020-06-03 20:10:13 +00:00
|
|
|
use Digest::MD5 qw(md5);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
$::Driver = 1;
|
2012-11-28 12:36:47 +00:00
|
|
|
$::Have_Forker = 0;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2020-05-23 13:50:04 +00:00
|
|
|
eval "use Parallel::Forker; \$Fork=Parallel::Forker->new(use_sig_child=>1, poll_interval=>10*1000); \$::Have_Forker=1;";
|
2021-09-08 03:50:28 +00:00
|
|
|
$Fork = Forker->new(use_sig_child => 1) if !$Fork;
|
2020-05-19 18:31:13 +00:00
|
|
|
my $forker_Min_Version = 1.258;
|
|
|
|
if ($::Have_Forker && $Parallel::Forker::VERSION < $forker_Min_Version) {
|
|
|
|
print STDERR "driver.pl: Parallel::Forker is older than $forker_Min_Version, suggest 'cpan install Parallel::Forker'\n";
|
|
|
|
$::Have_Forker = 0;
|
|
|
|
}
|
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"; };
|
|
|
|
|
|
|
|
#======================================================================
|
|
|
|
|
2018-05-08 00:42:28 +00:00
|
|
|
# Map of all scenarios, with the names used to enable them
|
|
|
|
our %All_Scenarios
|
2020-04-15 23:39:03 +00:00
|
|
|
= (dist => [ "dist"],
|
|
|
|
atsim => [ "simulator", "simulator_st", "atsim"],
|
|
|
|
ghdl => ["linter", "simulator", "simulator_st", "ghdl"],
|
|
|
|
iv => [ "simulator", "simulator_st", "iv"],
|
|
|
|
ms => ["linter", "simulator", "simulator_st", "ms"],
|
|
|
|
nc => ["linter", "simulator", "simulator_st", "nc"],
|
|
|
|
vcs => ["linter", "simulator", "simulator_st", "vcs"],
|
|
|
|
xsim => ["linter", "simulator", "simulator_st", "xsim"],
|
|
|
|
vlt => ["linter", "simulator", "simulator_st", "vlt_all", "vlt"],
|
|
|
|
vltmt => [ "simulator", "vlt_all", "vltmt"],
|
2018-05-08 00:42:28 +00:00
|
|
|
);
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
#======================================================================
|
|
|
|
# main
|
|
|
|
|
|
|
|
autoflush STDOUT 1;
|
|
|
|
autoflush STDERR 1;
|
|
|
|
|
|
|
|
our @Orig_ARGV = @ARGV;
|
2021-09-08 03:50:28 +00:00
|
|
|
our @Orig_ARGV_Sw; foreach (@Orig_ARGV) { push @Orig_ARGV_Sw, $_ if /^-/ && !/^-j/; }
|
2011-10-05 13:50:00 +00:00
|
|
|
our $Start = time();
|
2019-06-27 15:26:25 +00:00
|
|
|
our $Vltmt_threads = 3;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
$Debug = 0;
|
2008-11-21 19:38:12 +00:00
|
|
|
my $opt_benchmark;
|
2006-08-26 11:35:28 +00:00
|
|
|
my @opt_tests;
|
2018-05-08 00:42:28 +00:00
|
|
|
my $opt_dist;
|
2009-10-02 02:32:40 +00:00
|
|
|
my $opt_gdb;
|
2019-07-26 01:34:09 +00:00
|
|
|
my $opt_rr;
|
2009-10-04 22:04:37 +00:00
|
|
|
my $opt_gdbbt;
|
2010-01-17 19:48:39 +00:00
|
|
|
my $opt_gdbsim;
|
2020-03-08 02:38:44 +00:00
|
|
|
my $opt_hashset;
|
2009-10-02 02:32:40 +00:00
|
|
|
my $opt_jobs = 1;
|
2019-06-29 19:24:56 +00:00
|
|
|
my $opt_quiet;
|
|
|
|
my $opt_rerun;
|
2019-07-26 01:34:09 +00:00
|
|
|
my $opt_rrsim;
|
2020-12-16 02:07:27 +00:00
|
|
|
my $opt_sanitize;
|
2018-05-08 00:42:28 +00:00
|
|
|
my %opt_scenarios;
|
2011-01-02 00:18:32 +00:00
|
|
|
my $opt_site;
|
2009-10-02 02:32:40 +00:00
|
|
|
my $opt_stop;
|
2009-05-08 17:16:19 +00:00
|
|
|
my $opt_trace;
|
2006-12-19 14:09:57 +00:00
|
|
|
my $opt_verbose;
|
2006-08-26 11:35:28 +00:00
|
|
|
my $Opt_Verilated_Debug;
|
2012-04-17 00:28:24 +00:00
|
|
|
our $Opt_Verilation = 1;
|
2009-01-21 21:56:50 +00:00
|
|
|
our @Opt_Driver_Verilator_Flags;
|
|
|
|
|
2019-03-10 13:25:23 +00:00
|
|
|
Getopt::Long::config("pass_through");
|
|
|
|
if (! GetOptions(
|
2018-05-08 00:42:28 +00:00
|
|
|
"benchmark:i" => sub { $opt_benchmark = $_[1] ? $_[1] : 1; },
|
|
|
|
"debug" => \&debug,
|
|
|
|
#debugi see parameter()
|
|
|
|
"gdb!" => \$opt_gdb,
|
|
|
|
"gdbbt!" => \$opt_gdbbt,
|
|
|
|
"gdbsim!" => \$opt_gdbsim,
|
|
|
|
"golden!" => sub { $ENV{HARNESS_UPDATE_GOLDEN} = 1; },
|
2020-03-08 02:38:44 +00:00
|
|
|
"hashset=s" => \$opt_hashset,
|
2018-05-08 00:42:28 +00:00
|
|
|
"help" => \&usage,
|
|
|
|
"j=i" => \$opt_jobs,
|
2019-06-29 19:24:56 +00:00
|
|
|
"quiet!" => \$opt_quiet,
|
|
|
|
"rerun!" => \$opt_rerun,
|
2019-07-26 01:34:09 +00:00
|
|
|
"rr!" => \$opt_rr,
|
|
|
|
"rrsim!" => \$opt_rrsim,
|
2020-12-16 02:07:27 +00:00
|
|
|
"sanitize!" => \$opt_sanitize,
|
2018-05-08 00:42:28 +00:00
|
|
|
"site!" => \$opt_site,
|
|
|
|
"stop!" => \$opt_stop,
|
|
|
|
"trace!" => \$opt_trace,
|
|
|
|
"verbose!" => \$opt_verbose,
|
|
|
|
"verilation!" => \$Opt_Verilation, # Undocumented debugging
|
2018-08-31 00:04:55 +00:00
|
|
|
"verilated-debug!" => \$Opt_Verilated_Debug,
|
2018-05-08 00:42:28 +00:00
|
|
|
#W see parameter()
|
|
|
|
# Scenarios
|
|
|
|
"atsim|athdl!"=> sub { $opt_scenarios{atsim} = $_[1]; },
|
|
|
|
"dist!" => sub { $opt_scenarios{dist} = $_[1]; },
|
|
|
|
"ghdl!" => sub { $opt_scenarios{ghdl} = $_[1]; },
|
2019-11-20 02:53:17 +00:00
|
|
|
"iv!" => sub { $opt_scenarios{iv} = $_[1]; },
|
2018-05-08 00:42:28 +00:00
|
|
|
"ms!" => sub { $opt_scenarios{ms} = $_[1]; },
|
|
|
|
"nc!" => sub { $opt_scenarios{nc} = $_[1]; },
|
|
|
|
"vlt!" => sub { $opt_scenarios{vlt} = $_[1]; },
|
2018-07-23 00:54:28 +00:00
|
|
|
"vltmt!" => sub { $opt_scenarios{vltmt} = $_[1]; },
|
2018-05-08 00:42:28 +00:00
|
|
|
"vcs!" => sub { $opt_scenarios{vcs} = $_[1]; },
|
2019-08-29 21:00:49 +00:00
|
|
|
"xsim!" => sub { $opt_scenarios{xsim} = $_[1]; },
|
2018-05-08 00:42: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
|
|
|
|
2021-09-08 03:50:28 +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);
|
|
|
|
|
2018-05-08 00:42:28 +00:00
|
|
|
if ((scalar keys %opt_scenarios) < 1) {
|
|
|
|
$opt_scenarios{dist} = 1;
|
|
|
|
$opt_scenarios{vlt} = 1;
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
2011-01-02 00:18:32 +00:00
|
|
|
our @Test_Dirs = "t";
|
2021-09-08 03:50:28 +00:00
|
|
|
push @Test_Dirs, split(/:/, $ENV{VERILATOR_TESTS_SITE})
|
|
|
|
if (($#opt_tests < 0 ? $opt_site : 1) && $ENV{VERILATOR_TESTS_SITE});
|
2011-01-02 00:18:32 +00:00
|
|
|
|
2021-09-08 03:50:28 +00:00
|
|
|
if ($#opt_tests < 0) { # Run everything
|
2011-01-02 00:18:32 +00:00
|
|
|
my %uniq;
|
|
|
|
foreach my $dir (@Test_Dirs) {
|
2019-05-08 02:34:09 +00:00
|
|
|
my @stats = stat($dir); # Uniquify by inode, so different paths to same place get combined
|
|
|
|
next if !$stats[1] || $uniq{$stats[1]}++;
|
2018-10-02 10:31:11 +00:00
|
|
|
push @opt_tests, sort(glob("${dir}/t_*.pl"));
|
2011-01-02 00:18:32 +00:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2020-03-08 02:38:44 +00:00
|
|
|
@opt_tests = _calc_hashset(@opt_tests) if $opt_hashset;
|
|
|
|
|
2021-09-08 03:50:28 +00:00
|
|
|
if ($#opt_tests >= 2 && $opt_jobs >= 2) {
|
2014-11-27 15:48:27 +00:00
|
|
|
# Without this tests such as t_debug_sigsegv_bt_bad.pl will occasionally
|
|
|
|
# block on input and cause a SIGSTOP, then a "fg" was needed to resume testing.
|
2019-03-10 16:14:58 +00:00
|
|
|
if (!$::Have_Forker) {
|
|
|
|
print STDERR "driver.pl: NO_FORKER: For faster testing 'sudo cpan install Parallel::Forker'\n";
|
|
|
|
}
|
2014-11-27 15:48:27 +00:00
|
|
|
print STDERR "== Many jobs; redirecting STDIN\n";
|
2019-06-29 19:24:56 +00:00
|
|
|
open(STDIN, "+>/dev/null");
|
2014-11-27 15:48:27 +00:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2018-05-08 00:42:28 +00:00
|
|
|
mkdir "obj_dist";
|
2019-06-29 19:24:56 +00:00
|
|
|
my $timestart = strftime("%Y%m%d_%H%M%S", localtime);
|
|
|
|
|
|
|
|
my $runner;
|
|
|
|
{
|
|
|
|
$runner = Runner->new(
|
|
|
|
driver_log_filename => "obj_dist/driver_${timestart}.log",
|
|
|
|
quiet => $opt_quiet);
|
|
|
|
foreach my $testpl (@opt_tests) {
|
|
|
|
foreach my $scenario (sort keys %opt_scenarios) {
|
|
|
|
next if !$opt_scenarios{$scenario};
|
|
|
|
$runner->one_test(pl_filename => $testpl,
|
|
|
|
$scenario => 1);
|
|
|
|
}
|
2018-05-08 00:42:28 +00:00
|
|
|
}
|
2019-06-29 19:24:56 +00:00
|
|
|
$runner->wait_and_report;
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
2019-06-29 19:24:56 +00:00
|
|
|
if ($opt_rerun && $runner->fail_count) {
|
2021-09-08 03:50:28 +00:00
|
|
|
print("=" x 70, "\n");
|
|
|
|
print("=" x 70, "\n");
|
2019-06-29 19:24:56 +00:00
|
|
|
print("RERUN ==\n\n");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2019-06-29 19:24:56 +00:00
|
|
|
# Avoid parallel run to ensure that isn't causing problems
|
|
|
|
# If > 10 failures something more wrong and get results quickly
|
|
|
|
$Fork->max_proc(1) unless $runner->fail_count > 10;
|
|
|
|
|
|
|
|
my $orig_runner = $runner;
|
|
|
|
$runner = Runner->new(
|
|
|
|
driver_log_filename => "obj_dist/driver_${timestart}_rerun.log",
|
|
|
|
quiet => 0,
|
2019-10-17 23:33:47 +00:00
|
|
|
fail1_cnt => $orig_runner->fail_count,
|
|
|
|
ok_cnt => $orig_runner->{ok_cnt},
|
2022-11-20 03:43:10 +00:00
|
|
|
skip_cnt => $orig_runner->{skip_cnt});
|
2019-06-29 19:24:56 +00:00
|
|
|
foreach my $test (@{$orig_runner->{fail_tests}}) {
|
2019-06-29 21:40:31 +00:00
|
|
|
# Reschedule test
|
2022-12-03 00:57:40 +00:00
|
|
|
$test->clean if $test->rerunnable;
|
2019-06-29 19:24:56 +00:00
|
|
|
$runner->one_test(pl_filename => $test->{pl_filename},
|
2022-12-03 00:57:40 +00:00
|
|
|
$test->{scenario} => 1,
|
|
|
|
rerun_skipping => !$test->rerunnable);
|
2019-06-29 19:24:56 +00:00
|
|
|
}
|
|
|
|
$runner->wait_and_report;
|
|
|
|
}
|
2008-09-23 14:02:31 +00:00
|
|
|
|
2019-06-29 19:24:56 +00:00
|
|
|
exit(10) if $runner->fail_count;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
|
|
|
sub usage {
|
2021-09-08 03:50:28 +00:00
|
|
|
pod2usage(-verbose => 2, -exitval => 0, -output => \*STDOUT);
|
2019-10-01 03:15:10 +00:00
|
|
|
exit(1); # Unreachable
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sub debug {
|
|
|
|
$Debug = 1;
|
2012-07-21 13:16:19 +00:00
|
|
|
push @Opt_Driver_Verilator_Flags, "--debug --no-skip-identical";
|
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) {
|
2019-05-08 02:34:09 +00:00
|
|
|
($param =~ /^(\d+)$/)
|
|
|
|
or die "%Error: Expected number following $_Parameter_Next_Level: $param\n";
|
|
|
|
push @Opt_Driver_Verilator_Flags, $param;
|
2021-09-08 03:50:28 +00:00
|
|
|
$_Parameter_Next_Level = undef;
|
2009-01-21 21:56:50 +00:00
|
|
|
}
|
|
|
|
elsif ($param =~ /\.pl/) {
|
2019-05-08 02:34:09 +00:00
|
|
|
push @opt_tests, $param;
|
2009-01-21 21:56:50 +00:00
|
|
|
}
|
2022-09-18 19:53:42 +00:00
|
|
|
elsif ($param =~ /^-?(-debugi|-dumpi)/) {
|
2019-05-08 02:34:09 +00:00
|
|
|
push @Opt_Driver_Verilator_Flags, $param;
|
|
|
|
$_Parameter_Next_Level = $param;
|
2009-01-21 21:56:50 +00:00
|
|
|
}
|
2013-12-21 11:46:48 +00:00
|
|
|
elsif ($param =~ /^-?(-W||-debug-check)/) {
|
2019-05-08 02:34:09 +00:00
|
|
|
push @Opt_Driver_Verilator_Flags, $param;
|
2011-10-07 12:29:34 +00:00
|
|
|
}
|
2009-01-21 21:56:50 +00:00
|
|
|
else {
|
2019-05-08 02:34:09 +00:00
|
|
|
die "%Error: Unknown parameter: $param\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
}
|
2008-06-10 01:25:10 +00:00
|
|
|
|
2020-12-23 21:07:14 +00:00
|
|
|
our $_Max_Procs;
|
2021-09-08 03:50:28 +00:00
|
|
|
|
2019-06-27 15:26:25 +00:00
|
|
|
sub max_procs {
|
2020-12-23 21:07:14 +00:00
|
|
|
if (!defined $_Max_Procs) {
|
|
|
|
$_Max_Procs = `python3 -c 'import multiprocessing\nprint(multiprocessing.cpu_count())'`;
|
|
|
|
chomp $_Max_Procs;
|
|
|
|
if ($_Max_Procs < 2) {
|
|
|
|
$_Max_Procs = 2;
|
|
|
|
warn "driver.pl: Python didn't find at least two CPUs\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $_Max_Procs;
|
2019-06-27 15:26:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sub calc_threads {
|
|
|
|
my $default = shift;
|
|
|
|
my $ok = max_procs();
|
|
|
|
$ok && !$@ or return $default;
|
|
|
|
return ($ok < $default) ? $ok : $default;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub calc_jobs {
|
|
|
|
my $ok = max_procs();
|
2008-09-23 14:02:31 +00:00
|
|
|
$ok && !$@ or die "%Error: Can't use -j: $@\n";
|
2021-09-08 03:50:28 +00:00
|
|
|
print "driver.pl: Found $ok cores, using -j ", $ok + 1, "\n";
|
2008-09-23 14:02:31 +00:00
|
|
|
return $ok + 1;
|
|
|
|
}
|
|
|
|
|
2020-03-08 02:38:44 +00:00
|
|
|
sub _calc_hashset {
|
|
|
|
my @in = @_;
|
|
|
|
return @in if !$opt_hashset;
|
|
|
|
$opt_hashset =~ m!^(\d+)/(\d+)$!
|
|
|
|
or die "%Error: Need number/number format for --hashset: $opt_hashset\n";
|
|
|
|
my ($set, $nsets) = ($1, $2);
|
|
|
|
my @new;
|
|
|
|
foreach my $t (@opt_tests) {
|
Travis: Use workspaces and per job persistent ccache (#2399)
Change the Travis builds to use workspaces and persistent ccache
We proceed in 2 stages (as before, but using workspaces for
persistence):
1. In the 'build' stage, we clone the repo, build it and
save the whole checkout ($TRAVIS_BUILD_DIR) as a workspace
2. In the 'test' stage, rather than cloning the repo, multiple jobs
pull down the same workspace we built to run the tests from
This enables:
- Reuse of the build in multiple test jobs (this is what we used the Travis
cache for before)
- Each job having a separate persistent Travis cache, which now only
contains the ccache. This means all jobs, including 'build' and 'test'
jobs can make maximum use of ccache across runs. This drastically cuts
down build times when the ccache hits, which is very often the case for
'test' jobs. Also, the separate caches only store the objects build by
the particular job that owns the cache, so we can keep the per job
ccache small.
If the commit message contains '[travis ccache clear]', the ccache will
be cleared at the beginning of the build. This can be used to test build
complete within the 50 minute timeout imposed by Travis, even without a
persistent ccache.
2020-06-03 20:10:13 +00:00
|
|
|
my $checksum = unpack('L', substr(md5($t), 0, 4));
|
2020-03-09 21:57:27 +00:00
|
|
|
if (($set % $nsets) == ($checksum % $nsets)) {
|
2020-03-08 02:38:44 +00:00
|
|
|
push @new, $t;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return @new;
|
|
|
|
}
|
|
|
|
|
2019-06-29 19:24:56 +00:00
|
|
|
#######################################################################
|
|
|
|
#######################################################################
|
|
|
|
#######################################################################
|
|
|
|
#######################################################################
|
|
|
|
# Runner class
|
|
|
|
|
|
|
|
package Runner;
|
2019-06-29 19:58:00 +00:00
|
|
|
use strict;
|
2019-06-29 19:24:56 +00:00
|
|
|
|
|
|
|
sub new {
|
|
|
|
my $class = shift;
|
|
|
|
my $self = {
|
|
|
|
# Parameters
|
|
|
|
driver_log_filename => undef,
|
|
|
|
quiet => 0,
|
|
|
|
# Counts
|
2019-09-30 23:22:36 +00:00
|
|
|
all_cnt => 0,
|
2019-06-29 19:24:56 +00:00
|
|
|
left_cnt => 0,
|
|
|
|
ok_cnt => 0,
|
|
|
|
fail1_cnt => 0,
|
|
|
|
fail_cnt => 0,
|
|
|
|
skip_cnt => 0,
|
2019-07-31 02:20:37 +00:00
|
|
|
skip_msgs => [],
|
2019-06-29 19:24:56 +00:00
|
|
|
fail_msgs => [],
|
|
|
|
fail_tests => [],
|
2020-05-16 10:15:25 +00:00
|
|
|
# Per-task identifiers
|
|
|
|
running_ids => {},
|
2019-06-29 19:24:56 +00:00
|
|
|
@_};
|
|
|
|
bless $self, $class;
|
|
|
|
return $self;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub fail_count { return $_[0]->{fail_cnt}; }
|
|
|
|
|
2019-06-29 17:37:01 +00:00
|
|
|
sub one_test {
|
2019-06-29 19:24:56 +00:00
|
|
|
my $self = shift;
|
2019-06-29 17:37:01 +00:00
|
|
|
my @params = @_;
|
|
|
|
my %params = (@params);
|
2019-09-30 23:22:36 +00:00
|
|
|
$self->{all_cnt}++;
|
2019-06-29 19:24:56 +00:00
|
|
|
$self->{left_cnt}++;
|
|
|
|
$::Fork->schedule
|
2019-06-29 17:37:01 +00:00
|
|
|
(
|
|
|
|
test_pl_filename => $params{pl_filename},
|
2020-05-16 10:15:25 +00:00
|
|
|
run_pre_start => sub {
|
|
|
|
my $process = shift;
|
|
|
|
# Running in context of parent, before run_on_start
|
|
|
|
# Make an identifier that is unique across all current running jobs
|
|
|
|
my $i = 1; while (exists $self->{running_ids}{$i}) { ++$i; }
|
|
|
|
$process->{running_id} = $i;
|
|
|
|
$self->{running_ids}{$process->{running_id}} = 1;
|
|
|
|
},
|
2019-06-29 17:37:01 +00:00
|
|
|
run_on_start => sub {
|
2020-05-16 10:15:25 +00:00
|
|
|
my $process = shift;
|
2019-06-29 17:37:01 +00:00
|
|
|
# Running in context of child, so can't pass data to parent directly
|
2019-06-29 19:24:56 +00:00
|
|
|
if ($self->{quiet}) {
|
|
|
|
open(STDOUT, ">/dev/null");
|
|
|
|
open(STDERR, ">&STDOUT");
|
|
|
|
}
|
2021-09-08 03:50:28 +00:00
|
|
|
print("=" x 70, "\n");
|
2020-05-16 10:15:25 +00:00
|
|
|
my $test = VTest->new(@params,
|
|
|
|
running_id => $process->{running_id});
|
2021-09-08 03:50:28 +00:00
|
|
|
$test->oprint("=" x 50, "\n");
|
2022-12-03 00:57:40 +00:00
|
|
|
unlink $test->{status_filename} if !$params{rerun_skipping};
|
2019-06-29 17:37:01 +00:00
|
|
|
$test->_prep;
|
2022-12-03 00:57:40 +00:00
|
|
|
if ($params{rerun_skipping}) {
|
|
|
|
print " ---------- Earlier logfiles below; test was rerunnable = 0\n";
|
|
|
|
system("cat $test->{obj_dir}/*.log");
|
|
|
|
print " ---------- Earlier logfiles above; test was rerunnable = 0\n";
|
|
|
|
} else {
|
|
|
|
$test->_read;
|
|
|
|
}
|
2019-06-29 17:37:01 +00:00
|
|
|
# Don't put anything other than _exit after _read,
|
|
|
|
# as may call _exit via another path
|
|
|
|
$test->_exit;
|
|
|
|
},
|
|
|
|
run_on_finish => sub {
|
2019-06-29 19:24:56 +00:00
|
|
|
# Running in context of parent
|
2020-05-16 10:15:25 +00:00
|
|
|
my $process = shift;
|
|
|
|
my $test = VTest->new(@params,
|
|
|
|
running_id => $process->{running_id});
|
2019-06-29 17:37:01 +00:00
|
|
|
$test->_read_status;
|
|
|
|
if ($test->ok) {
|
2019-06-29 19:24:56 +00:00
|
|
|
$self->{ok_cnt}++;
|
2019-06-29 17:37:01 +00:00
|
|
|
} elsif ($test->scenario_off && !$test->errors) {
|
|
|
|
} elsif ($test->skips && !$test->errors) {
|
2019-07-31 02:20:37 +00:00
|
|
|
push @{$self->{skip_msgs}},
|
2021-09-08 03:50:28 +00:00
|
|
|
("\t#" . $test->soprint("-Skip: $test->{skips}\n"));
|
2019-06-29 19:24:56 +00:00
|
|
|
$self->{skip_cnt}++;
|
2019-06-29 17:37:01 +00:00
|
|
|
} else {
|
2019-06-29 19:24:56 +00:00
|
|
|
$test->oprint("FAILED: $test->{errors}\n");
|
2021-09-08 22:45:25 +00:00
|
|
|
my $j = ($opt_jobs > 1 ? " -j" : "");
|
2020-05-31 13:03:51 +00:00
|
|
|
my $makecmd = $ENV{VERILATOR_MAKE} || "$ENV{MAKE}$j &&";
|
2020-05-24 02:24:26 +00:00
|
|
|
my $upperdir = (Cwd::getcwd() =~ /test_regress/
|
|
|
|
? 'test_regress/' : '');
|
2019-06-29 19:24:56 +00:00
|
|
|
push @{$self->{fail_msgs}},
|
2021-09-08 03:50:28 +00:00
|
|
|
("\t#" . $test->soprint("%Error: $test->{errors}\n")
|
|
|
|
. "\t\t$makecmd "
|
|
|
|
. $upperdir . $test->{pl_filename}
|
|
|
|
. " " . join(' ', _manual_args())
|
|
|
|
. " --" . $test->{scenario} . "\n");
|
2019-06-29 19:24:56 +00:00
|
|
|
push @{$self->{fail_tests}}, $test;
|
|
|
|
$self->{fail_cnt}++;
|
|
|
|
$self->report($self->{driver_log_filename});
|
2019-06-29 17:37:01 +00:00
|
|
|
my $other = "";
|
2019-06-29 19:24:56 +00:00
|
|
|
foreach my $proc ($::Fork->running) {
|
2021-09-08 03:50:28 +00:00
|
|
|
$other .= " " . $proc->{test_pl_filename};
|
2019-06-29 17:37:01 +00:00
|
|
|
}
|
2021-09-08 03:50:28 +00:00
|
|
|
$test->oprint("Simultaneous running tests:", $other, "\n")
|
2019-08-04 19:04:34 +00:00
|
|
|
if $other && !$opt_quiet;
|
2019-06-29 17:37:01 +00:00
|
|
|
if ($opt_stop) { die "%Error: --stop and errors found\n"; }
|
|
|
|
}
|
2019-06-29 19:24:56 +00:00
|
|
|
$self->{left_cnt}--;
|
|
|
|
$self->print_summary;
|
2020-06-03 01:42:24 +00:00
|
|
|
delete $self->{running_ids}{$process->{running_id}} if $process->{running_id};
|
2019-06-29 17:37:01 +00:00
|
|
|
},
|
|
|
|
)->ready();
|
|
|
|
}
|
|
|
|
|
2019-06-29 19:24:56 +00:00
|
|
|
sub wait_and_report {
|
|
|
|
my $self = shift;
|
2021-09-08 03:50:28 +00:00
|
|
|
$self->print_summary(force => 1);
|
2019-10-10 12:27:15 +00:00
|
|
|
# Wait for all children to finish
|
2019-10-17 03:17:31 +00:00
|
|
|
while ($::Fork->is_any_left) {
|
|
|
|
$::Fork->poll;
|
2019-11-07 23:15:55 +00:00
|
|
|
if ((time() - ($self->{_last_summary_time} || 0) >= 30)
|
|
|
|
&& (!$opt_gdb && !$opt_gdbsim)) { # Don't show for interactive gdb etc
|
2021-09-08 03:50:28 +00:00
|
|
|
$self->print_summary(force => 1, show_running => 1);
|
2019-10-10 12:27:15 +00:00
|
|
|
}
|
2021-09-08 03:50:28 +00:00
|
|
|
Time::HiRes::usleep 100 * 1000;
|
2019-10-17 03:17:31 +00:00
|
|
|
}
|
2019-06-29 19:24:56 +00:00
|
|
|
$runner->report(undef);
|
|
|
|
$runner->report($self->{driver_log_filename});
|
|
|
|
}
|
|
|
|
|
2008-09-23 14:02:31 +00:00
|
|
|
sub report {
|
2019-06-29 19:24:56 +00:00
|
|
|
my $self = shift;
|
2008-09-23 14:02:31 +00:00
|
|
|
my $filename = shift;
|
|
|
|
|
|
|
|
my $fh = \*STDOUT;
|
|
|
|
if ($filename) {
|
2019-05-08 02:34:09 +00:00
|
|
|
$fh = IO::File->new(">$filename") or die "%Error: $! writing $filename,";
|
2008-09-23 14:02:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
$fh->print("\n");
|
2021-09-08 03:50:28 +00:00
|
|
|
$fh->print("=" x 70, "\n");
|
2019-06-29 19:24:56 +00:00
|
|
|
foreach my $f (sort @{$self->{fail_msgs}}) {
|
2019-05-08 02:34:09 +00:00
|
|
|
chomp $f;
|
|
|
|
$fh->print("$f\n");
|
2008-09-23 14:02:31 +00:00
|
|
|
}
|
2019-07-31 02:20:37 +00:00
|
|
|
foreach my $f (sort @{$self->{skip_msgs}}) {
|
|
|
|
chomp $f;
|
|
|
|
$fh->print("$f\n");
|
|
|
|
}
|
|
|
|
my $sum = ($self->{fail_cnt} && "FAILED"
|
|
|
|
|| $self->{skip_cnt} && "PASSED w/SKIPS"
|
|
|
|
|| "PASSED");
|
2021-09-08 03:50:28 +00:00
|
|
|
$fh->print("TESTS DONE, $sum: " . $self->sprint_summary . "\n");
|
2019-06-29 19:24:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sub print_summary {
|
|
|
|
my $self = shift;
|
|
|
|
my %params = (force => 0, # Force printing
|
2019-10-10 12:27:15 +00:00
|
|
|
show_running => 0, # Show running processes
|
2019-06-29 19:24:56 +00:00
|
|
|
@_);
|
2019-07-15 01:42:03 +00:00
|
|
|
if (!$self->{quiet} || $params{force}
|
|
|
|
|| ($self->{left_cnt} < 5)
|
2019-11-07 23:15:55 +00:00
|
|
|
|| (time() - ($self->{_last_summary_time} || 0) >= 15)) { # Don't show for interactive gdb etc
|
2019-10-10 12:27:15 +00:00
|
|
|
$self->{_last_summary_time} = time();
|
2021-09-08 03:50:28 +00:00
|
|
|
print STDERR ("==SUMMARY: " . $self->sprint_summary . "\n");
|
2019-10-10 12:27:15 +00:00
|
|
|
if ($params{show_running}) {
|
|
|
|
my $other;
|
|
|
|
foreach my $proc ($::Fork->running) {
|
2021-09-08 03:50:28 +00:00
|
|
|
$other .= " " . $proc->{test_pl_filename};
|
2019-10-10 12:27:15 +00:00
|
|
|
}
|
2021-09-08 03:50:28 +00:00
|
|
|
print STDERR ("==STILL RUNNING: " . $other . "\n");
|
2019-10-10 12:27:15 +00:00
|
|
|
}
|
2019-06-29 19:58:00 +00:00
|
|
|
}
|
2019-06-29 19:24:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sub sprint_summary {
|
|
|
|
my $self = shift;
|
|
|
|
|
2019-06-29 19:58:00 +00:00
|
|
|
my $delta = time() - $::Start;
|
2019-06-29 19:24:56 +00:00
|
|
|
my $leftmsg = $::Have_Forker ? $self->{left_cnt} : "NO-FORKER";
|
2021-09-08 03:50:28 +00:00
|
|
|
my $pct = int(100 * ($self->{left_cnt} / ($self->{all_cnt} + 0.001)) + 0.999);
|
2021-03-03 23:58:17 +00:00
|
|
|
# Fudge of 120% works out about right so ETA correctly predicts completion time
|
|
|
|
my $eta = 1.2 * (($self->{all_cnt}
|
|
|
|
* ($delta / (($self->{all_cnt} - $self->{left_cnt})+0.001)))
|
|
|
|
- $delta);
|
2019-10-05 11:54:14 +00:00
|
|
|
$eta = 0 if $delta < 10;
|
2019-06-29 19:24:56 +00:00
|
|
|
my $out = "";
|
2019-06-30 21:38:41 +00:00
|
|
|
$out .= "Left $leftmsg " if $self->{left_cnt};
|
|
|
|
$out .= "Passed $self->{ok_cnt}";
|
2019-06-29 21:40:31 +00:00
|
|
|
# Ordered below most severe to least severe
|
2019-06-29 19:24:56 +00:00
|
|
|
$out .= " Failed $self->{fail_cnt}";
|
2019-10-02 01:32:38 +00:00
|
|
|
$out .= " Failed-First $self->{fail1_cnt}" if $self->{fail1_cnt};
|
|
|
|
$out .= " Skipped $self->{skip_cnt}" if $self->{skip_cnt};
|
2021-09-08 03:50:28 +00:00
|
|
|
$out .= sprintf(" Eta %d:%02d", int($eta / 60), $eta % 60) if $self->{left_cnt} > 10 && $eta > 10;
|
|
|
|
$out .= sprintf(" Time %d:%02d", int($delta / 60), $delta % 60);
|
2019-06-29 19:24:56 +00:00
|
|
|
return $out;
|
2008-09-23 14:02:31 +00:00
|
|
|
}
|
|
|
|
|
2019-06-29 21:40:31 +00:00
|
|
|
sub _manual_args {
|
2018-05-08 00:42:28 +00:00
|
|
|
# Return command line with scenarios stripped
|
|
|
|
my @out;
|
|
|
|
arg:
|
|
|
|
foreach my $arg (@Orig_ARGV_Sw) {
|
|
|
|
foreach my $allsc (keys %All_Scenarios) {
|
|
|
|
foreach my $allscarg (@{$All_Scenarios{$allsc}}) {
|
|
|
|
next arg if ("--$allscarg" eq $arg);
|
|
|
|
}
|
|
|
|
}
|
2019-06-29 21:40:31 +00:00
|
|
|
# Also strip certain flags that per-test debugging won't want
|
|
|
|
next arg if $arg eq '--rerun';
|
|
|
|
next arg if $arg eq '--quiet';
|
2018-05-08 00:42:28 +00:00
|
|
|
push @out, $arg;
|
|
|
|
}
|
|
|
|
return @out;
|
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
#######################################################################
|
|
|
|
#######################################################################
|
|
|
|
#######################################################################
|
|
|
|
#######################################################################
|
|
|
|
# Test class
|
|
|
|
|
|
|
|
package VTest;
|
|
|
|
use Carp;
|
|
|
|
use Cwd;
|
2011-01-02 00:18:32 +00:00
|
|
|
use Data::Dumper;
|
|
|
|
use File::Spec;
|
2019-06-15 20:37:58 +00:00
|
|
|
use File::Path qw(mkpath);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2019-03-10 13:25:23 +00:00
|
|
|
use vars qw($Self $Self);
|
2006-08-26 11:35:28 +00:00
|
|
|
use strict;
|
|
|
|
|
2019-08-29 21:00:49 +00:00
|
|
|
sub defineOpt {
|
|
|
|
my $xsim = shift;
|
|
|
|
return $xsim ? "--define " : "+define+";
|
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
sub new {
|
|
|
|
my $class = shift;
|
2008-11-25 02:38:45 +00:00
|
|
|
my $self = {@_};
|
|
|
|
|
2011-01-02 00:18:32 +00:00
|
|
|
$self->{name} ||= $2 if $self->{pl_filename} =~ m!^(.*/)?([^/]*)\.pl$!;
|
2012-03-07 01:58:23 +00:00
|
|
|
|
2018-05-08 00:42:28 +00:00
|
|
|
$self->{scenario} = "";
|
|
|
|
$self->{scenario} ||= "dist" if $self->{dist};
|
|
|
|
$self->{scenario} ||= "atsim" if $self->{atsim};
|
|
|
|
$self->{scenario} ||= "ghdl" if $self->{ghdl};
|
|
|
|
$self->{scenario} ||= "vcs" if $self->{vcs};
|
|
|
|
$self->{scenario} ||= "vlt" if $self->{vlt};
|
2018-07-23 00:54:28 +00:00
|
|
|
$self->{scenario} ||= "vltmt" if $self->{vltmt};
|
2018-05-08 00:42:28 +00:00
|
|
|
$self->{scenario} ||= "nc" if $self->{nc};
|
|
|
|
$self->{scenario} ||= "ms" if $self->{ms};
|
|
|
|
$self->{scenario} ||= "iv" if $self->{iv};
|
2019-08-29 21:00:49 +00:00
|
|
|
$self->{scenario} ||= "xsim" if $self->{xsim};
|
2012-03-07 01:58:23 +00:00
|
|
|
|
2011-01-02 00:18:32 +00:00
|
|
|
foreach my $dir (@::Test_Dirs) {
|
2019-05-08 02:34:09 +00:00
|
|
|
# t_dir used both absolutely and under obj_dir
|
|
|
|
if (-e "$dir/$self->{name}.pl") {
|
|
|
|
# Note most tests require error messages of the form t/x.v
|
|
|
|
# Therefore pl_filename must be t/ for local tests
|
|
|
|
$self->{pl_filename} = File::Spec->abs2rel("$dir/$self->{name}.pl");
|
|
|
|
# t_dir must be absolute - used under t or under obj_dir
|
|
|
|
$self->{t_dir} ||= File::Spec->rel2abs($dir);
|
|
|
|
last;
|
|
|
|
}
|
2011-01-02 00:18:32 +00:00
|
|
|
}
|
|
|
|
$self->{t_dir} or die "%Error: Can't locate dir for $self->{name},";
|
2012-03-07 01:58:23 +00:00
|
|
|
|
2019-06-15 20:37:58 +00:00
|
|
|
if (!$self->{obj_dir}) {
|
|
|
|
my $scen_dir = File::Spec->abs2rel("$self->{t_dir}/../obj_$self->{scenario}");
|
|
|
|
$scen_dir =~ s!^t/\.\./!!; # Simplify filenames on local runs
|
|
|
|
mkdir $scen_dir; # Not a mkpath so find out if trying to build somewhere odd
|
2021-09-08 03:50:28 +00:00
|
|
|
$self->{obj_dir} = "$scen_dir/$self->{name}";
|
2019-06-15 20:37:58 +00:00
|
|
|
}
|
|
|
|
|
2019-08-29 21:00:49 +00:00
|
|
|
my $define_opt = defineOpt($self->{xsim});
|
|
|
|
|
2008-11-25 02:38:45 +00:00
|
|
|
$self = {
|
2019-05-08 02:34:09 +00:00
|
|
|
name => undef, # Set below, name of this test
|
|
|
|
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
|
|
|
|
make_pli => 0, # need to compile pli
|
2020-04-15 23:39:03 +00:00
|
|
|
sc_time_resolution => "SC_PS", # Keep - PS is SystemC default
|
2019-05-08 02:34:09 +00:00
|
|
|
sim_time => 1100,
|
2022-07-05 09:57:16 +00:00
|
|
|
threads => -1, # --threads (negative means auto based on scenario)
|
2022-07-12 10:41:15 +00:00
|
|
|
context_threads => 0, # Number of threads to allocate in the context
|
2019-05-08 02:34:09 +00:00
|
|
|
benchmark => $opt_benchmark,
|
|
|
|
verbose => $opt_verbose,
|
2022-12-03 00:57:40 +00:00
|
|
|
rerunnable => 1, # Rerun if fails
|
2019-05-08 02:34:09 +00:00
|
|
|
run_env => '',
|
|
|
|
# All compilers
|
2019-06-15 20:37:58 +00:00
|
|
|
v_flags => [split(/\s+/,
|
2019-08-29 21:00:49 +00:00
|
|
|
(($self->{xsim} ? " -f input.xsim.vc " :
|
|
|
|
(-r 'input.vc' ? " -f input.vc " : ""))
|
2019-06-15 20:37:58 +00:00
|
|
|
.($self->{t_dir} !~ m!/test_regress! # Don't include standard dir, only site's
|
|
|
|
? " +incdir+$self->{t_dir} -y $self->{t_dir}" : "")
|
2021-09-08 03:50:28 +00:00
|
|
|
. " " . $define_opt . "TEST_OBJ_DIR=$self->{obj_dir}"
|
2021-09-08 22:45:25 +00:00
|
|
|
.($opt_verbose ? " " . $define_opt . "TEST_VERBOSE=1" : "")
|
|
|
|
.($opt_benchmark ? " " . $define_opt . "TEST_BENCHMARK=$opt_benchmark" : "")
|
|
|
|
.($opt_trace ? " " . $define_opt . "WAVES=1" : "")
|
2019-06-15 20:37:58 +00:00
|
|
|
))],
|
2019-03-10 13:25:23 +00:00
|
|
|
v_flags2 => [], # Overridden in some sim files
|
|
|
|
v_other_filenames => [], # After the filename so we can spec multiple files
|
|
|
|
all_run_flags => [],
|
2019-05-08 02:34:09 +00:00
|
|
|
pli_flags => ["-I$ENV{VERILATOR_ROOT}/include/vltstd -fPIC -shared"
|
2021-09-08 03:50:28 +00:00
|
|
|
. (($^O eq "darwin" )
|
|
|
|
? " -Wl,-undefined,dynamic_lookup"
|
|
|
|
: " -export-dynamic")
|
2021-09-08 22:45:25 +00:00
|
|
|
. ($opt_verbose ? " -DTEST_VERBOSE=1" : "")
|
2021-09-08 03:50:28 +00:00
|
|
|
. (cfg_with_m32() ? " -m32" : "")
|
|
|
|
. " -o $self->{obj_dir}/libvpi.so"],
|
2021-02-04 00:29:24 +00:00
|
|
|
tool_c_flags => [],
|
2019-05-08 02:34:09 +00:00
|
|
|
# ATSIM
|
|
|
|
atsim => 0,
|
2021-02-04 00:29:24 +00:00
|
|
|
atsim_define => 'ATSIM',
|
2021-09-08 22:45:25 +00:00
|
|
|
atsim_flags => [split(/\s+/, "-c +sv +define+ATSIM"),
|
2019-05-08 02:34:09 +00:00
|
|
|
"+sv_dir+$self->{obj_dir}/.athdl_compile"],
|
|
|
|
atsim_flags2 => [], # Overridden in some sim files
|
|
|
|
atsim_run_flags => [],
|
2011-12-10 00:58:14 +00:00
|
|
|
# GHDL
|
2019-05-08 02:34:09 +00:00
|
|
|
ghdl => 0,
|
2021-02-04 00:29:24 +00:00
|
|
|
ghdl_define => 'GHDL',
|
2019-05-08 02:34:09 +00:00
|
|
|
ghdl_work_dir => "$self->{obj_dir}/ghdl_compile",
|
2021-09-08 22:45:25 +00:00
|
|
|
ghdl_flags => [($::Debug ? "-v" : ""),
|
2019-05-08 02:34:09 +00:00
|
|
|
"--workdir=$self->{obj_dir}/ghdl_compile", ],
|
|
|
|
ghdl_flags2 => [], # Overridden in some sim files
|
|
|
|
ghdl_run_flags => [],
|
2009-10-02 02:32:40 +00:00
|
|
|
# IV
|
2019-05-08 02:34:09 +00:00
|
|
|
iv => 0,
|
2021-02-04 00:29:24 +00:00
|
|
|
iv_define => 'IVERILOG',
|
2021-09-08 22:45:25 +00:00
|
|
|
iv_flags => [split(/\s+/, "+define+IVERILOG -g2012 -o $self->{obj_dir}/simiv")],
|
2019-03-10 13:25:23 +00:00
|
|
|
iv_flags2 => [], # Overridden in some sim files
|
|
|
|
iv_pli => 0, # need to use pli
|
|
|
|
iv_run_flags => [],
|
2019-05-08 02:34:09 +00:00
|
|
|
# VCS
|
|
|
|
vcs => 0,
|
2021-02-04 00:29:24 +00:00
|
|
|
vcs_define => 'VCS',
|
2021-09-08 22:45:25 +00:00
|
|
|
vcs_flags => [split(/\s+/, "+vcs+lic+wait +cli -debug_access +define+VCS+1 -q -sverilog -CFLAGS '-DVCS' ")],
|
2019-05-08 02:34:09 +00:00
|
|
|
vcs_flags2 => [], # Overridden in some sim files
|
2021-09-08 22:45:25 +00:00
|
|
|
vcs_run_flags => [split(/\s+/, "+vcs+lic_wait")],
|
2019-05-08 02:34:09 +00:00
|
|
|
# NC
|
|
|
|
nc => 0,
|
2021-02-04 00:29:24 +00:00
|
|
|
nc_define => 'NC',
|
2021-09-08 22:45:25 +00:00
|
|
|
nc_flags => [split(/\s+/, ("+licqueue +nowarn+LIBNOU +define+NC=1 -q +assert +sv -c "
|
|
|
|
. ($opt_trace ? " +access+r" : "")))],
|
2019-05-08 02:34:09 +00:00
|
|
|
nc_flags2 => [], # Overridden in some sim files
|
2021-09-08 22:45:25 +00:00
|
|
|
nc_run_flags => [split(/\s+/, "+licqueue -q +assert +sv -R")],
|
2019-05-08 02:34:09 +00:00
|
|
|
# ModelSim
|
|
|
|
ms => 0,
|
2021-02-04 00:29:24 +00:00
|
|
|
ms_define => 'MS',
|
2020-04-07 23:07:47 +00:00
|
|
|
ms_flags => [split(/\s+/, ("-sv -work $self->{obj_dir}/work +define+MS=1 -ccflags \"-DMS=1\""))],
|
2019-05-08 02:34:09 +00:00
|
|
|
ms_flags2 => [], # Overridden in some sim files
|
2019-11-20 02:53:17 +00:00
|
|
|
ms_pli => 1, # need to use pli
|
2021-09-08 22:45:25 +00:00
|
|
|
ms_run_flags => [split(/\s+/, "-lib $self->{obj_dir}/work -c -do 'run -all;quit' ")],
|
2019-08-29 21:00:49 +00:00
|
|
|
# XSim
|
|
|
|
xsim => 0,
|
2021-02-04 00:29:24 +00:00
|
|
|
xsim_define => 'XSIM',
|
2021-09-08 22:45:25 +00:00
|
|
|
xsim_flags => [split(/\s+/, ("--nolog --sv --define XSIM --work $self->{name}=$self->{obj_dir}/xsim"))],
|
2019-08-29 21:00:49 +00:00
|
|
|
xsim_flags2 => [], # Overridden in some sim files
|
2021-09-08 22:45:25 +00:00
|
|
|
xsim_run_flags => [split(/\s+/, ("--nolog --runall --lib $self->{name}=$self->{obj_dir}/xsim"
|
|
|
|
.($opt_trace ? " --debug all" : "")))],
|
2019-09-26 19:54:51 +00:00
|
|
|
xsim_run_flags2 => [], # Overridden in some sim files
|
2019-05-08 02:34:09 +00:00
|
|
|
# Verilator
|
|
|
|
vlt => 0,
|
2018-07-23 00:54:28 +00:00
|
|
|
vltmt => 0,
|
2021-02-04 00:29:24 +00:00
|
|
|
verilator_define => 'VERILATOR',
|
2019-05-08 02:34:09 +00:00
|
|
|
verilator_flags => ["-cc",
|
|
|
|
"-Mdir $self->{obj_dir}",
|
2022-06-04 00:43:16 +00:00
|
|
|
"--fdedup", # As currently disabled unless -O3
|
2017-12-09 16:52:35 +00:00
|
|
|
"--debug-check",
|
|
|
|
"--comp-limit-members 10", ],
|
2019-05-08 02:34:09 +00:00
|
|
|
verilator_flags2 => [],
|
|
|
|
verilator_flags3 => ["--clk clk"],
|
2019-10-08 02:15:43 +00:00
|
|
|
verilator_make_gmake => 1,
|
2019-10-17 23:44:10 +00:00
|
|
|
verilator_make_cmake => 0,
|
2019-03-10 13:25:23 +00:00
|
|
|
verilated_debug => $Opt_Verilated_Debug,
|
|
|
|
stdout_filename => undef, # Redirect stdout
|
|
|
|
%$self};
|
2006-08-26 11:35:28 +00:00
|
|
|
bless $self, $class;
|
|
|
|
|
2018-07-23 00:54:28 +00:00
|
|
|
$self->{vlt_all} = $self->{vlt} || $self->{vltmt}; # Any Verilator scenario
|
2018-05-08 23:39:32 +00:00
|
|
|
|
2021-09-08 03:50:28 +00:00
|
|
|
$self->{VM_PREFIX} ||= "V" . $self->{name};
|
|
|
|
$self->{stats} ||= "$self->{obj_dir}/V" . $self->{name} . "__stats.txt";
|
|
|
|
$self->{status_filename} ||= "$self->{obj_dir}/V" . $self->{name} . ".status";
|
2010-01-06 13:54:56 +00:00
|
|
|
$self->{run_log_filename} ||= "$self->{obj_dir}/vlt_sim.log";
|
2014-11-24 02:06:10 +00:00
|
|
|
$self->{coverage_filename} ||= "$self->{obj_dir}/coverage.dat";
|
2011-07-06 00:42:33 +00:00
|
|
|
$self->{main_filename} ||= "$self->{obj_dir}/$self->{VM_PREFIX}__main.cpp";
|
2018-11-02 01:04:19 +00:00
|
|
|
($self->{top_filename} ||= $self->{pl_filename}) =~ s/\.pl$//;
|
|
|
|
($self->{golden_filename} ||= $self->{pl_filename}) =~ s/\.pl$/.out/;
|
2021-09-08 03:50:28 +00:00
|
|
|
if (-e ($self->{top_filename} . ".vhd")) { # If VHDL file exists
|
2019-03-10 13:25:23 +00:00
|
|
|
$self->{vhdl} = 1;
|
2012-02-12 01:40:58 +00:00
|
|
|
$self->{top_filename} .= ".vhd";
|
|
|
|
} else {
|
|
|
|
$self->{top_filename} .= ".v";
|
|
|
|
}
|
|
|
|
if (!$self->{make_top_shell}) {
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->{top_shell_filename} = $self->{top_filename};
|
2012-02-12 01:40:58 +00:00
|
|
|
} else {
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->{top_shell_filename} = "$self->{obj_dir}/$self->{VM_PREFIX}__top.v";
|
2012-02-12 01:40:58 +00:00
|
|
|
}
|
2021-09-08 03:50:28 +00:00
|
|
|
$self->{pli_filename} ||= $self->{name} . ".cpp";
|
2022-11-20 14:40:02 +00:00
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
return $self;
|
|
|
|
}
|
|
|
|
|
2021-07-01 15:17:55 +00:00
|
|
|
sub benchmarksim_filename {
|
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2021-09-08 03:50:28 +00:00
|
|
|
return $self->{obj_dir} . "/$self->{name}_benchmarksim.csv";
|
2021-07-01 15:17:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sub init_benchmarksim {
|
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
|
|
|
# Simulations with benchmarksim enabled append to the same file between runs.
|
|
|
|
# Test files must ensure a clean benchmark data file before executing tests.
|
|
|
|
my $filename = $self->benchmarksim_filename();
|
2021-09-08 03:50:28 +00:00
|
|
|
my $fh = IO::File->new(">" . $filename) or die "%Error: $! " . $filename;
|
2021-07-01 15:17:55 +00:00
|
|
|
print $fh "# Verilator simulation benchmark data\n";
|
2021-09-08 03:50:28 +00:00
|
|
|
print $fh "# Test name: " . $self->{name} . "\n";
|
|
|
|
print $fh "# Top file: " . $self->{top_filename} . "\n";
|
2021-07-01 15:17:55 +00:00
|
|
|
print $fh "evals, time[s]\n";
|
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
sub soprint {
|
2018-05-08 00:42:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2021-09-08 03:50:28 +00:00
|
|
|
my $str = "$self->{scenario}/$self->{name}: " . join('', @_);
|
2006-08-26 11:35:28 +00:00
|
|
|
$str =~ s/\n\n+$/\n/s;
|
|
|
|
return $str;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub oprint {
|
2018-05-08 00:42:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2006-08-26 11:35:28 +00:00
|
|
|
print $self->soprint(@_);
|
|
|
|
}
|
|
|
|
|
|
|
|
sub error {
|
2018-05-08 00:42:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2021-09-08 03:50:28 +00:00
|
|
|
my $msg = join('', @_);
|
2018-05-08 00:42:28 +00:00
|
|
|
# Called from tests as: error("Reason message"[, ...]);
|
2021-09-08 03:50:28 +00:00
|
|
|
warn "%Warning: $self->{scenario}/$self->{name}: " . $msg . "\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
$self->{errors} ||= $msg;
|
|
|
|
}
|
|
|
|
|
2021-03-18 22:45:34 +00:00
|
|
|
sub error_keep_going {
|
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2021-09-08 03:50:28 +00:00
|
|
|
my $msg = join('', @_);
|
2021-03-18 22:45:34 +00:00
|
|
|
# Called from tests as: error_keep_going("Reason message"[, ...]);
|
2021-09-08 03:50:28 +00:00
|
|
|
warn "%Warning: $self->{scenario}/$self->{name}: " . $msg . "\n";
|
2021-03-18 22:45:34 +00:00
|
|
|
$self->{errors_keep_going} ||= $msg;
|
|
|
|
}
|
|
|
|
|
2008-06-12 00:33:53 +00:00
|
|
|
sub skip {
|
2018-05-08 00:42:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2021-09-08 03:50:28 +00:00
|
|
|
my $msg = join('', @_);
|
2018-05-08 00:42:28 +00:00
|
|
|
# Called from tests as: skip("Reason message"[, ...]);
|
2021-09-08 03:50:28 +00:00
|
|
|
warn "-Skip: $self->{scenario}/$self->{name}: " . $msg . "\n";
|
|
|
|
$self->{skips} ||= "Skip: " . $msg;
|
2008-06-12 00:33:53 +00:00
|
|
|
}
|
|
|
|
|
2018-05-08 00:42:28 +00:00
|
|
|
sub scenarios {
|
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
|
|
|
my %params = (@_);
|
|
|
|
# Called from tests as: scenarios(...);
|
|
|
|
# to specify which scenarios this test runs under.
|
|
|
|
# Where ... is one cases listed in All_Scenarios
|
|
|
|
if ((scalar keys %params) < 1) {
|
|
|
|
$params{simulators} = 1;
|
|
|
|
}
|
|
|
|
my %enabled_scenarios;
|
|
|
|
foreach my $scenario (keys %params) {
|
|
|
|
my $value = $params{$scenario};
|
|
|
|
my $hit = 0;
|
|
|
|
foreach my $allsc (keys %All_Scenarios) {
|
|
|
|
foreach my $allscarg (@{$All_Scenarios{$allsc}}) {
|
|
|
|
if ($scenario eq $allscarg) {
|
|
|
|
$hit = 1;
|
|
|
|
$enabled_scenarios{$allsc} = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!$hit) {
|
|
|
|
$self->error("scenarios('$scenario' => ...) has unknown scenario '$scenario',");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$enabled_scenarios{$self->{scenario}}) {
|
|
|
|
$self->skip("scenario '$self->{scenario}' not enabled for test");
|
|
|
|
$self->{scenario_off} ||= 1;
|
|
|
|
$self->_exit();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub _prep {
|
2008-11-25 02:38:45 +00:00
|
|
|
my $self = shift;
|
|
|
|
mkdir $self->{obj_dir}; # Ok if already exists
|
|
|
|
}
|
|
|
|
|
2018-05-08 00:42:28 +00:00
|
|
|
sub _read {
|
2006-08-26 11:35:28 +00:00
|
|
|
my $self = shift;
|
|
|
|
# Read the control file
|
|
|
|
(-r $self->{pl_filename})
|
2019-05-08 02:34:09 +00:00
|
|
|
or return $self->error("Can't open $self->{pl_filename}\n");
|
2008-11-25 02:38:45 +00:00
|
|
|
$Self = $self;
|
2006-08-26 11:35:28 +00:00
|
|
|
delete $INC{$self->{pl_filename}};
|
|
|
|
require $self->{pl_filename};
|
|
|
|
}
|
|
|
|
|
2018-05-08 00:42:28 +00:00
|
|
|
sub _exit {
|
|
|
|
my $self = shift;
|
|
|
|
if ($self->ok) {
|
|
|
|
$self->oprint("Self PASSED\n");
|
|
|
|
} elsif ($self->skips && !$self->errors) {
|
2020-12-06 03:58:36 +00:00
|
|
|
$self->oprint("-Skip: $self->{skips}\n");
|
2018-05-08 00:42:28 +00:00
|
|
|
} else {
|
|
|
|
$self->error("Missing ok\n") if !$self->errors;
|
|
|
|
$self->oprint("%Error: $self->{errors}\n");
|
|
|
|
}
|
|
|
|
$self->_write_status;
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
sub _write_status {
|
2006-08-26 11:35:28 +00:00
|
|
|
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,";
|
2019-06-29 19:24:56 +00:00
|
|
|
$Data::Dumper::Indent = 1;
|
|
|
|
$Data::Dumper::Sortkeys = 1;
|
2006-08-26 11:35:28 +00:00
|
|
|
print $fh Dumper($self);
|
|
|
|
print $fh "1;";
|
|
|
|
$fh->close();
|
|
|
|
}
|
|
|
|
|
2018-05-08 00:42:28 +00:00
|
|
|
sub _read_status {
|
2006-08-26 11:35:28 +00:00
|
|
|
my $self = shift;
|
|
|
|
my $filename = $self->{status_filename};
|
|
|
|
use vars qw($VAR1);
|
|
|
|
local $VAR1;
|
2018-05-08 23:39:32 +00:00
|
|
|
if (!-r $filename) {
|
|
|
|
$self->error("driver.pl _read_status file missing: $filename");
|
|
|
|
return;
|
|
|
|
}
|
2019-06-29 19:24:56 +00:00
|
|
|
{
|
|
|
|
local %INC = ();
|
|
|
|
require $filename or die "%Error: $! $filename,";
|
|
|
|
}
|
2009-12-03 00:32:41 +00:00
|
|
|
if ($VAR1) {
|
2019-05-08 02:34:09 +00:00
|
|
|
%{$self} = %{$VAR1};
|
2019-06-29 19:24:56 +00:00
|
|
|
} else {
|
|
|
|
$self->error("driver.pl _read_status file empty: $filename");
|
|
|
|
return;
|
2009-12-03 00:32:41 +00:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
# Methods invoked by tests
|
|
|
|
|
2020-04-11 15:40:15 +00:00
|
|
|
sub clean {
|
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
|
|
|
# Called on a rerun to cleanup files
|
|
|
|
if ($self->{clean_command}) {
|
|
|
|
system($self->{clean_command});
|
|
|
|
}
|
|
|
|
if (1) {
|
|
|
|
# Prevents false-failures when switching compilers
|
|
|
|
# Remove old results to force hard rebuild
|
|
|
|
system("rm", "-rf", "$self->{obj_dir}__fail1");
|
|
|
|
system("mv", "$self->{obj_dir}", "$self->{obj_dir}__fail1");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-24 01:27:06 +00:00
|
|
|
sub clean_objs {
|
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
|
|
|
system("rm", "-rf", glob("$self->{obj_dir}/*"));
|
|
|
|
}
|
|
|
|
|
2019-10-17 23:44:10 +00:00
|
|
|
sub compile_vlt_cmd {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2019-10-17 23:44:10 +00:00
|
|
|
my %param = (%{$self}, @_); # Default arguments are from $self
|
2022-11-20 03:43:10 +00:00
|
|
|
return 1 if $self->errors || $self->skips;
|
2019-10-17 23:44:10 +00:00
|
|
|
|
|
|
|
my @vlt_cmd = (
|
|
|
|
"perl", "$ENV{VERILATOR_ROOT}/bin/verilator",
|
|
|
|
$self->compile_vlt_flags(%param),
|
|
|
|
$param{top_filename},
|
|
|
|
@{$param{v_other_filenames}},
|
2021-09-08 03:50:28 +00:00
|
|
|
$param{stdout_filename} ? "> " . $param{stdout_filename} : ""
|
2019-10-17 23:44:10 +00:00
|
|
|
);
|
|
|
|
return @vlt_cmd;
|
|
|
|
}
|
|
|
|
|
2011-05-20 01:25:23 +00:00
|
|
|
sub compile_vlt_flags {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2019-03-10 13:25:23 +00:00
|
|
|
my %param = (%{$self}, @_); # Default arguments are from $self
|
2022-11-20 03:43:10 +00:00
|
|
|
return 1 if $self->errors || $self->skips;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2021-09-08 03:50:28 +00:00
|
|
|
my $checkflags = join(' ', @{$param{v_flags}},
|
2019-05-08 02:34:09 +00:00
|
|
|
@{$param{v_flags2}},
|
|
|
|
@{$param{verilator_flags}},
|
|
|
|
@{$param{verilator_flags2}},
|
|
|
|
@{$param{verilator_flags3}});
|
2022-11-05 12:47:34 +00:00
|
|
|
die "%Error: specify threads via 'threads =>' argument, not as a command line option" unless ($checkflags !~ /(^|\s)-?-threads\s/);
|
2008-12-05 15:54:14 +00:00
|
|
|
$self->{sc} = 1 if ($checkflags =~ /-sc\b/);
|
2018-10-02 22:42:53 +00:00
|
|
|
$self->{trace} = ($opt_trace || $checkflags =~ /-trace\b/
|
2020-04-21 22:49:07 +00:00
|
|
|
|| $checkflags =~ /-trace-fst\b/);
|
2021-04-06 20:18:58 +00:00
|
|
|
$self->{trace_format} = (($checkflags =~ /-trace-fst/ && $self->{sc} && 'fst-sc')
|
|
|
|
|| ($checkflags =~ /-trace-fst/ && !$self->{sc} && 'fst-c')
|
2018-08-28 10:41:17 +00:00
|
|
|
|| ($self->{sc} && 'vcd-sc')
|
|
|
|
|| (!$self->{sc} && 'vcd-c'));
|
2012-08-27 01:13:47 +00:00
|
|
|
$self->{savable} = 1 if ($checkflags =~ /-savable\b/);
|
2008-12-05 15:54:14 +00:00
|
|
|
$self->{coverage} = 1 if ($checkflags =~ /-coverage\b/);
|
2020-12-16 02:07:27 +00:00
|
|
|
$self->{sanitize} = $opt_sanitize unless exists($self->{sanitize});
|
2021-07-01 15:17:55 +00:00
|
|
|
$self->{benchmarksim} = 1 if ($param{benchmarksim});
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2011-05-20 01:25:23 +00:00
|
|
|
my @verilator_flags = @{$param{verilator_flags}};
|
2012-03-09 23:37:38 +00:00
|
|
|
unshift @verilator_flags, "--gdb" if $opt_gdb;
|
2011-05-20 01:25:23 +00:00
|
|
|
unshift @verilator_flags, "--gdbbt" if $opt_gdbbt;
|
2019-07-26 01:34:09 +00:00
|
|
|
unshift @verilator_flags, "--rr" if $opt_rr;
|
2011-05-20 01:25:23 +00:00
|
|
|
unshift @verilator_flags, "--x-assign unique"; # More likely to be buggy
|
|
|
|
unshift @verilator_flags, "--trace" if $opt_trace;
|
2022-07-05 09:57:16 +00:00
|
|
|
unshift @verilator_flags, "--threads $param{threads}" if $param{threads} >= 0;
|
2020-04-21 22:49:07 +00:00
|
|
|
unshift @verilator_flags, "--trace-threads 2" if $param{vltmt} && $checkflags =~ /-trace-fst /;
|
2018-07-23 00:54:28 +00:00
|
|
|
unshift @verilator_flags, "--debug-partition" if $param{vltmt};
|
2021-02-26 00:20:11 +00:00
|
|
|
unshift @verilator_flags, "-CFLAGS -ggdb -LDFLAGS -ggdb" if $opt_gdbsim;
|
2021-05-08 12:20:26 +00:00
|
|
|
unshift @verilator_flags, "-CFLAGS -fsanitize=address,undefined -LDFLAGS -fsanitize=address,undefined" if $param{sanitize};
|
2019-10-17 23:44:10 +00:00
|
|
|
unshift @verilator_flags, "--make gmake" if $param{verilator_make_gmake};
|
|
|
|
unshift @verilator_flags, "--make cmake" if $param{verilator_make_cmake};
|
2019-11-02 15:15:58 +00:00
|
|
|
unshift @verilator_flags, "--exe" if
|
|
|
|
$param{make_main} && $param{verilator_make_gmake};
|
2021-09-08 03:50:28 +00:00
|
|
|
unshift @verilator_flags, "../" . $self->{main_filename} if
|
2019-11-02 15:15:58 +00:00
|
|
|
$param{make_main} && $param{verilator_make_gmake};
|
2011-05-20 01:25:23 +00:00
|
|
|
|
2019-10-17 23:44:10 +00:00
|
|
|
my @cmdargs = (
|
2021-09-08 03:50:28 +00:00
|
|
|
"--prefix " . $param{VM_PREFIX},
|
2019-05-08 02:34:09 +00:00
|
|
|
@verilator_flags,
|
|
|
|
@{$param{verilator_flags2}},
|
|
|
|
@{$param{verilator_flags3}},
|
|
|
|
@{$param{v_flags}},
|
|
|
|
@{$param{v_flags2}},
|
2018-07-05 22:52:25 +00:00
|
|
|
# Flags from driver cmdline override default flags and
|
|
|
|
# flags from the test itself
|
2020-05-30 12:19:44 +00:00
|
|
|
driver_verilator_flags(),
|
2019-05-08 02:34:09 +00:00
|
|
|
);
|
2011-05-20 01:25:23 +00:00
|
|
|
return @cmdargs;
|
|
|
|
}
|
|
|
|
|
2020-05-30 12:19:44 +00:00
|
|
|
sub driver_verilator_flags {
|
2021-09-08 03:50:28 +00:00
|
|
|
# my $self = (ref $_[0] ? shift : $Self);
|
|
|
|
return @Opt_Driver_Verilator_Flags;
|
2020-05-30 12:19:44 +00:00
|
|
|
}
|
|
|
|
|
2019-06-13 01:05:02 +00:00
|
|
|
sub lint {
|
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
|
|
|
my %param = (#
|
|
|
|
%{$self}, # Default arguments are from $self
|
|
|
|
# Lint specific default overrides
|
|
|
|
make_main => 0,
|
|
|
|
make_top_shell => 0,
|
|
|
|
verilator_flags2 => ["--lint-only"],
|
2019-10-08 02:15:43 +00:00
|
|
|
verilator_make_gmake => 0,
|
2019-06-13 01:05:02 +00:00
|
|
|
@_);
|
|
|
|
$self->compile(%param);
|
|
|
|
}
|
|
|
|
|
2011-05-20 01:25:23 +00:00
|
|
|
sub compile {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2019-06-30 21:38:41 +00:00
|
|
|
my %param = (tee => 1,
|
|
|
|
%{$self}, @_); # Default arguments are from $self
|
2022-11-20 03:43:10 +00:00
|
|
|
return 1 if $self->errors || $self->skips;
|
2019-06-29 17:11:30 +00:00
|
|
|
$self->oprint("Compile\n") if $self->{verbose};
|
2011-05-20 01:25:23 +00:00
|
|
|
|
2022-07-05 09:57:16 +00:00
|
|
|
die "%Error: 'threads =>' argument must be <= 1 for vlt scenario" if $param{vlt} && $param{threads} > 1;
|
2022-07-12 10:41:15 +00:00
|
|
|
# Compute automatic parameter values
|
|
|
|
$param{threads} = ::calc_threads($Vltmt_threads) if $param{threads} < 0 && $param{vltmt};
|
|
|
|
$param{context_threads} = $param{threads} >= 1 ? $param{threads} : 1 if !$param{context_threads};
|
|
|
|
$self->{threads} = $param{threads};
|
|
|
|
$self->{context_threads} = $param{context_threads};
|
2022-07-05 09:57:16 +00:00
|
|
|
|
2019-10-17 23:44:10 +00:00
|
|
|
compile_vlt_cmd(%param);
|
2011-05-20 01:25:23 +00:00
|
|
|
|
2022-11-20 14:40:02 +00:00
|
|
|
my $define_opt = defineOpt($self->{xsim});
|
|
|
|
if (join(' ', @{$self->{v_flags}}) !~ /TEST_DUMPFILE/) {
|
|
|
|
push @{$self->{v_flags}}, ($define_opt . "TEST_DUMPFILE=" . $self->trace_filename);
|
|
|
|
}
|
|
|
|
|
2021-04-01 12:52:48 +00:00
|
|
|
if (!$param{make_top_shell}) {
|
2019-05-08 02:34:09 +00:00
|
|
|
$param{top_shell_filename}
|
2021-04-01 12:52:48 +00:00
|
|
|
= $self->{top_shell_filename} = "";
|
2011-12-10 00:58:14 +00:00
|
|
|
} else {
|
2019-05-08 02:34:09 +00:00
|
|
|
$param{top_shell_filename}
|
2021-09-08 03:50:28 +00:00
|
|
|
= $self->{top_shell_filename} = "$self->{obj_dir}/$self->{VM_PREFIX}__top." . $self->v_suffix;
|
2011-12-10 00:58:14 +00:00
|
|
|
}
|
|
|
|
|
2010-03-18 16:03:08 +00:00
|
|
|
if ($param{atsim}) {
|
2021-02-04 00:29:24 +00:00
|
|
|
$param{tool_define} ||= $param{atsim_define};
|
2021-04-01 12:52:48 +00:00
|
|
|
$self->_make_top() if $param{make_top_shell};
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/atsim_compile.log",
|
|
|
|
fails=>$param{fails},
|
2021-09-08 03:50:28 +00:00
|
|
|
cmd=>[($ENV{VERILATOR_ATSIM} || "atsim"),
|
2019-05-08 02:34:09 +00:00
|
|
|
@{$param{atsim_flags}},
|
|
|
|
@{$param{atsim_flags2}},
|
|
|
|
@{$param{v_flags}},
|
|
|
|
@{$param{v_flags2}},
|
|
|
|
$param{top_filename},
|
|
|
|
$param{top_shell_filename},
|
|
|
|
@{$param{v_other_filenames}},
|
|
|
|
]);
|
2010-03-18 16:03:08 +00:00
|
|
|
}
|
2011-12-10 00:58:14 +00:00
|
|
|
elsif ($param{ghdl}) {
|
2021-02-04 00:29:24 +00:00
|
|
|
$param{tool_define} ||= $param{ghdl_define};
|
2019-05-08 02:34:09 +00:00
|
|
|
mkdir $self->{ghdl_work_dir};
|
2021-04-01 12:52:48 +00:00
|
|
|
$self->_make_top() if $param{make_top_shell};
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/ghdl_compile.log",
|
|
|
|
fails=>$param{fails},
|
2021-09-08 03:50:28 +00:00
|
|
|
cmd=>[($ENV{VERILATOR_GHDL} || "ghdl"),
|
2019-05-08 02:34:09 +00:00
|
|
|
# Add -c here, as having -c twice freaks it out
|
2021-09-08 03:50:28 +00:00
|
|
|
((($ENV{VERILATOR_GHDL} || ' ') =~ / -c\b/) ? "" : "-c"),
|
2019-05-08 02:34:09 +00:00
|
|
|
@{$param{ghdl_flags}},
|
|
|
|
@{$param{ghdl_flags2}},
|
|
|
|
#@{$param{v_flags}}, # Not supported
|
|
|
|
#@{$param{v_flags2}}, # Not supported
|
|
|
|
$param{top_filename},
|
|
|
|
$param{top_shell_filename},
|
|
|
|
@{$param{v_other_filenames}},
|
|
|
|
"-e t",
|
|
|
|
]);
|
2011-12-10 00:58:14 +00:00
|
|
|
}
|
|
|
|
elsif ($param{vcs}) {
|
2021-02-04 00:29:24 +00:00
|
|
|
$param{tool_define} ||= $param{vcs_define};
|
2021-04-01 12:52:48 +00:00
|
|
|
$self->_make_top() if $param{make_top_shell};
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/vcs_compile.log",
|
|
|
|
fails=>$param{fails},
|
2021-09-08 03:50:28 +00:00
|
|
|
cmd=>[($ENV{VERILATOR_VCS} || "vcs"),
|
2019-05-08 02:34:09 +00:00
|
|
|
@{$param{vcs_flags}},
|
|
|
|
@{$param{vcs_flags2}},
|
2021-09-08 22:45:25 +00:00
|
|
|
($opt_verbose ? " -CFLAGS -DTEST_VERBOSE=1" : ""),
|
2019-05-08 02:34:09 +00:00
|
|
|
@{$param{v_flags}},
|
|
|
|
@{$param{v_flags2}},
|
|
|
|
$param{top_filename},
|
|
|
|
$param{top_shell_filename},
|
|
|
|
@{$param{v_other_filenames}},
|
|
|
|
]);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2011-12-10 00:58:14 +00:00
|
|
|
elsif ($param{nc}) {
|
2021-02-04 00:29:24 +00:00
|
|
|
$param{tool_define} ||= $param{nc_define};
|
2021-04-01 12:52:48 +00:00
|
|
|
$self->_make_top() if $param{make_top_shell};
|
2019-05-08 02:34:09 +00:00
|
|
|
my @more_args;
|
|
|
|
if ($self->vhdl) {
|
|
|
|
((my $ts = $param{top_shell_filename}) =~ s!\.v!!);
|
2021-09-08 03:50:28 +00:00
|
|
|
$ts =~ s!.*/!!;
|
2019-05-08 02:34:09 +00:00
|
|
|
push @more_args, "-vhdltop", $ts;
|
|
|
|
}
|
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/nc_compile.log",
|
|
|
|
fails=>$param{fails},
|
2021-09-08 03:50:28 +00:00
|
|
|
cmd=>[($ENV{VERILATOR_NCVERILOG} || "ncverilog"),
|
2019-05-08 02:34:09 +00:00
|
|
|
@{$param{nc_flags}},
|
|
|
|
@{$param{nc_flags2}},
|
|
|
|
@{$param{v_flags}},
|
|
|
|
@{$param{v_flags2}},
|
|
|
|
$param{top_filename},
|
|
|
|
$param{top_shell_filename},
|
|
|
|
@{$param{v_other_filenames}},
|
|
|
|
@more_args
|
|
|
|
]);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2015-02-11 01:16:03 +00:00
|
|
|
elsif ($param{ms}) {
|
2021-02-04 00:29:24 +00:00
|
|
|
$param{tool_define} ||= $param{ms_define};
|
2021-04-01 12:52:48 +00:00
|
|
|
$self->_make_top() if $param{make_top_shell};
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/ms_compile.log",
|
|
|
|
fails=>$param{fails},
|
|
|
|
cmd=>[("vlib $self->{obj_dir}/work && "),
|
2021-09-08 03:50:28 +00:00
|
|
|
($ENV{VERILATOR_MODELSIM} || "vlog"),
|
2019-05-08 02:34:09 +00:00
|
|
|
@{$param{ms_flags}},
|
|
|
|
@{$param{ms_flags2}},
|
|
|
|
@{$param{v_flags}},
|
|
|
|
@{$param{v_flags2}},
|
|
|
|
$param{top_filename},
|
|
|
|
$param{top_shell_filename},
|
|
|
|
@{$param{v_other_filenames}}
|
|
|
|
]);
|
2015-02-11 01:16:03 +00:00
|
|
|
}
|
2011-12-10 00:58:14 +00:00
|
|
|
elsif ($param{iv}) {
|
2021-02-04 00:29:24 +00:00
|
|
|
$param{tool_define} ||= $param{iv_define};
|
2021-04-01 12:52:48 +00:00
|
|
|
$self->_make_top() if $param{make_top_shell};
|
2021-09-08 03:50:28 +00:00
|
|
|
my @cmd = (($ENV{VERILATOR_IVERILOG} || "iverilog"),
|
2019-05-08 02:34:09 +00:00
|
|
|
@{$param{iv_flags}},
|
|
|
|
@{$param{iv_flags2}},
|
|
|
|
@{$param{v_flags}},
|
|
|
|
@{$param{v_flags2}},
|
|
|
|
$param{top_filename},
|
|
|
|
$param{top_shell_filename},
|
|
|
|
@{$param{v_other_filenames}});
|
|
|
|
@cmd = grep { s/\+define\+/-D /g; $_; } @cmd;
|
|
|
|
|
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/iv_compile.log",
|
|
|
|
fails=>$param{fails},
|
|
|
|
cmd=>\@cmd);
|
2009-10-02 02:32:40 +00:00
|
|
|
}
|
2019-08-29 21:00:49 +00:00
|
|
|
elsif ($param{xsim}) {
|
2021-02-04 00:29:24 +00:00
|
|
|
$param{tool_define} ||= $param{xsim_define};
|
2021-04-01 12:52:48 +00:00
|
|
|
$self->_make_top() if $param{make_top_shell};
|
2019-08-29 21:00:49 +00:00
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/xsim_compile.log",
|
|
|
|
fails=>$param{fails},
|
2021-09-08 03:50:28 +00:00
|
|
|
cmd=>[($ENV{VERILATOR_XVLOG} || "xvlog"),
|
2019-08-29 21:00:49 +00:00
|
|
|
@{$param{xsim_flags}},
|
|
|
|
@{$param{xsim_flags2}},
|
|
|
|
@{$param{v_flags}},
|
|
|
|
@{$param{v_flags2}},
|
|
|
|
$param{top_filename},
|
|
|
|
$param{top_shell_filename},
|
|
|
|
@{$param{v_other_filenames}}
|
|
|
|
]);
|
|
|
|
}
|
2018-05-08 23:39:32 +00:00
|
|
|
elsif ($param{vlt_all}) {
|
2021-02-04 00:29:24 +00:00
|
|
|
$param{tool_define} ||= $param{verilator_define};
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2019-05-08 02:34:09 +00:00
|
|
|
if ($self->sc && !$self->have_sc) {
|
|
|
|
$self->skip("Test requires SystemC; ignore error since not installed\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2019-10-17 23:44:10 +00:00
|
|
|
if ($param{verilator_make_cmake} && !$self->have_cmake) {
|
|
|
|
$self->skip("Test requires CMake; ignore error since not available or version too old\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-11-30 00:50:09 +00:00
|
|
|
if (!$param{fails} && $param{make_main}) {
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 12:26:32 +00:00
|
|
|
$self->_make_main($param{timing_loop});
|
2019-05-08 02:34:09 +00:00
|
|
|
}
|
2011-07-06 00:42:33 +00:00
|
|
|
|
2019-10-17 23:44:10 +00:00
|
|
|
if ($param{verilator_make_gmake}
|
|
|
|
|| (!$param{verilator_make_gmake} && !$param{verilator_make_cmake})) {
|
|
|
|
my @vlt_cmd = $self->compile_vlt_cmd(%param);
|
|
|
|
$self->oprint("Running Verilator (gmake)\n") if $self->{verbose};
|
|
|
|
$self->_run(logfile => "$self->{obj_dir}/vlt_compile.log",
|
|
|
|
fails => $param{fails},
|
|
|
|
tee => $param{tee},
|
|
|
|
expect => $param{expect},
|
|
|
|
expect_filename => $param{expect_filename},
|
2020-05-16 10:15:25 +00:00
|
|
|
verilator_run => 1,
|
2019-10-17 23:44:10 +00:00
|
|
|
cmd => \@vlt_cmd) if $::Opt_Verilation;
|
2022-11-20 03:43:10 +00:00
|
|
|
return 1 if $self->errors || $self->skips;
|
2019-10-17 23:44:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($param{verilator_make_cmake}) {
|
|
|
|
my @vlt_args = $self->compile_vlt_flags(%param);
|
|
|
|
$self->oprint("Running cmake\n") if $self->{verbose};
|
|
|
|
mkdir $self->{obj_dir};
|
|
|
|
my @csources = ();
|
|
|
|
unshift @csources, $self->{main_filename} if $param{make_main};
|
|
|
|
$self->_run(logfile => "$self->{obj_dir}/vlt_cmake.log",
|
|
|
|
fails => $param{fails},
|
|
|
|
tee => $param{tee},
|
|
|
|
expect => $param{expect},
|
|
|
|
expect_filename => $param{expect_filename},
|
2020-05-16 10:15:25 +00:00
|
|
|
verilator_run => 1,
|
2021-09-08 03:50:28 +00:00
|
|
|
cmd => ["cd \"" . $self->{obj_dir} . "\" && cmake",
|
|
|
|
"\"" . $self->{t_dir} . "/..\"",
|
2019-10-17 23:44:10 +00:00
|
|
|
"-DTEST_VERILATOR_ROOT=$ENV{VERILATOR_ROOT}",
|
|
|
|
"-DTEST_NAME=$self->{name}",
|
|
|
|
"-DTEST_CSOURCES=\"@csources\"",
|
|
|
|
"-DTEST_VERILATOR_ARGS=\"@vlt_args\"",
|
|
|
|
"-DTEST_VERILATOR_SOURCES=\"$param{top_filename} @{$param{v_other_filenames}}\"",
|
2021-09-08 03:50:28 +00:00
|
|
|
"-DTEST_VERBOSE=\"" . ($self->{verbose} ? 1 : 0) . "\"",
|
|
|
|
"-DTEST_SYSTEMC=\"" . ($self->sc ? 1 : 0) . "\"",
|
|
|
|
"-DCMAKE_PREFIX_PATH=\"" . (($ENV{SYSTEMC_INCLUDE} || $ENV{SYSTEMC} || '') . "/..\""),
|
2020-05-27 23:57:49 +00:00
|
|
|
"-DTEST_OPT_FAST=\"" . ($param{benchmark} ? "-Os" : "-O0") . "\"",
|
2020-05-27 00:52:08 +00:00
|
|
|
"-DTEST_OPT_GLOBAL=\"" . ($param{benchmark} ? "-Os" : "-O0") . "\"",
|
2019-10-17 23:44:10 +00:00
|
|
|
"-DTEST_VERILATION=\"" . $::Opt_Verilation . "\"",
|
|
|
|
]);
|
2022-11-20 03:43:10 +00:00
|
|
|
return 1 if $self->errors || $self->skips;
|
2019-10-17 23:44:10 +00:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2019-10-08 02:15:43 +00:00
|
|
|
if (!$param{fails} && $param{verilator_make_gmake}) {
|
2019-10-17 23:44:10 +00:00
|
|
|
$self->oprint("Running make (gmake)\n") if $self->{verbose};
|
|
|
|
$self->_run(logfile => "$self->{obj_dir}/vlt_gcc.log",
|
2020-01-25 03:40:19 +00:00
|
|
|
entering => "$self->{obj_dir}",
|
2020-05-31 13:03:51 +00:00
|
|
|
cmd => [$ENV{MAKE},
|
2021-09-08 03:50:28 +00:00
|
|
|
"-C " . $self->{obj_dir},
|
|
|
|
"-f " . $FindBin::RealBin . "/Makefile_obj",
|
2019-10-17 23:44:10 +00:00
|
|
|
($self->{verbose} ? "" : "--no-print-directory"),
|
|
|
|
"VM_PREFIX=$self->{VM_PREFIX}",
|
|
|
|
"TEST_OBJ_DIR=$self->{obj_dir}",
|
2021-09-08 03:50:28 +00:00
|
|
|
"CPPFLAGS_DRIVER=-D" . uc($self->{name}),
|
2021-09-08 22:45:25 +00:00
|
|
|
($self->{verbose} ? "CPPFLAGS_DRIVER2=-DTEST_VERBOSE=1" : ""),
|
2020-05-27 23:57:49 +00:00
|
|
|
($param{benchmark} ? "" : "OPT_FAST=-O0"),
|
2020-05-27 00:52:08 +00:00
|
|
|
($param{benchmark} ? "" : "OPT_GLOBAL=-O0"),
|
2019-10-17 23:44:10 +00:00
|
|
|
"$self->{VM_PREFIX}", # bypass default rule, as we don't need archive
|
2021-09-08 03:50:28 +00:00
|
|
|
($param{make_flags} || ""),
|
2019-10-17 23:44:10 +00:00
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$param{fails} && $param{verilator_make_cmake}) {
|
|
|
|
$self->oprint("Running cmake --build\n") if $self->{verbose};
|
|
|
|
$self->_run(logfile => "$self->{obj_dir}/vlt_cmake_build.log",
|
|
|
|
cmd => ["cmake",
|
|
|
|
"--build", $self->{obj_dir},
|
2021-09-08 22:45:25 +00:00
|
|
|
($self->{verbose} ? "--verbose" : ""),
|
2019-06-29 17:11:30 +00:00
|
|
|
]);
|
2019-05-08 02:34:09 +00:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2011-12-10 00:58:14 +00:00
|
|
|
else {
|
2018-05-08 00:42:28 +00:00
|
|
|
$self->error("No compile step defined for '$self->{scenario}' scenario");
|
2011-12-10 00:58:14 +00:00
|
|
|
}
|
2013-06-13 11:58:52 +00:00
|
|
|
|
|
|
|
if ($param{make_pli}) {
|
2019-06-29 17:11:30 +00:00
|
|
|
$self->oprint("Compile vpi\n") if $self->{verbose};
|
2021-02-04 00:29:24 +00:00
|
|
|
my @cmd = ($ENV{CXX}, @{$param{pli_flags}},
|
2021-09-08 03:50:28 +00:00
|
|
|
"-D" . $param{tool_define},
|
|
|
|
"-DIS_VPI", ($ENV{CFLAGS} || ''),
|
2019-09-19 22:46:45 +00:00
|
|
|
"$self->{t_dir}/$self->{pli_filename}");
|
2013-06-13 11:58:52 +00:00
|
|
|
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/pli_compile.log",
|
|
|
|
fails=>$param{fails},
|
|
|
|
cmd=>\@cmd);
|
2013-06-13 11:58:52 +00:00
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub execute {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2022-11-20 03:43:10 +00:00
|
|
|
return 1 if $self->errors || $self->skips;
|
2019-03-10 13:25:23 +00:00
|
|
|
my %param = (%{$self}, @_); # Default arguments are from $self
|
|
|
|
# params may be expect or {tool}_expect
|
2019-06-29 17:11:30 +00:00
|
|
|
$self->oprint("Run\n") if $self->{verbose};
|
|
|
|
|
|
|
|
delete $ENV{SYSTEMC_DISABLE_COPYRIGHT_MESSAGE};
|
|
|
|
$ENV{SYSTEMC_DISABLE_COPYRIGHT_MESSAGE} = "DISABLE" if !$self->{verbose};
|
2010-01-29 00:33:02 +00:00
|
|
|
|
|
|
|
my $run_env = $param{run_env};
|
|
|
|
$run_env .= ' ' if $run_env;
|
|
|
|
|
2010-03-18 16:03:08 +00:00
|
|
|
if ($param{atsim}) {
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/atsim_sim.log",
|
|
|
|
fails=>$param{fails},
|
2021-09-08 03:50:28 +00:00
|
|
|
cmd=>["echo q | " . $run_env . "$self->{obj_dir}/athdl_sv",
|
2019-05-08 02:34:09 +00:00
|
|
|
@{$param{atsim_run_flags}},
|
|
|
|
@{$param{all_run_flags}},
|
|
|
|
],
|
|
|
|
%param,
|
2018-11-03 18:59:04 +00:00
|
|
|
expect=>$param{atsim_run_expect}, # non-verilator expect isn't the same
|
|
|
|
expect_filename=>$param{atsim_run_expect_filename},
|
|
|
|
);
|
2010-03-18 16:03:08 +00:00
|
|
|
}
|
2011-12-10 00:58:14 +00:00
|
|
|
elsif ($param{ghdl}) {
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/ghdl_sim.log",
|
|
|
|
fails=>$param{fails},
|
2021-09-08 03:50:28 +00:00
|
|
|
cmd=>[$run_env . "$self->{obj_dir}/simghdl",
|
2019-05-08 02:34:09 +00:00
|
|
|
@{$param{ghdl_run_flags}},
|
|
|
|
@{$param{all_run_flags}},
|
|
|
|
],
|
|
|
|
%param,
|
2018-11-03 18:59:04 +00:00
|
|
|
expect=>$param{ghdl_run_expect}, # non-verilator expect isn't the same
|
|
|
|
expect_filename=>$param{ghdl_run_expect_filename},
|
|
|
|
);
|
2011-12-10 00:58:14 +00:00
|
|
|
}
|
|
|
|
elsif ($param{iv}) {
|
2021-09-08 03:50:28 +00:00
|
|
|
my @cmd = ($run_env . "$self->{obj_dir}/simiv",
|
2019-05-08 02:34:09 +00:00
|
|
|
@{$param{iv_run_flags}},
|
|
|
|
@{$param{all_run_flags}},
|
|
|
|
);
|
2021-02-03 23:59:27 +00:00
|
|
|
if ($param{use_libvpi}) {
|
2019-03-10 13:25:23 +00:00
|
|
|
# don't enter command line on $stop, include vpi
|
|
|
|
unshift @cmd, "vvp -n -m $self->{obj_dir}/libvpi.so";
|
|
|
|
}
|
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/iv_sim.log",
|
2019-05-08 02:34:09 +00:00
|
|
|
fails=>$param{fails},
|
|
|
|
cmd=> \@cmd,
|
|
|
|
%param,
|
2018-11-03 18:59:04 +00:00
|
|
|
expect=>$param{iv_run_expect}, # non-verilator expect isn't the same
|
|
|
|
expect_filename=>$param{iv_run_expect_filename},
|
|
|
|
);
|
2009-10-02 02:32:40 +00:00
|
|
|
}
|
2015-02-11 01:16:03 +00:00
|
|
|
elsif ($param{ms}) {
|
2021-09-08 03:50:28 +00:00
|
|
|
my @pli_opt = ();
|
2021-02-03 23:59:27 +00:00
|
|
|
if ($param{use_libvpi}) {
|
2019-11-20 02:53:17 +00:00
|
|
|
unshift @pli_opt, "-pli $self->{obj_dir}/libvpi.so";
|
|
|
|
}
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/ms_sim.log",
|
|
|
|
fails=>$param{fails},
|
2021-09-08 03:50:28 +00:00
|
|
|
cmd=>["echo q | " . $run_env . ($ENV{VERILATOR_MODELSIM} || "vsim"),
|
2019-05-08 02:34:09 +00:00
|
|
|
@{$param{ms_run_flags}},
|
|
|
|
@{$param{all_run_flags}},
|
2019-11-20 02:53:17 +00:00
|
|
|
@{pli_opt},
|
2015-02-11 01:16:03 +00:00
|
|
|
(" top")
|
2019-05-08 02:34:09 +00:00
|
|
|
],
|
|
|
|
%param,
|
2018-11-03 18:59:04 +00:00
|
|
|
expect=>$param{ms_run_expect}, # non-verilator expect isn't the same
|
|
|
|
expect_filename=>$param{ms_expect_filename},
|
|
|
|
);
|
2015-02-11 01:16:03 +00:00
|
|
|
}
|
2011-12-10 00:58:14 +00:00
|
|
|
elsif ($param{nc}) {
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/nc_sim.log",
|
|
|
|
fails=>$param{fails},
|
2021-09-08 03:50:28 +00:00
|
|
|
cmd=>["echo q | " . $run_env . ($ENV{VERILATOR_NCVERILOG} || "ncverilog"),
|
2019-05-08 02:34:09 +00:00
|
|
|
@{$param{nc_run_flags}},
|
|
|
|
@{$param{all_run_flags}},
|
|
|
|
],
|
|
|
|
%param,
|
2018-11-03 18:59:04 +00:00
|
|
|
expect=>$param{nc_run_expect}, # non-verilator expect isn't the same
|
|
|
|
expect_filename=>$param{nc_run_expect_filename},
|
|
|
|
);
|
2008-11-21 19:38:12 +00:00
|
|
|
}
|
2011-12-10 00:58:14 +00:00
|
|
|
elsif ($param{vcs}) {
|
2019-05-08 02:34:09 +00:00
|
|
|
#my $fh = IO::File->new(">simv.key") or die "%Error: $! simv.key,";
|
|
|
|
#$fh->print("quit\n"); $fh->close;
|
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/vcs_sim.log",
|
2021-09-08 03:50:28 +00:00
|
|
|
cmd=>["echo q | " . $run_env . "./simv",
|
2019-05-08 02:34:09 +00:00
|
|
|
@{$param{vcs_run_flags}},
|
|
|
|
@{$param{all_run_flags}},
|
|
|
|
],
|
|
|
|
%param,
|
2018-11-03 18:59:04 +00:00
|
|
|
expect=>$param{vcs_run_expect}, # non-verilator expect isn't the same
|
|
|
|
expect_filename=>$param{vcs_run_expect_filename},
|
|
|
|
);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2019-08-29 21:00:49 +00:00
|
|
|
elsif ($param{xsim}) {
|
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/xsim_sim.log",
|
|
|
|
fails=>$param{fails},
|
2021-09-08 03:50:28 +00:00
|
|
|
cmd=>[$run_env.($ENV{VERILATOR_XELAB} || "xelab"),
|
2019-08-29 21:00:49 +00:00
|
|
|
@{$param{xsim_run_flags}},
|
2019-09-26 19:54:51 +00:00
|
|
|
@{$param{xsim_run_flags2}},
|
2019-08-29 21:00:49 +00:00
|
|
|
@{$param{all_run_flags}},
|
|
|
|
(" $self->{name}.top")
|
|
|
|
],
|
|
|
|
%param,
|
|
|
|
expect=>$param{xsim_run_expect}, # non-verilator expect isn't the same
|
|
|
|
expect_filename=>$param{xsim_expect_filename},
|
|
|
|
);
|
|
|
|
}
|
2018-05-08 23:39:32 +00:00
|
|
|
elsif ($param{vlt_all}
|
2019-05-08 02:34:09 +00:00
|
|
|
#&& (!$param{needs_v4} || -r "$ENV{VERILATOR_ROOT}/src/V3Gate.cpp")
|
|
|
|
) {
|
|
|
|
$param{executable} ||= "$self->{obj_dir}/$param{VM_PREFIX}";
|
2019-07-26 01:34:09 +00:00
|
|
|
my $debugger = "";
|
|
|
|
if ($opt_gdbsim) {
|
2021-09-08 03:50:28 +00:00
|
|
|
$debugger = ($ENV{VERILATOR_GDB} || "gdb") . " ";
|
2019-07-26 01:34:09 +00:00
|
|
|
} elsif ($opt_rrsim) {
|
|
|
|
$debugger = "rr record ";
|
|
|
|
}
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->_run(logfile=>"$self->{obj_dir}/vlt_sim.log",
|
|
|
|
cmd=>[($run_env
|
2019-07-26 01:34:09 +00:00
|
|
|
.$debugger
|
2019-05-08 02:34:09 +00:00
|
|
|
.$param{executable}
|
|
|
|
.($opt_gdbsim ? " -ex 'run " : "")),
|
|
|
|
@{$param{all_run_flags}},
|
|
|
|
($opt_gdbsim ? "'" : ""),
|
|
|
|
],
|
|
|
|
%param,
|
2018-11-03 18:59:04 +00:00
|
|
|
expect=>$param{expect}, # backward compatible name
|
|
|
|
expect_filename=>$param{expect_filename}, # backward compatible name
|
|
|
|
);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2011-12-10 00:58:14 +00:00
|
|
|
else {
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->error("No execute step for this simulator");
|
2011-12-10 00:58:14 +00:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
2020-05-24 02:24:26 +00:00
|
|
|
sub setenv {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2020-05-24 02:24:26 +00:00
|
|
|
my $var = shift;
|
|
|
|
my $val = shift;
|
|
|
|
print "\texport $var='$val'\n";
|
|
|
|
$ENV{$var} = $val;
|
|
|
|
}
|
|
|
|
|
2008-12-05 15:54:14 +00:00
|
|
|
sub inline_checks {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2022-11-20 03:43:10 +00:00
|
|
|
return 1 if $self->errors || $self->skips;
|
2018-05-08 23:39:32 +00:00
|
|
|
return 1 if !$self->{vlt_all};
|
2008-12-10 22:10:03 +00:00
|
|
|
|
2019-03-10 13:25:23 +00:00
|
|
|
my %param = (%{$self}, @_); # Default arguments are from $self
|
2008-12-05 15:54:14 +00:00
|
|
|
|
|
|
|
my $covfn = $Self->{coverage_filename};
|
|
|
|
my $contents = $self->file_contents($covfn);
|
|
|
|
|
2019-06-29 17:11:30 +00:00
|
|
|
$self->oprint("Extract checks\n") if $self->{verbose};
|
2008-12-05 15:54:14 +00:00
|
|
|
my $fh = IO::File->new("<$self->{top_filename}");
|
|
|
|
while (defined(my $line = $fh->getline)) {
|
2019-05-08 02:34:09 +00:00
|
|
|
if ($line =~ /CHECK/) {
|
|
|
|
if ($line =~ /CHECK_COVER *\( *([---0-9]+) *, *"([^"]+)" *, *("([^"]+)" *,|) *(\d+) *\)/) {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $lineno = ($. + $1); my $hier = $2; my $comment = $4; my $count = $5;
|
|
|
|
my $regexp = "\001l\002" . $lineno;
|
|
|
|
$regexp .= ".*\001o\002" . quotemeta($comment) if $comment;
|
|
|
|
$regexp .= ".*\001h\002" . quotemeta($hier) if $hier;
|
|
|
|
$regexp .= ".*' " . $count;
|
2019-05-08 02:34:09 +00:00
|
|
|
if ($contents !~ /$regexp/) {
|
2021-09-08 03:50:28 +00:00
|
|
|
$self->error("CHECK_COVER: $covfn: Regexp not found: $regexp\n" .
|
2019-05-08 02:34:09 +00:00
|
|
|
"From $self->{top_filename}:$.: $line");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
elsif ($line =~ /CHECK_COVER_MISSING *\( *([---0-9]+) *\)/) {
|
2020-05-31 19:52:17 +00:00
|
|
|
my $lineno = ($. + $1);
|
2021-09-08 03:50:28 +00:00
|
|
|
my $regexp = "\001l\002" . $lineno;
|
2019-05-08 02:34:09 +00:00
|
|
|
if ($contents =~ /$regexp/) {
|
2021-09-08 03:50:28 +00:00
|
|
|
$self->error("CHECK_COVER_MISSING: $covfn: Regexp found: $regexp\n" .
|
2019-05-08 02:34:09 +00:00
|
|
|
"From $self->{top_filename}:$.: $line");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$self->error("$self->{top_filename}:$.: Unknown CHECK request: $line");
|
|
|
|
}
|
|
|
|
}
|
2008-12-05 15:54:14 +00:00
|
|
|
}
|
|
|
|
$fh->close;
|
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
#----------------------------------------------------------------------
|
|
|
|
# Accessors
|
|
|
|
|
|
|
|
sub ok {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2006-08-26 11:35:28 +00:00
|
|
|
$self->{ok} = $_[0] if defined $_[0];
|
2022-11-20 03:43:10 +00:00
|
|
|
$self->{ok} = 0 if $self->{errors} || $self->{errors_keep_going} || $self->{skips};
|
2006-08-26 11:35:28 +00:00
|
|
|
return $self->{ok};
|
|
|
|
}
|
|
|
|
|
2014-04-30 02:59:38 +00:00
|
|
|
sub continuing {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2022-11-20 03:43:10 +00:00
|
|
|
return !($self->errors || $self->skips);
|
2014-04-30 02:59:38 +00:00
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
sub errors {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2006-08-26 11:35:28 +00:00
|
|
|
return $self->{errors};
|
|
|
|
}
|
|
|
|
|
2021-01-12 23:09:56 +00:00
|
|
|
sub golden_filename {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2021-01-12 23:09:56 +00:00
|
|
|
$self->{golden_filename} = shift if defined $_[0];
|
|
|
|
return $self->{golden_filename};
|
|
|
|
}
|
|
|
|
|
2018-05-08 00:42:28 +00:00
|
|
|
sub scenario_off {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2018-05-08 00:42:28 +00:00
|
|
|
return $self->{scenario_off};
|
|
|
|
}
|
|
|
|
|
2010-02-07 12:00:59 +00:00
|
|
|
sub skips {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2010-02-07 12:00:59 +00:00
|
|
|
return $self->{skips};
|
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
sub top_filename {
|
2021-09-08 03:50:28 +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};
|
|
|
|
}
|
|
|
|
|
2011-12-10 00:58:14 +00:00
|
|
|
sub vhdl {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2011-12-10 00:58:14 +00:00
|
|
|
$self->{vhdl} = shift if defined $_[0];
|
|
|
|
if ($self->{vhdl}) {
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->{top_filename} =~ s/\.v$/\.vhdl/;
|
2011-12-10 00:58:14 +00:00
|
|
|
}
|
|
|
|
return $self->{vhdl};
|
|
|
|
}
|
|
|
|
|
|
|
|
sub v_suffix {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2011-12-10 00:58:14 +00:00
|
|
|
# Suffix for file type, e.g. .vhdl or .v
|
|
|
|
return $self->{vhdl} ? "vhdl" : "v";
|
|
|
|
}
|
|
|
|
|
2007-12-13 13:54:04 +00:00
|
|
|
sub sc {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2007-12-13 13:54:04 +00:00
|
|
|
return $self->{sc};
|
|
|
|
}
|
|
|
|
|
2018-02-01 00:27:42 +00:00
|
|
|
sub have_sc {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2020-05-28 22:51:46 +00:00
|
|
|
return 1 if (defined $ENV{SYSTEMC} || defined $ENV{SYSTEMC_INCLUDE} || $ENV{CFG_HAVE_SYSTEMC});
|
2022-10-21 01:42:30 +00:00
|
|
|
return 1 if $self->verilator_get_supported('SYSTEMC');
|
2018-02-01 00:27:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 12:26:32 +00:00
|
|
|
sub have_coroutines {
|
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2022-10-21 01:42:30 +00:00
|
|
|
return 1 if $self->verilator_get_supported('COROUTINES');
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 12:26:32 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-25 20:12:34 +00:00
|
|
|
sub make_version {
|
2020-05-31 13:03:51 +00:00
|
|
|
my $ver = `$ENV{MAKE} --version`;
|
2020-05-25 20:12:34 +00:00
|
|
|
if ($ver =~ /make ([0-9]+\.[0-9]+)/i) {
|
|
|
|
return $1;
|
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-17 23:44:10 +00:00
|
|
|
sub have_cmake {
|
|
|
|
return cmake_version() >= version->declare("3.8");
|
|
|
|
}
|
|
|
|
|
|
|
|
sub cmake_version {
|
|
|
|
chomp(my $cmake_bin = `which cmake`);
|
|
|
|
if (!$cmake_bin) {
|
|
|
|
return undef;
|
|
|
|
}
|
|
|
|
my $cmake_version = `cmake --version`;
|
|
|
|
if ($cmake_version !~ /cmake version (\d+)\.(\d+)/) {
|
|
|
|
return undef;
|
|
|
|
}
|
|
|
|
$cmake_version = "$1.$2";
|
|
|
|
return version->declare($cmake_version);
|
|
|
|
}
|
|
|
|
|
2018-08-28 10:41:17 +00:00
|
|
|
sub trace_filename {
|
|
|
|
my $self = shift;
|
2018-10-02 22:42:53 +00:00
|
|
|
return "$self->{obj_dir}/simx.fst" if $self->{trace_format} =~ /^fst/;
|
2018-08-28 10:41:17 +00:00
|
|
|
return "$self->{obj_dir}/simx.vcd";
|
|
|
|
}
|
|
|
|
|
2019-08-28 01:36:59 +00:00
|
|
|
sub obj_dir {
|
|
|
|
my $self = shift;
|
|
|
|
return $self->{obj_dir};
|
|
|
|
}
|
|
|
|
|
2019-06-27 15:26:25 +00:00
|
|
|
sub get_default_vltmt_threads {
|
|
|
|
return $Vltmt_threads;
|
|
|
|
}
|
|
|
|
|
2019-09-19 22:46:45 +00:00
|
|
|
sub pli_filename {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2019-09-19 22:46:45 +00:00
|
|
|
$self->{pli_filename} = shift if defined $_[0];
|
|
|
|
return $self->{pli_filename};
|
|
|
|
}
|
|
|
|
|
2022-12-03 00:57:40 +00:00
|
|
|
sub rerunnable {
|
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
|
|
|
$self->{rerunnable} = shift if defined $_[0];
|
|
|
|
return $self->{rerunnable};
|
|
|
|
}
|
|
|
|
|
2019-06-27 15:26:25 +00:00
|
|
|
sub too_few_cores {
|
|
|
|
my $threads = ::calc_threads($Vltmt_threads);
|
|
|
|
return $threads < $Vltmt_threads;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub skip_if_too_few_cores {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2019-06-27 15:26:25 +00:00
|
|
|
if (too_few_cores()) {
|
|
|
|
$self->skip("Skipping due to too few cores\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub wno_unopthreads_for_few_cores {
|
|
|
|
if (too_few_cores()) {
|
|
|
|
warn "Too few cores, using -Wno-UNOPTTHREADS\n";
|
|
|
|
return "-Wno-UNOPTTHREADS";
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2019-09-21 12:26:34 +00:00
|
|
|
sub VM_PREFIX {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2019-09-21 12:26:34 +00:00
|
|
|
$self->{VM_PREFIX} = shift if defined $_[0];
|
|
|
|
return $self->{VM_PREFIX};
|
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
2017-09-23 12:50:39 +00:00
|
|
|
sub run {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2017-09-23 12:50:39 +00:00
|
|
|
$self->_run(@_);
|
|
|
|
}
|
2021-09-08 22:45:25 +00:00
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
sub _run {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2020-01-25 03:40:19 +00:00
|
|
|
my %param = (tee => 1,
|
|
|
|
#entering => # Print entering directory information
|
2020-05-16 10:15:25 +00:00
|
|
|
#verilator_run => # Move gcov data to parallel area
|
2019-05-08 02:34:09 +00:00
|
|
|
@_);
|
2021-03-08 02:05:15 +00:00
|
|
|
|
2021-09-08 03:50:28 +00:00
|
|
|
my $command = join(' ', @{$param{cmd}});
|
2015-10-21 10:54:03 +00:00
|
|
|
$command = "time $command" if $opt_benchmark && $command !~ /^cd /;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2020-05-16 10:15:25 +00:00
|
|
|
if ($param{verilator_run}) {
|
|
|
|
# Gcov fails when parallel jobs write same data file,
|
2021-03-08 02:05:15 +00:00
|
|
|
# so we make sure .gcda output dir is unique across all running jobs.
|
2022-12-10 02:06:27 +00:00
|
|
|
# We can't just put each one in an unique obj_dir as it uses too much disk.
|
2021-03-08 02:05:15 +00:00
|
|
|
# Must use absolute path as some execute()s have different PWD
|
2020-05-16 10:15:25 +00:00
|
|
|
$ENV{GCOV_PREFIX_STRIP} = 99;
|
2021-03-08 02:05:15 +00:00
|
|
|
$ENV{GCOV_PREFIX} = File::Spec->rel2abs("$FindBin::RealBin/obj_dist/gcov_$self->{running_id}");
|
|
|
|
mkdir $ENV{GCOV_PREFIX};
|
|
|
|
print "export GCOV_PREFIX_STRIP=99 GCOV_PREFIX=$ENV{GCOV_PREFIX}\n" if $self->{verbose};
|
2020-05-16 10:15:25 +00:00
|
|
|
} else {
|
|
|
|
delete $ENV{GCOV_PREFIX_STRIP};
|
|
|
|
delete $ENV{GCOV_PREFIX};
|
|
|
|
}
|
|
|
|
|
2021-03-08 02:05:15 +00:00
|
|
|
print "\t$command";
|
|
|
|
print " > $param{logfile}" if $param{logfile};
|
|
|
|
print "\n";
|
|
|
|
|
2013-05-24 00:08:39 +00:00
|
|
|
# Execute command redirecting output, keeping order between stderr and stdout.
|
|
|
|
# Must do low-level IO so GCC interaction works (can't be line-based)
|
|
|
|
my $status;
|
|
|
|
{
|
2019-05-08 02:34:09 +00:00
|
|
|
pipe(PARENTRD, CHILDWR) or die "%Error: Can't Pipe, stopped";
|
|
|
|
autoflush PARENTRD 1;
|
|
|
|
autoflush CHILDWR 1;
|
|
|
|
my $logfh;
|
|
|
|
if ($param{logfile}) {
|
|
|
|
$logfh = IO::File->new(">$param{logfile}") or die "%Error: Can't open $param{logfile}";
|
|
|
|
}
|
2021-09-08 03:50:28 +00:00
|
|
|
my $pid = fork();
|
2019-03-10 13:25:23 +00:00
|
|
|
if ($pid) { # Parent
|
|
|
|
close CHILDWR;
|
2020-01-25 03:40:19 +00:00
|
|
|
print "driver: Entering directory '",
|
|
|
|
File::Spec->rel2abs($param{entering}), "'\n" if $param{entering};
|
2019-05-08 02:34:09 +00:00
|
|
|
while (1) {
|
|
|
|
my $buf = '';
|
2021-09-08 03:50:28 +00:00
|
|
|
my $got = sysread PARENTRD, $buf, 10000;
|
|
|
|
last if defined $got && $got == 0;
|
2019-05-08 02:34:09 +00:00
|
|
|
print $buf if $param{tee};
|
|
|
|
print $logfh $buf if $logfh;
|
|
|
|
}
|
|
|
|
close PARENTRD;
|
|
|
|
close $logfh if $logfh;
|
2020-01-25 03:40:19 +00:00
|
|
|
print "driver: Leaving directory '",
|
|
|
|
File::Spec->rel2abs($param{entering}), "'\n" if $param{entering};
|
2019-03-10 13:25:23 +00:00
|
|
|
}
|
|
|
|
else { # Child
|
|
|
|
close PARENTRD;
|
2019-05-08 02:34:09 +00:00
|
|
|
close $logfh if $logfh;
|
|
|
|
# Reset signals
|
|
|
|
$SIG{ALRM} = 'DEFAULT';
|
|
|
|
$SIG{CHLD} = 'DEFAULT';
|
|
|
|
# Logging
|
|
|
|
open(STDOUT, ">&CHILDWR") or croak "%Error: Can't redirect stdout, stopped";
|
|
|
|
open(STDERR, ">&STDOUT") or croak "%Error: Can't dup stdout, stopped";
|
|
|
|
autoflush STDOUT 1;
|
|
|
|
autoflush STDERR 1;
|
|
|
|
system "$command";
|
2019-11-21 22:59:17 +00:00
|
|
|
my $status = $?;
|
|
|
|
if (($status & 127) == 4 # SIGILL
|
|
|
|
|| ($status & 127) == 8 # SIGFPA
|
|
|
|
|| ($status & 127) == 11) { # SIGSEGV
|
|
|
|
$Self->error("Exec failed with core dump");
|
|
|
|
}
|
|
|
|
exit($? ? 10 : 0); # $?>>8 misses coredumps
|
2019-03-10 13:25:23 +00:00
|
|
|
}
|
2021-09-08 03:50:28 +00:00
|
|
|
waitpid($pid, 0);
|
2019-05-08 02:34:09 +00:00
|
|
|
$status = $? || 0;
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
flush STDOUT;
|
|
|
|
flush STDERR;
|
|
|
|
|
|
|
|
if (!$param{fails} && $status) {
|
2018-03-16 23:50:17 +00:00
|
|
|
my $firstline = "";
|
|
|
|
if (my $fh = IO::File->new("<$param{logfile}")) {
|
2019-11-21 22:59:17 +00:00
|
|
|
$firstline = $fh->getline || '';
|
2018-03-16 23:50:17 +00:00
|
|
|
chomp $firstline;
|
|
|
|
}
|
|
|
|
$self->error("Exec of $param{cmd}[0] failed: $firstline\n");
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
if ($param{fails} && $status) {
|
2019-05-08 02:34:09 +00:00
|
|
|
print "(Exec expected to fail, and did.)\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
if ($param{fails} && !$status) {
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->error("Exec of $param{cmd}[0] ok, but expected to fail\n");
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2022-11-20 03:43:10 +00:00
|
|
|
return if $self->errors || $self->skips;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
# Read the log file a couple of times to allow for NFS delays
|
2010-01-29 00:33:02 +00:00
|
|
|
if ($param{check_finished} || $param{expect}) {
|
2020-02-03 23:43:56 +00:00
|
|
|
for (my $try = $self->tries - 1; $try >= 0; $try--) {
|
|
|
|
sleep 1 if ($try != $self->tries - 1);
|
|
|
|
my $moretry = $try != 0;
|
2019-05-08 02:34:09 +00:00
|
|
|
|
|
|
|
my $fh = IO::File->new("<$param{logfile}");
|
|
|
|
next if !$fh && $moretry;
|
|
|
|
local $/; undef $/;
|
|
|
|
my $wholefile = <$fh>;
|
|
|
|
$fh->close();
|
|
|
|
|
|
|
|
# Finished?
|
|
|
|
if ($param{check_finished} && $wholefile !~ /\*\-\* All Finished \*\-\*/) {
|
|
|
|
next if $moretry;
|
|
|
|
$self->error("Missing All Finished\n");
|
|
|
|
}
|
|
|
|
if ($param{expect}) {
|
2018-11-03 13:07:28 +00:00
|
|
|
# Strip debugging comments
|
|
|
|
# See also files_identical
|
|
|
|
$wholefile =~ s/^- [^\n]+\n//mig;
|
|
|
|
$wholefile =~ s/^- [a-z.0-9]+:\d+:[^\n]+\n//mig;
|
|
|
|
$wholefile =~ s/^dot [^\n]+\n//mig;
|
|
|
|
|
2019-03-10 13:25:23 +00:00
|
|
|
# Compare
|
|
|
|
my $quoted = quotemeta($param{expect});
|
2017-09-30 17:30:01 +00:00
|
|
|
my $ok = ($wholefile eq $param{expect}
|
2019-05-08 02:34:09 +00:00
|
|
|
|| _try_regex($wholefile, $param{expect}) == 1
|
|
|
|
|| $wholefile =~ /$quoted/ms);
|
|
|
|
if (!$ok) {
|
|
|
|
#print "**BAD $self->{name} $param{logfile} MT $moretry $try\n";
|
|
|
|
next if $moretry;
|
2020-11-12 23:50:05 +00:00
|
|
|
$self->error("Miscompares in output from $param{cmd}[0]\n");
|
2021-09-08 03:50:28 +00:00
|
|
|
$self->error("Might be error in regexp format\n") if $ok < 1;
|
2019-05-08 02:34:09 +00:00
|
|
|
print "GOT:\n";
|
|
|
|
print $wholefile;
|
|
|
|
print "ENDGOT\n";
|
|
|
|
print "EXPECT:\n";
|
|
|
|
print $param{expect};
|
|
|
|
print "ENDEXPECT\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
last;
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2018-11-03 18:59:04 +00:00
|
|
|
if ($param{expect_filename}) {
|
|
|
|
files_identical($param{logfile}, $param{expect_filename}, 'logfile');
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#######################################################################
|
|
|
|
# Little utilities
|
|
|
|
|
2017-09-30 17:30:01 +00:00
|
|
|
sub _try_regex {
|
|
|
|
# Try to eval a regexp
|
|
|
|
# Returns:
|
|
|
|
# 1 if $text ~= /$regex/ms
|
|
|
|
# 0 if no match
|
|
|
|
# -1 if $regex is invalid, doesn't compile
|
|
|
|
my ($text, $regex) = @_;
|
|
|
|
my $result;
|
|
|
|
{
|
|
|
|
local $@;
|
|
|
|
eval {
|
|
|
|
$result = ($text =~ /$regex/ms);
|
|
|
|
};
|
|
|
|
$result = -1 if $@;
|
|
|
|
}
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
sub _make_main {
|
|
|
|
my $self = shift;
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 12:26:32 +00:00
|
|
|
my $timing_loop = shift;
|
|
|
|
|
|
|
|
if ($timing_loop && $self->sc) {
|
|
|
|
$self->error("Cannot use timing loop and SystemC together!\n");
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2011-12-10 00:58:14 +00:00
|
|
|
if ($self->vhdl) {
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->_read_inputs_vhdl();
|
2011-12-10 00:58:14 +00:00
|
|
|
} else {
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->_read_inputs_v();
|
2011-12-10 00:58:14 +00:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2011-07-06 00:42:33 +00:00
|
|
|
my $filename = $self->{main_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
|
|
|
|
2008-08-05 18:45:20 +00:00
|
|
|
print $fh "// Test defines\n";
|
2021-09-08 03:50:28 +00:00
|
|
|
print $fh "#define MAIN_TIME_MULTIPLIER " . ($self->{main_time_multiplier} || 1) . "\n";
|
2008-08-05 18:45:20 +00:00
|
|
|
|
2020-12-16 23:31:47 +00:00
|
|
|
print $fh "#include <memory>\n";
|
2021-07-01 15:17:55 +00:00
|
|
|
print $fh "#include <fstream>\n" if $self->{benchmarksim};
|
|
|
|
print $fh "#include <chrono>\n" if $self->{benchmarksim};
|
|
|
|
print $fh "#include <iomanip>\n" if $self->{benchmarksim};
|
|
|
|
|
2019-01-20 19:16:09 +00:00
|
|
|
print $fh "// OS header\n";
|
|
|
|
print $fh "#include \"verilatedos.h\"\n";
|
|
|
|
|
2008-08-05 18:45:20 +00:00
|
|
|
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
|
|
|
|
2009-12-03 02:15:56 +00:00
|
|
|
print $fh "// General headers\n";
|
|
|
|
print $fh "#include \"verilated.h\"\n";
|
2007-12-13 13:54:04 +00:00
|
|
|
print $fh "#include \"systemc.h\"\n" if $self->sc;
|
2018-10-02 22:42:53 +00:00
|
|
|
print $fh "#include \"verilated_fst_c.h\"\n" if $self->{trace} && $self->{trace_format} eq 'fst-c';
|
2021-04-06 20:18:58 +00:00
|
|
|
print $fh "#include \"verilated_fst_sc.h\"\n" if $self->{trace} && $self->{trace_format} eq 'fst-sc';
|
2018-08-28 10:41:17 +00:00
|
|
|
print $fh "#include \"verilated_vcd_c.h\"\n" if $self->{trace} && $self->{trace_format} eq 'vcd-c';
|
|
|
|
print $fh "#include \"verilated_vcd_sc.h\"\n" if $self->{trace} && $self->{trace_format} eq 'vcd-sc';
|
2012-08-27 01:13:47 +00:00
|
|
|
print $fh "#include \"verilated_save.h\"\n" if $self->{savable};
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2020-12-16 23:31:47 +00:00
|
|
|
print $fh "std::unique_ptr<$VM_PREFIX> topp;\n";
|
2012-08-27 01:13:47 +00:00
|
|
|
|
|
|
|
if ($self->{savable}) {
|
2019-05-08 02:34:09 +00:00
|
|
|
$fh->print("\n");
|
|
|
|
$fh->print("void save_model(const char* filenamep) {\n");
|
|
|
|
$fh->print(" VL_PRINTF(\"Saving model to '%s'\\n\", filenamep);\n");
|
|
|
|
$fh->print(" VerilatedSave os;\n");
|
|
|
|
$fh->print(" os.open(filenamep);\n");
|
|
|
|
$fh->print(" os << *topp;\n");
|
|
|
|
$fh->print(" os.close();\n");
|
|
|
|
$fh->print("}\n");
|
|
|
|
$fh->print("\n");
|
|
|
|
$fh->print("void restore_model(const char* filenamep) {\n");
|
|
|
|
$fh->print(" VL_PRINTF(\"Restoring model from '%s'\\n\", filenamep);\n");
|
|
|
|
$fh->print(" VerilatedRestore os;\n");
|
|
|
|
$fh->print(" os.open(filenamep);\n");
|
|
|
|
$fh->print(" os >> *topp;\n");
|
|
|
|
$fh->print(" os.close();\n");
|
|
|
|
$fh->print("}\n");
|
2012-08-27 01:13:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#### Main
|
2017-09-08 01:08:49 +00:00
|
|
|
if ($self->sc) {
|
2019-10-30 02:57:25 +00:00
|
|
|
print $fh "extern int sc_main(int argc, char** argv);\n";
|
|
|
|
print $fh "int sc_main(int argc, char** argv) {\n";
|
2019-05-08 02:34:09 +00:00
|
|
|
print $fh " sc_signal<bool> fastclk;\n" if $self->{inputs}{fastclk};
|
2021-09-08 22:45:25 +00:00
|
|
|
print $fh " sc_signal<bool> clk;\n" if $self->{inputs}{clk};
|
2020-04-15 23:39:03 +00:00
|
|
|
print $fh " sc_set_time_resolution(1, $Self->{sc_time_resolution});\n";
|
|
|
|
print $fh " sc_time sim_time($self->{sim_time}, $Self->{sc_time_resolution});\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
} else {
|
2022-11-23 23:50:31 +00:00
|
|
|
print $fh "int main(int argc, char** argv) {\n";
|
2022-03-27 19:27:40 +00:00
|
|
|
print $fh " uint64_t sim_time = $self->{sim_time};\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2021-03-07 16:01:54 +00:00
|
|
|
|
|
|
|
print $fh " const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};\n";
|
2022-07-12 10:41:15 +00:00
|
|
|
print $fh " contextp->threads($self->{context_threads});\n";
|
2021-03-07 16:01:54 +00:00
|
|
|
print $fh " contextp->commandArgs(argc, argv);\n";
|
2021-09-08 03:50:28 +00:00
|
|
|
print $fh " contextp->debug(" . ($self->{verilated_debug} ? 1 : 0) . ");\n";
|
2018-05-25 11:15:29 +00:00
|
|
|
print $fh " srand48(5);\n"; # Ensure determinism
|
2021-09-08 03:50:28 +00:00
|
|
|
print $fh " contextp->randReset(" . $self->{verilated_randReset} . ");\n"
|
|
|
|
if defined $self->{verilated_randReset};
|
2020-12-16 23:31:47 +00:00
|
|
|
print $fh " topp.reset(new $VM_PREFIX(\"top\"));\n";
|
2021-03-07 16:01:54 +00:00
|
|
|
print $fh " contextp->internalsDump()\n;" if $self->{verilated_debug};
|
2019-07-01 01:53:01 +00:00
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
my $set;
|
2017-09-08 01:08:49 +00:00
|
|
|
if ($self->sc) {
|
2019-05-08 02:34:09 +00:00
|
|
|
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 {
|
2019-05-08 02:34:09 +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
|
|
|
|
2021-07-01 15:17:55 +00:00
|
|
|
if ($self->{benchmarksim}) {
|
|
|
|
$fh->print(" std::chrono::time_point<std::chrono::steady_clock> starttime;\n");
|
|
|
|
$fh->print(" bool warm = false;\n");
|
|
|
|
$fh->print(" uint64_t n_evals = 0;\n");
|
|
|
|
}
|
|
|
|
|
2007-12-13 13:54:04 +00:00
|
|
|
if ($self->{trace}) {
|
2019-05-08 02:34:09 +00:00
|
|
|
$fh->print("\n");
|
|
|
|
$fh->print("#if VM_TRACE\n");
|
2021-03-07 16:01:54 +00:00
|
|
|
$fh->print(" contextp->traceEverOn(true);\n");
|
2020-12-16 23:31:47 +00:00
|
|
|
$fh->print(" std::unique_ptr<VerilatedFstC> tfp{new VerilatedFstC};\n") if $self->{trace_format} eq 'fst-c';
|
2021-04-06 20:18:58 +00:00
|
|
|
$fh->print(" std::unique_ptr<VerilatedFstSc> tfp{new VerilatedFstSc};\n") if $self->{trace_format} eq 'fst-sc';
|
2020-12-16 23:31:47 +00:00
|
|
|
$fh->print(" std::unique_ptr<VerilatedVcdC> tfp{new VerilatedVcdC};\n") if $self->{trace_format} eq 'vcd-c';
|
|
|
|
$fh->print(" std::unique_ptr<VerilatedVcdSc> tfp{new VerilatedVcdSc};\n") if $self->{trace_format} eq 'vcd-sc';
|
2021-12-22 23:41:11 +00:00
|
|
|
$fh->print(" sc_core::sc_start(sc_core::SC_ZERO_TIME); // Finish elaboration before trace and open\n") if $self->sc;
|
2020-12-16 23:31:47 +00:00
|
|
|
$fh->print(" topp->trace(tfp.get(), 99);\n");
|
2021-09-08 03:50:28 +00:00
|
|
|
$fh->print(" tfp->open(\"" . $self->trace_filename . "\");\n");
|
2021-09-08 22:45:25 +00:00
|
|
|
|
2019-05-08 02:34:09 +00:00
|
|
|
if ($self->{trace} && !$self->sc) {
|
2021-03-07 16:01:54 +00:00
|
|
|
$fh->print(" if (tfp) tfp->dump(contextp->time());\n");
|
2019-05-08 02:34:09 +00:00
|
|
|
}
|
|
|
|
$fh->print("#endif\n");
|
2007-11-27 16:52:19 +00:00
|
|
|
}
|
|
|
|
|
2012-08-27 01:13:47 +00:00
|
|
|
if ($self->{savable}) {
|
2021-03-07 16:01:54 +00:00
|
|
|
$fh->print(" const char* save_time_strp = contextp->commandArgsPlusMatch(\"save_time=\");\n");
|
2022-09-15 01:10:19 +00:00
|
|
|
$fh->print(" unsigned int save_time = !save_time_strp[0] ? 0 : std::atoi(save_time_strp + std::strlen(\"+save_time=\"));\n");
|
2021-03-07 16:01:54 +00:00
|
|
|
$fh->print(" const char* save_restore_strp = contextp->commandArgsPlusMatch(\"save_restore=\");\n");
|
2019-05-08 02:34:09 +00:00
|
|
|
$fh->print(" unsigned int save_restore = !save_restore_strp[0] ? 0 : 1;\n");
|
2012-08-27 01:13:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($self->{savable}) {
|
2019-05-08 02:34:09 +00:00
|
|
|
$fh->print(" if (save_restore) {\n");
|
|
|
|
$fh->print(" restore_model(\"$self->{obj_dir}/saved.vltsv\");\n");
|
|
|
|
$fh->print(" } else {\n");
|
2012-08-27 01:13:47 +00:00
|
|
|
} else {
|
2019-05-08 02:34:09 +00:00
|
|
|
$fh->print(" {\n");
|
2012-08-27 01:13:47 +00:00
|
|
|
}
|
2019-10-05 21:35:08 +00:00
|
|
|
print $fh " ${set}fastclk = false;\n" if $self->{inputs}{fastclk};
|
|
|
|
print $fh " ${set}clk = false;\n" if $self->{inputs}{clk};
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 12:26:32 +00:00
|
|
|
if (!$timing_loop) {
|
|
|
|
_print_advance_time($self, $fh, 10);
|
|
|
|
}
|
2012-08-27 01:13:47 +00:00
|
|
|
print $fh " }\n";
|
2009-11-14 00:15:48 +00:00
|
|
|
|
2021-03-07 16:01:54 +00:00
|
|
|
my $time = $self->sc ? "sc_time_stamp()" : "contextp->time()";
|
|
|
|
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 12:26:32 +00:00
|
|
|
print $fh " while (";
|
|
|
|
if (!$timing_loop || $self->{inputs}{clk}) {
|
|
|
|
print $fh "(${time} < sim_time * MAIN_TIME_MULTIPLIER) && ";
|
|
|
|
}
|
|
|
|
print $fh "!contextp->gotFinish()) {\n";
|
2021-03-07 16:01:54 +00:00
|
|
|
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 12:26:32 +00:00
|
|
|
if ($timing_loop) {
|
|
|
|
print $fh " topp->eval();\n";
|
|
|
|
if ($self->{trace}) {
|
|
|
|
$fh->print("#if VM_TRACE\n");
|
|
|
|
$fh->print(" if (tfp) tfp->dump(contextp->time());\n");
|
|
|
|
$fh->print("#endif // VM_TRACE\n");
|
2019-05-08 02:34:09 +00:00
|
|
|
}
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 12:26:32 +00:00
|
|
|
if ($self->{inputs}{clk}) {
|
|
|
|
print $fh " uint64_t cycles = contextp->time() / MAIN_TIME_MULTIPLIER;\n";
|
|
|
|
print $fh " uint64_t new_time = (cycles + 1) * MAIN_TIME_MULTIPLIER;\n";
|
|
|
|
print $fh " if (topp->eventsPending() &&\n";
|
|
|
|
print $fh " topp->nextTimeSlot() / MAIN_TIME_MULTIPLIER <= cycles) {\n";
|
|
|
|
print $fh " new_time = topp->nextTimeSlot();\n";
|
|
|
|
print $fh " } else {\n";
|
|
|
|
print $fh " ${set}clk = !${set}clk;\n";
|
|
|
|
print $fh " }\n";
|
|
|
|
print $fh " contextp->time(new_time);\n";
|
|
|
|
} else {
|
|
|
|
print $fh " if (!topp->eventsPending()) break;\n";
|
|
|
|
print $fh " contextp->time(topp->nextTimeSlot());\n";
|
2019-05-08 02:34:09 +00:00
|
|
|
}
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 12:26:32 +00:00
|
|
|
} else {
|
|
|
|
for (my $i = 0; $i < 5; $i++) {
|
|
|
|
my $action = 0;
|
|
|
|
if ($self->{inputs}{fastclk}) {
|
|
|
|
print $fh " ${set}fastclk = !${set}fastclk;\n";
|
|
|
|
$action = 1;
|
|
|
|
}
|
|
|
|
if ($i == 0 && $self->{inputs}{clk}) {
|
|
|
|
print $fh " ${set}clk = !${set}clk;\n";
|
|
|
|
$action = 1;
|
|
|
|
}
|
|
|
|
if ($self->{savable}) {
|
|
|
|
$fh->print(" if (save_time && ${time} == save_time) {\n");
|
|
|
|
$fh->print(" save_model(\"$self->{obj_dir}/saved.vltsv\");\n");
|
|
|
|
$fh->print(" printf(\"Exiting after save_model\\n\");\n");
|
|
|
|
$fh->print(" topp.reset(nullptr);\n");
|
|
|
|
$fh->print(" return 0;\n");
|
|
|
|
$fh->print(" }\n");
|
|
|
|
}
|
|
|
|
_print_advance_time($self, $fh, 1, $action);
|
2019-05-08 02:34:09 +00:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2021-07-01 15:17:55 +00:00
|
|
|
if ($self->{benchmarksim}) {
|
|
|
|
$fh->print(" if (VL_UNLIKELY(!warm)) {\n");
|
|
|
|
$fh->print(" starttime = std::chrono::steady_clock::now();\n");
|
|
|
|
$fh->print(" warm = true;\n");
|
|
|
|
$fh->print(" } else {\n");
|
|
|
|
$fh->print(" ++n_evals;\n");
|
|
|
|
$fh->print(" }\n");
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
print $fh " }\n";
|
2021-07-01 15:17:55 +00:00
|
|
|
|
|
|
|
if ($self->{benchmarksim}) {
|
|
|
|
$fh->print(" {\n");
|
|
|
|
$fh->print(" const std::chrono::duration<double> exec_s = std::chrono::steady_clock::now() - starttime;\n");
|
2021-09-08 03:50:28 +00:00
|
|
|
$fh->print(" std::ofstream benchfile(\"" . $self->benchmarksim_filename() . "\", std::ofstream::out | std::ofstream::app);\n");
|
2021-07-01 15:17:55 +00:00
|
|
|
$fh->print(" benchfile << std::fixed << std::setprecision(9) << n_evals << \",\" << exec_s.count() << std::endl;\n");
|
|
|
|
$fh->print(" benchfile.close();\n");
|
|
|
|
$fh->print(" }\n");
|
|
|
|
}
|
|
|
|
|
2021-03-07 16:01:54 +00:00
|
|
|
print $fh " if (!contextp->gotFinish()) {\n";
|
2021-09-08 03:50:28 +00:00
|
|
|
print $fh ' vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish");', "\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
print $fh " }\n";
|
2006-08-30 19:50:24 +00:00
|
|
|
print $fh " topp->final();\n";
|
2007-11-27 16:52:19 +00:00
|
|
|
|
2011-07-28 23:41:05 +00:00
|
|
|
if ($self->{coverage}) {
|
2019-05-08 02:34:09 +00:00
|
|
|
$fh->print("#if VM_COVERAGE\n");
|
2021-09-08 03:50:28 +00:00
|
|
|
$fh->print(" VerilatedCov::write(\"", $self->{coverage_filename}, "\");\n");
|
2018-10-02 10:31:11 +00:00
|
|
|
$fh->print("#endif // VM_COVERAGE\n");
|
2011-07-28 23:41:05 +00:00
|
|
|
}
|
2007-12-13 13:54:04 +00:00
|
|
|
if ($self->{trace}) {
|
2019-05-08 02:34:09 +00:00
|
|
|
$fh->print("#if VM_TRACE\n");
|
2018-07-19 01:25:21 +00:00
|
|
|
$fh->print(" if (tfp) tfp->close();\n");
|
2020-12-16 23:31:47 +00:00
|
|
|
$fh->print(" tfp.reset();\n");
|
2018-10-02 10:31:11 +00:00
|
|
|
$fh->print("#endif // VM_TRACE\n");
|
2007-11-27 16:52:19 +00:00
|
|
|
}
|
|
|
|
$fh->print("\n");
|
|
|
|
|
2020-12-16 23:31:47 +00:00
|
|
|
print $fh " topp.reset();\n";
|
2021-02-26 00:26:36 +00:00
|
|
|
print $fh " return 0;\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
print $fh "}\n";
|
|
|
|
$fh->close();
|
|
|
|
}
|
|
|
|
|
2009-11-14 00:15:48 +00:00
|
|
|
sub _print_advance_time {
|
|
|
|
my $self = shift;
|
|
|
|
my $fh = shift;
|
|
|
|
my $time = shift;
|
|
|
|
my $action = shift;
|
|
|
|
|
|
|
|
my $set;
|
2017-09-08 01:08:49 +00:00
|
|
|
if ($self->sc) { $set = ""; }
|
2009-11-14 00:15:48 +00:00
|
|
|
else { $set = "topp->"; }
|
|
|
|
|
2017-09-08 01:08:49 +00:00
|
|
|
if ($self->sc) {
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 12:26:32 +00:00
|
|
|
print $fh " sc_start(${time} * MAIN_TIME_MULTIPLIER, $Self->{sc_time_resolution});\n";
|
2009-11-14 00:15:48 +00:00
|
|
|
} else {
|
2019-05-08 02:34:09 +00:00
|
|
|
if ($action) {
|
2018-07-19 01:25:21 +00:00
|
|
|
print $fh " ${set}eval();\n";
|
2019-05-08 02:34:09 +00:00
|
|
|
if ($self->{trace} && !$self->sc) {
|
|
|
|
$fh->print("#if VM_TRACE\n");
|
2021-03-07 16:01:54 +00:00
|
|
|
$fh->print(" if (tfp) tfp->dump(contextp->time());\n");
|
2018-10-02 10:31:11 +00:00
|
|
|
$fh->print("#endif // VM_TRACE\n");
|
2019-05-08 02:34:09 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-07 16:01:54 +00:00
|
|
|
print $fh " contextp->timeInc(${time} * MAIN_TIME_MULTIPLIER);\n";
|
2009-11-14 00:15:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
#######################################################################
|
|
|
|
|
|
|
|
sub _make_top {
|
|
|
|
my $self = shift;
|
2011-12-10 00:58:14 +00:00
|
|
|
if ($self->vhdl) {
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->_make_top_vhdl;
|
2011-12-10 00:58:14 +00:00
|
|
|
} else {
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->_make_top_v;
|
2011-12-10 00:58:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub _make_top_v {
|
|
|
|
my $self = shift;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2011-12-10 00:58:14 +00:00
|
|
|
$self->_read_inputs_v();
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2020-04-08 00:55:47 +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}})) {
|
2019-05-08 02:34:09 +00:00
|
|
|
print $fh " reg ${inp};\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
# Inst
|
|
|
|
print $fh " t t (\n";
|
2021-09-08 03:50:28 +00:00
|
|
|
my $comma = "";
|
2006-08-26 11:35:28 +00:00
|
|
|
foreach my $inp (sort (keys %{$self->{inputs}})) {
|
2019-05-08 02:34:09 +00:00
|
|
|
print $fh " ${comma}.${inp} (${inp})\n";
|
2021-09-08 03:50:28 +00:00
|
|
|
$comma = ",";
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
print $fh " );\n";
|
|
|
|
|
2009-05-08 17:16:19 +00:00
|
|
|
# Waves
|
|
|
|
print $fh "\n";
|
|
|
|
print $fh "`ifdef WAVES\n";
|
|
|
|
print $fh " initial begin\n";
|
2021-09-08 03:50:28 +00:00
|
|
|
print $fh " \$display(\"-Tracing Waves to Dumpfile: " . $self->trace_filename . "\");\n";
|
|
|
|
print $fh " \$dumpfile(\"" . $self->trace_filename . "\");\n";
|
2013-10-12 01:34:48 +00:00
|
|
|
print $fh " \$dumpvars(0, top);\n";
|
2009-05-08 17:16:19 +00:00
|
|
|
print $fh " end\n";
|
|
|
|
print $fh "`endif\n";
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
# Test
|
2009-05-08 17:16:19 +00:00
|
|
|
print $fh "\n";
|
2006-08-26 11:35:28 +00:00
|
|
|
print $fh " initial begin\n";
|
2019-05-08 03:00:52 +00:00
|
|
|
print $fh " fastclk = 0;\n" if $self->{inputs}{fastclk};
|
|
|
|
print $fh " clk = 0;\n" if $self->{inputs}{clk};
|
2009-11-14 00:15:48 +00:00
|
|
|
print $fh " #10;\n";
|
2019-05-08 03:00:52 +00:00
|
|
|
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";
|
2021-09-08 03:50:28 +00:00
|
|
|
for (my $i = 0; $i < 5; $i++) {
|
2019-05-08 02:34:09 +00:00
|
|
|
print $fh " #1;\n";
|
2019-05-08 03:00:52 +00:00
|
|
|
print $fh " fastclk = !fastclk;\n" if $self->{inputs}{fastclk};
|
2021-09-08 03:50:28 +00:00
|
|
|
print $fh " clk = !clk;\n" if $i == 4 && $self->{inputs}{clk};
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
print $fh " end\n";
|
|
|
|
print $fh " end\n";
|
|
|
|
|
|
|
|
print $fh "endmodule\n";
|
|
|
|
$fh->close();
|
|
|
|
}
|
|
|
|
|
2011-12-10 00:58:14 +00:00
|
|
|
sub _make_top_vhdl {
|
|
|
|
my $self = shift;
|
|
|
|
|
|
|
|
$self->_read_inputs_vhdl();
|
|
|
|
|
|
|
|
my $fh = IO::File->new(">$self->{top_shell_filename}") or die "%Error: $! $self->{top_shell_filename},";
|
|
|
|
print $fh "library ieee;\n";
|
|
|
|
|
|
|
|
my @ports = sort (keys %{$self->{inputs}});
|
|
|
|
|
|
|
|
print $fh "entity t_ent is\n";
|
|
|
|
if ($#ports >= 0) {
|
2019-05-08 02:34:09 +00:00
|
|
|
print $fh " port(\n";
|
|
|
|
my $semi = "";
|
|
|
|
foreach my $inp (@ports) {
|
|
|
|
print $fh " ${semi}${inp} : in std_logic\n";
|
2021-09-08 03:50:28 +00:00
|
|
|
$semi = ";";
|
2019-05-08 02:34:09 +00:00
|
|
|
}
|
|
|
|
print $fh " );\n";
|
2011-12-10 00:58:14 +00:00
|
|
|
}
|
|
|
|
print $fh "end;\n";
|
|
|
|
|
|
|
|
print $fh "entity top is\n";
|
|
|
|
print $fh "end;\n";
|
|
|
|
|
|
|
|
# Inst
|
|
|
|
print $fh "architecture t_beh of t_ent is\n";
|
|
|
|
if ($#ports >= 0) {
|
2019-05-08 02:34:09 +00:00
|
|
|
foreach my $inp (@ports) {
|
|
|
|
print $fh " signal ${inp} : std_logic := '0';\n";
|
|
|
|
}
|
2011-12-10 00:58:14 +00:00
|
|
|
}
|
|
|
|
print $fh " begin\n";
|
|
|
|
|
|
|
|
print $fh " t : t_ent\n";
|
|
|
|
if ($#ports >= 0) {
|
2019-05-08 02:34:09 +00:00
|
|
|
print $fh " port map(\n";
|
2021-09-08 03:50:28 +00:00
|
|
|
my $comma = "";
|
2019-05-08 02:34:09 +00:00
|
|
|
foreach my $inp (@ports) {
|
|
|
|
print $fh "\t${comma}${inp} => ${inp}\n";
|
2021-09-08 03:50:28 +00:00
|
|
|
$comma = ",";
|
2019-05-08 02:34:09 +00:00
|
|
|
}
|
|
|
|
print $fh " )\n";
|
2011-12-10 00:58:14 +00:00
|
|
|
}
|
|
|
|
print $fh " ;\n";
|
|
|
|
|
|
|
|
print $fh " end;\n";
|
|
|
|
|
|
|
|
# Waves TBD
|
|
|
|
# Test TBD
|
|
|
|
|
|
|
|
print $fh "end;\n";
|
|
|
|
$fh->close();
|
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
#######################################################################
|
|
|
|
|
2011-12-10 00:58:14 +00:00
|
|
|
sub _read_inputs_v {
|
2006-08-26 11:35:28 +00:00
|
|
|
my $self = shift;
|
2012-02-12 01:40:58 +00:00
|
|
|
my $filename = $self->top_filename;
|
2011-01-02 00:18:32 +00:00
|
|
|
$filename = "$self->{t_dir}/$filename" if !-r $filename;
|
2006-09-13 14:38:48 +00:00
|
|
|
my $fh = IO::File->new("<$filename") or die "%Error: $! $filename,";
|
2021-09-08 03:50:28 +00:00
|
|
|
my $get_sigs = 1;
|
2013-05-24 02:00:55 +00:00
|
|
|
my %inputs;
|
2006-08-26 11:35:28 +00:00
|
|
|
while (defined(my $line = $fh->getline)) {
|
2019-05-08 02:34:09 +00:00
|
|
|
if ($get_sigs) {
|
|
|
|
if ($line =~ /^\s*input\s*(\S+)\s*(\/[^\/]+\/|)\s*;/) {
|
|
|
|
$inputs{$1} = $1;
|
|
|
|
}
|
|
|
|
if ($line =~ /^\s*(function|task|endmodule)/) {
|
|
|
|
$get_sigs = 0;
|
|
|
|
}
|
2019-03-10 13:25:23 +00:00
|
|
|
}
|
|
|
|
if ($line =~ /^\s*module\s+t\b/) { # Ignore any earlier inputs; Module 't' has precedence
|
|
|
|
%inputs = ();
|
2019-05-08 02:34:09 +00:00
|
|
|
$get_sigs = 1;
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2013-05-24 02:00:55 +00:00
|
|
|
$self->{inputs}{$_} = $inputs{$_} foreach keys %inputs;
|
2006-08-26 11:35:28 +00:00
|
|
|
$fh->close();
|
|
|
|
}
|
|
|
|
|
2011-12-10 00:58:14 +00:00
|
|
|
#######################################################################
|
|
|
|
|
|
|
|
sub _read_inputs_vhdl {
|
|
|
|
my $self = shift;
|
2012-02-12 01:40:58 +00:00
|
|
|
my $filename = $self->top_filename;
|
2011-12-10 00:58:14 +00:00
|
|
|
$filename = "$self->{t_dir}/$filename" if !-r $filename;
|
|
|
|
my $fh = IO::File->new("<$filename") or die "%Error: $! $filename,";
|
|
|
|
while (defined(my $line = $fh->getline)) {
|
2019-05-08 02:34:09 +00:00
|
|
|
# Only supports this form right now:
|
|
|
|
# signal: in ...
|
|
|
|
# signal: out ...
|
|
|
|
if ($line =~ /^\s*(\S+)\s*:\s*(in)\b\s*/) {
|
|
|
|
$self->{inputs}{$1} = $1;
|
|
|
|
}
|
|
|
|
if ($line =~ /^\s*(architecture)/) {
|
|
|
|
last;
|
|
|
|
}
|
2011-12-10 00:58:14 +00:00
|
|
|
}
|
|
|
|
$fh->close();
|
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
#######################################################################
|
|
|
|
# Verilator utilities
|
|
|
|
|
2022-10-21 01:42:30 +00:00
|
|
|
our %_Verilator_Supported;
|
|
|
|
sub verilator_get_supported {
|
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
|
|
|
my $feature = shift;
|
|
|
|
# Returns if given feature is supported
|
|
|
|
if (!defined $_Verilator_Supported{$feature}) {
|
|
|
|
my @args = ("perl", "$ENV{VERILATOR_ROOT}/bin/verilator", "-get-supported", $feature);
|
2021-09-08 03:50:28 +00:00
|
|
|
my $args = join(' ', @args);
|
2022-10-21 01:42:30 +00:00
|
|
|
my $out = `$args`;
|
|
|
|
$out or die "couldn't run: $! " . join(' ', @args);
|
|
|
|
chomp $out;
|
|
|
|
$_Verilator_Supported{$feature} = ($out =~ /1/ ? 1 : 0);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2022-10-21 01:42:30 +00:00
|
|
|
return $_Verilator_Supported{$feature};
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#######################################################################
|
|
|
|
# File utilities
|
|
|
|
|
|
|
|
sub files_identical {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2006-08-26 11:35:28 +00:00
|
|
|
my $fn1 = shift;
|
|
|
|
my $fn2 = shift;
|
2018-11-03 18:59:04 +00:00
|
|
|
my $fn1_is_logfile = shift;
|
2022-11-20 03:43:10 +00:00
|
|
|
return 1 if $self->errors || $self->skips;
|
2018-11-03 18:59:04 +00:00
|
|
|
|
|
|
|
try:
|
2020-02-03 23:43:56 +00:00
|
|
|
for (my $try = $self->tries - 1; $try >= 0; $try--) {
|
|
|
|
sleep 1 if ($try != $self->tries - 1);
|
|
|
|
my $moretry = $try != 0;
|
2018-11-03 18:59:04 +00:00
|
|
|
|
|
|
|
my $f1 = IO::File->new("<$fn1");
|
|
|
|
my $f2 = IO::File->new("<$fn2");
|
|
|
|
if (!$f1) {
|
|
|
|
next try if $moretry;
|
|
|
|
$self->error("Files_identical file does not exist $fn1\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!$f2 && !$ENV{HARNESS_UPDATE_GOLDEN}) {
|
|
|
|
next try if $moretry;
|
|
|
|
$self->error("Files_identical file does not exist $fn2\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
my @l1 = $f1 && $f1->getlines();
|
|
|
|
my @l2 = $f2 && $f2->getlines();
|
|
|
|
if ($fn1_is_logfile) {
|
|
|
|
@l1 = grep {
|
|
|
|
!/^- [^\n]+\n/
|
|
|
|
&& !/^- [a-z.0-9]+:\d+:[^\n]+\n/
|
|
|
|
&& !/^-node:/
|
|
|
|
&& !/^dot [^\n]+\n/
|
2020-04-23 21:29:37 +00:00
|
|
|
&& !/^In file: .*\/sc_.*:\d+/
|
2020-10-14 13:30:15 +00:00
|
|
|
&& !/^libgcov.*/
|
2021-09-04 12:27:59 +00:00
|
|
|
&& !/--- \/tmp\// # t_difftree.pl
|
|
|
|
&& !/\+\+\+ \/tmp\// # t_difftree.pl
|
2018-11-03 18:59:04 +00:00
|
|
|
} @l1;
|
2019-11-05 02:11:15 +00:00
|
|
|
@l1 = map {
|
2022-11-20 12:45:51 +00:00
|
|
|
s/(Internal Error: [^\n]+\.(cpp|h)):[0-9]+:/$1:#:/;
|
2019-11-07 00:47:34 +00:00
|
|
|
s/^-V\{t[0-9]+,[0-9]+\}/-V{t#,#}/; # --vlt vs --vltmt run differences
|
2019-11-05 02:11:15 +00:00
|
|
|
$_;
|
|
|
|
} @l1;
|
2021-07-25 15:20:19 +00:00
|
|
|
for (my $l = 0; $l <= $#l1; ++$l) {
|
2018-11-03 18:59:04 +00:00
|
|
|
# Don't put control chars into our source repository
|
|
|
|
$l1[$l] =~ s/\r/<#013>/mig;
|
|
|
|
$l1[$l] =~ s/Command Failed[^\n]+/Command Failed/mig;
|
2019-07-01 01:53:01 +00:00
|
|
|
$l1[$l] =~ s/Version: Verilator[^\n]+/Version: Verilator ###/mig;
|
2020-04-08 00:55:47 +00:00
|
|
|
$l1[$l] =~ s/CPU Time: +[0-9.]+ seconds[^\n]+/CPU Time: ###/mig;
|
2021-04-24 14:33:49 +00:00
|
|
|
$l1[$l] =~ s/\?v=[0-9.]+/?v=latest/mig; # warning URL
|
2021-08-11 15:22:52 +00:00
|
|
|
$l1[$l] =~ s/_h[0-9a-f]{8}_/_h########_/mg;
|
2018-11-03 18:59:04 +00:00
|
|
|
if ($l1[$l] =~ s/Exiting due to.*/Exiting due to/mig) {
|
|
|
|
splice @l1, $l+1; # Trunc rest
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-09-08 03:50:28 +00:00
|
|
|
my $nl = $#l1; $nl = $#l2 if ($#l2 > $nl);
|
2018-11-03 18:59:04 +00:00
|
|
|
for (my $l=0; $l<=$nl; ++$l) {
|
2021-09-08 03:50:28 +00:00
|
|
|
if (($l1[$l] || "") ne ($l2[$l] || "")) {
|
2018-11-03 18:59:04 +00:00
|
|
|
next try if $moretry;
|
2021-09-08 03:50:28 +00:00
|
|
|
$self->error_keep_going("Line " . ($l+1) . " miscompares; $fn1 != $fn2");
|
|
|
|
warn("F1: " . ($l1[$l] || "*EOF*\n")
|
|
|
|
. "F2: " . ($l2[$l] || "*EOF*\n"));
|
2018-11-03 18:59:04 +00:00
|
|
|
if ($ENV{HARNESS_UPDATE_GOLDEN}) { # Update golden files with current
|
|
|
|
warn "%Warning: HARNESS_UPDATE_GOLDEN set: cp $fn1 $fn2\n";
|
|
|
|
my $fhw = IO::File->new(">$fn2") or $self->error("Files_identical $! $fn2\n");
|
2021-09-08 03:50:28 +00:00
|
|
|
$fhw->print(join('', @l1));
|
2018-11-03 18:59:04 +00:00
|
|
|
} else {
|
|
|
|
warn "To update reference: HARNESS_UPDATE_GOLDEN=1 {command} or --golden\n";
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-05 11:28:51 +00:00
|
|
|
sub files_identical_sorted {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2020-06-05 11:28:51 +00:00
|
|
|
my $fn1 = shift;
|
|
|
|
my $fn2 = shift;
|
|
|
|
my $fn1_is_logfile = shift;
|
2022-11-20 03:43:10 +00:00
|
|
|
return 1 if $self->errors || $self->skips;
|
2020-06-05 11:28:51 +00:00
|
|
|
# Set LC_ALL as suggested in the sort manpage to avoid sort order
|
|
|
|
# changes from the locale.
|
|
|
|
setenv('LC_ALL', "C");
|
|
|
|
my $fn1sort = "$fn1.sort";
|
|
|
|
run(cmd => ["sort", "$fn1", "> $fn1sort"]);
|
|
|
|
return $self->files_identical($fn1sort, $fn2, $fn1_is_logfile);
|
|
|
|
}
|
|
|
|
|
2019-12-10 23:53:35 +00:00
|
|
|
sub copy_if_golden {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2019-12-10 23:53:35 +00:00
|
|
|
my $fn1 = shift;
|
|
|
|
my $fn2 = shift;
|
|
|
|
if ($ENV{HARNESS_UPDATE_GOLDEN}) { # Update golden files with current
|
|
|
|
warn "%Warning: HARNESS_UPDATE_GOLDEN set: cp $fn1 $fn2\n";
|
|
|
|
eval "use File::Copy;";
|
|
|
|
File::Copy::copy($fn1, $fn2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-06-12 00:33:53 +00:00
|
|
|
sub vcd_identical {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2008-06-12 00:33:53 +00:00
|
|
|
my $fn1 = shift;
|
|
|
|
my $fn2 = shift;
|
2022-11-20 03:43:10 +00:00
|
|
|
return 0 if $self->errors || $self->skips;
|
2018-11-02 01:40:55 +00:00
|
|
|
if (!-r $fn1) { $self->error("Vcd_identical file does not exist $fn1\n"); return 0; }
|
|
|
|
if (!-r $fn2) { $self->error("Vcd_identical file does not exist $fn2\n"); return 0; }
|
2009-09-26 13:31:50 +00:00
|
|
|
{
|
2019-05-08 02:34:09 +00:00
|
|
|
# vcddiff to check transitions, if installed
|
|
|
|
my $cmd = qq{vcddiff --help};
|
|
|
|
print "\t$cmd\n" if $::Debug;
|
|
|
|
my $out = `$cmd`;
|
|
|
|
if (!$out || $out !~ /Usage:/) { $self->skip("No vcddiff installed\n"); return 1; }
|
|
|
|
|
|
|
|
$cmd = qq{vcddiff "$fn1" "$fn2"};
|
|
|
|
print "\t$cmd\n" if $::Debug;
|
|
|
|
$out = `$cmd`;
|
2021-07-01 20:35:19 +00:00
|
|
|
if ($? != 0 || $out ne '') {
|
2022-07-27 09:45:33 +00:00
|
|
|
$cmd = qq{vcddiff "$fn2" "$fn1"};
|
|
|
|
print "\t$cmd\n" if $::Debug;
|
|
|
|
$out = `$cmd`;
|
|
|
|
if ($? != 0 || $out ne '') {
|
|
|
|
print $out;
|
|
|
|
$self->error("VCD miscompares $fn2 $fn1\n");
|
|
|
|
$self->copy_if_golden($fn1, $fn2);
|
|
|
|
return 0;
|
|
|
|
}
|
2019-05-08 02:34:09 +00:00
|
|
|
}
|
2009-09-26 13:31:50 +00:00
|
|
|
}
|
|
|
|
{
|
2019-05-08 02:34:09 +00:00
|
|
|
# vcddiff doesn't check module and variable scope, so check that
|
|
|
|
# Also provides backup if vcddiff not installed
|
|
|
|
my $h1 = $self->_vcd_read($fn1);
|
|
|
|
my $h2 = $self->_vcd_read($fn2);
|
2019-05-08 03:00:52 +00:00
|
|
|
$Data::Dumper::Sortkeys = 1;
|
2019-05-08 02:34:09 +00:00
|
|
|
my $a = Dumper($h1);
|
|
|
|
my $b = Dumper($h2);
|
|
|
|
if ($a ne $b) {
|
|
|
|
print "$a\n$b\n" if $::Debug;
|
2020-11-12 23:50:05 +00:00
|
|
|
$self->error("VCD hier miscompares $fn1 $fn2\n");
|
2019-12-10 23:53:35 +00:00
|
|
|
$self->copy_if_golden($fn1, $fn2);
|
2019-05-08 02:34:09 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2008-06-12 00:33:53 +00:00
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-10-02 22:42:53 +00:00
|
|
|
sub fst2vcd {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2018-10-02 22:42:53 +00:00
|
|
|
my $fn1 = shift;
|
|
|
|
my $fn2 = shift;
|
|
|
|
if (!-r $fn1) { $self->error("File does not exist $fn1\n"); return 0; }
|
2020-06-04 20:08:32 +00:00
|
|
|
my $cmd = qq{fst2vcd -h};
|
2018-10-02 22:42:53 +00:00
|
|
|
print "\t$cmd\n" if $::Debug;
|
|
|
|
my $out = `$cmd`;
|
2019-04-11 00:51:38 +00:00
|
|
|
if (!$out || $out !~ /Usage:/) { $self->skip("No fst2vcd installed\n"); return 1; }
|
2018-10-02 22:42:53 +00:00
|
|
|
|
2020-06-04 20:08:32 +00:00
|
|
|
$cmd = qq{fst2vcd -e -f "$fn1" -o "$fn2"};
|
2020-03-07 21:59:46 +00:00
|
|
|
print "\t$cmd\n"; # Always print to help debug race cases
|
2018-10-02 22:42:53 +00:00
|
|
|
$out = `$cmd`;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-03-07 21:59:46 +00:00
|
|
|
sub fst_identical {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2020-03-07 21:59:46 +00:00
|
|
|
my $fn1 = shift;
|
|
|
|
my $fn2 = shift;
|
2022-11-20 03:43:10 +00:00
|
|
|
return 0 if $self->errors || $self->skips;
|
2021-09-08 03:50:28 +00:00
|
|
|
my $tmp = $fn1 . ".vcd";
|
2020-03-07 21:59:46 +00:00
|
|
|
fst2vcd($fn1, $tmp);
|
|
|
|
return vcd_identical($tmp, $fn2);
|
|
|
|
}
|
|
|
|
|
2009-09-26 13:31:50 +00:00
|
|
|
sub _vcd_read {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2009-09-26 13:31:50 +00:00
|
|
|
my $filename = shift;
|
|
|
|
my $data = {};
|
2019-03-10 13:25:23 +00:00
|
|
|
my $fh = IO::File->new("<$filename");
|
2009-09-26 13:31:50 +00:00
|
|
|
if (!$fh) { warn "%Error: $! $filename\n"; return $data; }
|
|
|
|
my @hier = ($data);
|
2018-10-08 02:04:49 +00:00
|
|
|
my $lasthier;
|
2009-09-26 13:31:50 +00:00
|
|
|
while (defined(my $line = $fh->getline)) {
|
2021-04-07 13:55:11 +00:00
|
|
|
if ($line =~ /\$scope (module|struct|interface)\s+(\S+)/) {
|
2019-05-08 02:34:09 +00:00
|
|
|
$hier[$#hier]->{$1} ||= {};
|
|
|
|
push @hier, $hier[$#hier]->{$1};
|
2018-10-08 02:04:49 +00:00
|
|
|
$lasthier = $hier[$#hier];
|
2019-05-08 02:34:09 +00:00
|
|
|
} elsif ($line =~ /(\$var \S+\s+\d+\s+)\S+\s+(\S+)/) {
|
2021-09-08 22:45:25 +00:00
|
|
|
$hier[$#hier]->{$1 . $2} ||= {};
|
2018-10-08 02:04:49 +00:00
|
|
|
$lasthier = $hier[$#hier];
|
|
|
|
} elsif ($line =~ /(\$attrbegin .* \$end)/) {
|
|
|
|
if ($lasthier) { $lasthier->{$1} ||= 1; }
|
2019-05-08 02:34:09 +00:00
|
|
|
} elsif ($line =~ /\$enddefinitions/) {
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
while ($line =~ s/\$upscope//) {
|
|
|
|
pop @hier;
|
2018-10-08 02:04:49 +00:00
|
|
|
$lasthier = undef;
|
2019-05-08 02:34:09 +00:00
|
|
|
}
|
2009-09-26 13:31:50 +00:00
|
|
|
}
|
|
|
|
return $data;
|
|
|
|
}
|
|
|
|
|
2017-10-26 23:14:05 +00:00
|
|
|
our $_Cxx_Version;
|
2021-09-08 22:45:25 +00:00
|
|
|
|
2017-10-26 23:14:05 +00:00
|
|
|
sub cxx_version {
|
2020-05-31 13:03:51 +00:00
|
|
|
$_Cxx_Version ||= `$ENV{MAKE} -C $ENV{VERILATOR_ROOT}/test_regress -f Makefile print-cxx-version`;
|
2017-10-26 23:14:05 +00:00
|
|
|
return $_Cxx_Version;
|
|
|
|
}
|
|
|
|
|
2021-06-13 19:13:27 +00:00
|
|
|
our $_Cfg_with_ccache;
|
2021-09-08 22:45:25 +00:00
|
|
|
|
2021-06-06 23:56:30 +00:00
|
|
|
sub cfg_with_ccache {
|
2021-06-13 19:13:27 +00:00
|
|
|
$_Cfg_with_ccache ||= `grep "OBJCACHE \?= ccache" "$ENV{VERILATOR_ROOT}/include/verilated.mk"` ne "";
|
|
|
|
return $_Cfg_with_ccache;
|
|
|
|
}
|
|
|
|
|
|
|
|
our $_Cfg_with_m32;
|
2021-09-08 22:45:25 +00:00
|
|
|
|
2021-06-13 19:13:27 +00:00
|
|
|
sub cfg_with_m32 {
|
|
|
|
$_Cfg_with_m32 ||= `grep "CXX.*=.*-m32" "$ENV{VERILATOR_ROOT}/include/verilated.mk"` ne "";
|
|
|
|
return $_Cfg_with_m32;
|
2021-06-06 23:56:30 +00:00
|
|
|
}
|
|
|
|
|
2018-11-03 13:07:28 +00:00
|
|
|
sub tries {
|
|
|
|
# Number of retries when reading logfiles, generally only need many
|
|
|
|
# retries when system is busy running a lot of tests
|
|
|
|
return 2 if !$::Fork->running;
|
|
|
|
return 7 if (scalar($::Fork->running)) > 1;
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
2021-07-14 21:37:37 +00:00
|
|
|
sub glob_all {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2021-07-14 21:37:37 +00:00
|
|
|
my $pattern = shift;
|
|
|
|
|
|
|
|
return glob($pattern);
|
|
|
|
}
|
|
|
|
|
|
|
|
sub glob_one {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2021-07-14 21:37:37 +00:00
|
|
|
my $pattern = shift;
|
2022-11-20 03:43:10 +00:00
|
|
|
return if $self->errors || $self->skips;
|
2021-07-14 21:37:37 +00:00
|
|
|
|
|
|
|
my @files = glob($pattern);
|
|
|
|
my $n = scalar @files;
|
|
|
|
if ($n == 0) {
|
2021-09-08 03:50:28 +00:00
|
|
|
$self->error("glob_one: pattern '$pattern' does not match any files\n");
|
2021-07-14 21:37:37 +00:00
|
|
|
} elsif ($n != 1) {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $msg = "glob_one: pattern '$pattern' matches multiple files:\n";
|
|
|
|
foreach my $file (@files) {
|
|
|
|
$msg .= $file . "\n";
|
|
|
|
}
|
|
|
|
$self->error($msg);
|
2021-07-14 21:37:37 +00:00
|
|
|
} else {
|
2021-09-08 03:50:28 +00:00
|
|
|
return $files[0];
|
2021-07-14 21:37:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
sub file_grep_not {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2006-08-26 11:35:28 +00:00
|
|
|
my $filename = shift;
|
|
|
|
my $regexp = shift;
|
2012-05-11 22:24:49 +00:00
|
|
|
my $expvalue = shift;
|
2022-11-20 03:43:10 +00:00
|
|
|
return if $self->errors || $self->skips;
|
2012-05-11 22:24:49 +00:00
|
|
|
!defined $expvalue or $self->error("file_grep_not: Unexpected 3rd argument: $expvalue");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
my $contents = $self->file_contents($filename);
|
|
|
|
return if ($contents eq "_Already_Errored_");
|
|
|
|
if ($contents =~ /$regexp/) {
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->error("File_grep_not: $filename: Regexp found: $regexp\n");
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub file_grep {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2006-08-26 11:35:28 +00:00
|
|
|
my $filename = shift;
|
|
|
|
my $regexp = shift;
|
2012-05-11 22:24:49 +00:00
|
|
|
my $expvalue = shift;
|
2022-11-20 03:43:10 +00:00
|
|
|
return if $self->errors || $self->skips;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
my $contents = $self->file_contents($filename);
|
|
|
|
return if ($contents eq "_Already_Errored_");
|
|
|
|
if ($contents !~ /$regexp/) {
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->error("File_grep: $filename: Regexp not found: $regexp\n");
|
2022-03-06 05:43:28 +00:00
|
|
|
} elsif (defined($expvalue) && $expvalue ne $1) {
|
2019-05-08 02:34:09 +00:00
|
|
|
$self->error("File_grep: $filename: Got='$1' Expected='$expvalue' in regexp: $regexp\n");
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-14 21:37:37 +00:00
|
|
|
sub file_grep_any {
|
|
|
|
my $self = $Self;
|
|
|
|
my @filenames = @{$_[0]}; shift;
|
|
|
|
my $regexp = shift;
|
|
|
|
my $expvalue = shift;
|
2022-11-20 03:43:10 +00:00
|
|
|
return if $self->errors || $self->skips;
|
2021-07-14 21:37:37 +00:00
|
|
|
|
|
|
|
foreach my $filename (@filenames) {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $contents = $self->file_contents($filename);
|
|
|
|
return if ($contents eq "_Already_Errored_");
|
|
|
|
if ($contents =~ /$regexp/) {
|
|
|
|
if ($expvalue && $expvalue ne $1) {
|
|
|
|
$self->error("file_grep: $filename: Got='$1' Expected='$expvalue' in regexp: $regexp\n");
|
|
|
|
}
|
|
|
|
return;
|
2021-07-14 21:37:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
my $msg = "file_grep_any: Regexp '$regexp' not found in any of the following files:\n";
|
|
|
|
foreach my $filename (@filenames) {
|
2021-09-08 03:50:28 +00:00
|
|
|
$msg .= $filename . "\n";
|
2021-07-14 21:37:37 +00:00
|
|
|
}
|
|
|
|
$self->error($msg);
|
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
my %_File_Contents_Cache;
|
|
|
|
|
|
|
|
sub file_contents {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2006-08-26 11:35:28 +00:00
|
|
|
my $filename = shift;
|
|
|
|
|
|
|
|
if (!$_File_Contents_Cache{$filename}) {
|
2019-05-08 02:34:09 +00:00
|
|
|
my $fh = IO::File->new("<$filename");
|
|
|
|
if (!$fh) {
|
|
|
|
$_File_Contents_Cache{$filename} = "_Already_Errored_";
|
2022-12-03 00:57:40 +00:00
|
|
|
$self->error("File_contents file not found: " . $filename . "\n");
|
2019-05-08 02:34:09 +00:00
|
|
|
return $_File_Contents_Cache{$filename};
|
|
|
|
}
|
|
|
|
local $/; undef $/;
|
|
|
|
my $wholefile = <$fh>;
|
|
|
|
$fh->close();
|
|
|
|
$_File_Contents_Cache{$filename} = $wholefile;
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return $_File_Contents_Cache{$filename};
|
|
|
|
}
|
|
|
|
|
2006-09-13 14:38:48 +00:00
|
|
|
sub write_wholefile {
|
2021-09-08 03:50:28 +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;
|
2020-10-14 13:30:15 +00:00
|
|
|
delete $_File_Contents_Cache{$filename};
|
|
|
|
}
|
|
|
|
|
|
|
|
sub file_sed {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2020-10-14 13:30:15 +00:00
|
|
|
my $infilename = shift;
|
|
|
|
my $outfilename = shift;
|
|
|
|
my $editcb = shift;
|
|
|
|
my $contents = $self->file_contents($infilename);
|
|
|
|
{
|
|
|
|
$_ = $contents;
|
|
|
|
$editcb->($contents);
|
|
|
|
$contents = $_;
|
|
|
|
}
|
|
|
|
$self->write_wholefile($outfilename, $contents);
|
2006-09-13 14:38:48 +00:00
|
|
|
}
|
|
|
|
|
2021-04-11 22:55:06 +00:00
|
|
|
sub extract {
|
2021-09-08 03:50:28 +00:00
|
|
|
my $self = (ref $_[0] ? shift : $Self);
|
2021-09-08 22:45:25 +00:00
|
|
|
my %param = ( #in =>,
|
2021-04-11 22:55:06 +00:00
|
|
|
#out =>
|
|
|
|
regexp => qr/.*/,
|
|
|
|
lineno_adjust => -9999,
|
|
|
|
lines => undef, #'#, #-#',
|
|
|
|
@_);
|
|
|
|
|
|
|
|
my $temp_fn = $param{out};
|
|
|
|
$temp_fn =~ s!.*/!!g;
|
|
|
|
$temp_fn = $self->{obj_dir} . "/" . $temp_fn;
|
|
|
|
|
|
|
|
my @out;
|
|
|
|
my $emph = "";
|
|
|
|
my $lineno = 0;
|
|
|
|
my $lineno_out = 0;
|
|
|
|
{
|
|
|
|
my $fh = IO::File->new("<$param{in}") or die "%Error: $! $param{in},";
|
|
|
|
while (defined(my $line = $fh->getline)) {
|
|
|
|
++$lineno;
|
|
|
|
if ($line =~ /$param{regexp}/
|
|
|
|
&& _lineno_match($lineno, $param{lines})) {
|
|
|
|
if ($line =~ m!t/[A-Za-z0-9_]+.v:(\d+):(\d+):!) {
|
|
|
|
my $lineno = $1;
|
|
|
|
my $col = $2;
|
|
|
|
$lineno += $param{lineno_adjust};
|
|
|
|
$lineno = 1 if $lineno < 1;
|
|
|
|
$line =~ s!t/[A-Za-z0-9_]+.v:(\d+):(\d+):!example.v:${lineno}:${col}!;
|
|
|
|
}
|
|
|
|
push @out, " " . $line;
|
|
|
|
++$lineno_out;
|
|
|
|
if ($line =~ /<--/) {
|
|
|
|
$emph .= "," if $emph;
|
|
|
|
$emph .= $lineno_out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
my $fhw = IO::File->new(">$temp_fn") or die "%Error: $! $temp_fn,";
|
|
|
|
my $lang = "";
|
|
|
|
$lang = " sv" if $param{in} =~ /\.s?vh?$/;
|
|
|
|
$fhw->print(".. comment: generated by " . $self->{name} . "\n");
|
|
|
|
$fhw->print(".. code-block::${lang}\n");
|
|
|
|
$fhw->print(" :linenos:\n") if $lang && $#out > 0;
|
|
|
|
$fhw->print(" :emphasize-lines: ${emph}\n") if $emph;
|
|
|
|
$fhw->print("\n");
|
2021-09-08 22:45:25 +00:00
|
|
|
|
2021-04-11 22:55:06 +00:00
|
|
|
foreach my $line (@out) {
|
|
|
|
$fhw->print($line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$self->files_identical($temp_fn, $param{out});
|
|
|
|
}
|
|
|
|
|
|
|
|
sub _lineno_match {
|
|
|
|
my $lineno = shift;
|
|
|
|
my $lines = shift;
|
|
|
|
return 1 if !defined $lines;
|
2021-05-19 12:14:14 +00:00
|
|
|
foreach my $lc (split /,/, $lines) {
|
|
|
|
if ($lc =~ /^(\d+)$/) {
|
|
|
|
return 1 if $1 == $lineno;
|
|
|
|
} elsif ($lc =~ /^(\d+)-(\d+)$/) {
|
|
|
|
return 1 if $1 <= $lineno && $2 >= $lineno;
|
|
|
|
}
|
2021-04-11 22:55:06 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
2021-09-08 22:45:25 +00:00
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
sub schedule {
|
|
|
|
my $self = shift;
|
|
|
|
my %params = (@_);
|
2019-03-10 16:14:58 +00:00
|
|
|
|
2020-05-16 10:15:25 +00:00
|
|
|
$params{run_pre_start}->($self);
|
2019-03-10 16:14:58 +00:00
|
|
|
if (my $pid = fork()) { # Parent
|
|
|
|
waitpid($pid, 0);
|
|
|
|
} else { # Child
|
|
|
|
$params{run_on_start}->($self);
|
|
|
|
exit(0); # Don't close anything
|
|
|
|
}
|
|
|
|
$params{run_on_finish}->($self);
|
2006-08-26 11:35:28 +00:00
|
|
|
return $self;
|
|
|
|
}
|
2021-09-08 22:45:25 +00:00
|
|
|
sub max_proc { }
|
|
|
|
sub sig_child { }
|
|
|
|
sub kill_tree_all { }
|
|
|
|
sub wait_all { }
|
|
|
|
sub ready { }
|
|
|
|
sub running { }
|
2019-10-17 03:17:31 +00:00
|
|
|
sub is_any_left { return 0; }
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
#######################################################################
|
|
|
|
1;
|
2021-09-08 03:50:28 +00:00
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
package main;
|
|
|
|
__END__
|
|
|
|
|
|
|
|
=pod
|
|
|
|
|
|
|
|
=head1 NAME
|
|
|
|
|
|
|
|
driver.pl - Run regression tests
|
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
|
2023-01-21 21:17:26 +00:00
|
|
|
make test # In Verilator directory
|
|
|
|
./driver.pl # Or, to run directly
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
|
2012-03-24 15:10:17 +00:00
|
|
|
driver.pl invokes Verilator or another simulator on each test file.
|
2023-01-21 21:17:26 +00:00
|
|
|
See docs/internals.rst in the distribution for more information.
|
2007-03-13 17:15:00 +00:00
|
|
|
|
2009-01-21 21:56:50 +00:00
|
|
|
=head1 DISTRIBUTION
|
|
|
|
|
2019-11-08 03:33:59 +00:00
|
|
|
The latest version is available from L<https://verilator.org>.
|
2009-01-21 21:56:50 +00:00
|
|
|
|
2023-01-01 15:18:39 +00:00
|
|
|
Copyright 2003-2023 by Wilson Snyder. This program is free software; you
|
2020-03-21 15:24:24 +00:00
|
|
|
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
|
|
|
|
Version 2.0.
|
|
|
|
|
|
|
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-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
|
|
|
|
|
|
|
|
######################################################################
|