rlm@67: #!/usr/bin/env perl
rlm@67: # -*- perl -*-
rlm@67: 
rlm@67: use strict;
rlm@67: use warnings;
rlm@67: use Getopt::Long qw(:config no_auto_abbrev no_ignore_case pass_through);
rlm@67: use IO::Pty;
rlm@67: 
rlm@67: #
rlm@67: # Turn on warnings
rlm@67: #
rlm@67: $^W = 1;
rlm@67: 
rlm@67: ##
rlm@67: ## Benchmark run script
rlm@67: ##
rlm@67: 
rlm@67: sub ExecModel($);
rlm@67: sub Exec($$);
rlm@67: sub CompareOutput();
rlm@67: sub ReadConfig($$);
rlm@67: 
rlm@67: 
rlm@67: #############################################################################
rlm@67: #############################################################################
rlm@67: ##
rlm@67: ## Start by figuring out the model location and type.
rlm@67: ##
rlm@67: #############################################################################
rlm@67: #############################################################################
rlm@67: 
rlm@67: my %config;
rlm@67: 
rlm@67: ReadConfig("config/env.sh", 1);
rlm@67: ReadConfig("$config{modelDir}/config/env.sh", 1);
rlm@67: ReadConfig("$config{modelDir}/config/signature.sh", 0);
rlm@67: 
rlm@67: ##
rlm@67: ## Pseudo-enumeration of possible model types
rlm@67: ##
rlm@67: my $MODEL_NONE = 0;
rlm@67: my $MODEL_FPGA = 1;                     # Model runs on FPGA hardware
rlm@67: my $MODEL_BLUESIM = 2;                  # Bluesim
rlm@67: my $MODEL_VSIM = 3;                     # Verilog simulator
rlm@67: 
rlm@67: my $mType = $MODEL_NONE;
rlm@67: 
rlm@67: 
rlm@67: #############################################################################
rlm@67: #############################################################################
rlm@67: ##
rlm@67: ## Process command line arguments
rlm@67: ##
rlm@67: #############################################################################
rlm@67: #############################################################################
rlm@67: 
rlm@67: my $help = 0;
rlm@67: my $onlyCompare = 0;
rlm@67: my $noCompare = 0;
rlm@67: my $forceLoad = 0;
rlm@67: my $gdb = 0;
rlm@67: my $noProgram = 0;
rlm@67: my $noReserve = 0;
rlm@67: my $funcpPrefix = "";
rlm@67: my $funcpSuffix = "";
rlm@67: my $noshowfp = 0;
rlm@67: my $printCycle = undef;
rlm@67: my $showfp = undef;
rlm@67: my $bluesimCmd = undef;
rlm@67: my $vcdStart = undef;
rlm@67: my $vcdCycles = 20000;
rlm@67: my $m5run = 0;
rlm@67: 
rlm@67: my $status = GetOptions("help!" => \$help,
rlm@67:                         "gdb!" => \$gdb,
rlm@67:                         "noprogram!" => \$noProgram,
rlm@67:                         "noreserve!" => \$noReserve,
rlm@67:                         "force-load!" => \$forceLoad,
rlm@67:                         "funcp-prefix=s" => \$funcpPrefix,
rlm@67:                         "funcp-suffix=s" => \$funcpSuffix,
rlm@67:                         "noshowfp!" => \$noshowfp,
rlm@67:                         "onlycompare!" => \$onlyCompare,
rlm@67:                         "nocompare!" => \$noCompare,
rlm@67:                         "pc=s" => \$printCycle,
rlm@67:                         "showfp:s" => \$showfp,
rlm@67:                         "bluesim=s" => \$bluesimCmd,
rlm@67:                         "vcdstart=i" => \$vcdStart,
rlm@67:                         "vcdcycles=i" => \$vcdCycles,
rlm@67:                         "m5!" => \$m5run,
rlm@67:                        );
rlm@67: 
rlm@67: # Put quotation marks back on arguments that have spaces since they will be
rlm@67: # passed through a shell once more.
rlm@67: foreach my $i ( 0 .. $#ARGV ) {
rlm@67:     if (($ARGV[$i] =~ /\s/) && ! ($ARGV[$i] =~ /['"]$/)) {
rlm@67:         $ARGV[$i] = '"' . $ARGV[$i] . '"';
rlm@67:     }
rlm@67: }
rlm@67: 
rlm@67: if ($onlyCompare != 0) {
rlm@67:     exit(CompareOutput());
rlm@67: }
rlm@67: 
rlm@67: if ($m5run != 0) {
rlm@67:     $mType = $MODEL_NONE;
rlm@67: }
rlm@67: elsif (-f "$config{modelDir}/$config{model}_hw.errinfo") {
rlm@67:     $mType = $MODEL_FPGA;
rlm@67: }
rlm@67: elsif (-f "$config{modelDir}/$config{model}_hw.exe") {
rlm@67:     $mType = $MODEL_BLUESIM;
rlm@67: }
rlm@67: elsif (-f "$config{modelDir}/$config{model}_hw.vexe") {
rlm@67:     $mType = $MODEL_VSIM;
rlm@67: }
rlm@67: else {
rlm@67:     die("Can't determine model type");
rlm@67: }
rlm@67: 
rlm@67: if ($help || ! $status) {
rlm@67:     print STDERR "\nArguments:\n";
rlm@67:     print STDERR "   [--gdb]                 Invokes the software side in gdb\n";
rlm@67:     print STDERR "   [--noprogram]           Skips the FPGA load and reservation steps\n";
rlm@67:     print STDERR "   [--noreserve]           Skips the FPGA reservation steps\n";
rlm@67:     print STDERR "   [--funcp-prefix=\"<prefix>\"]\n";
rlm@67:     print STDERR "                           Prepend prefix to HAsim's --funcp argument\n";
rlm@67:     print STDERR "   [--funcp-suffix=\"<suffix>\"]\n";
rlm@67:     print STDERR "                           Append suffix to HAsim's --funcp argument\n";
rlm@67:     print STDERR "   [--onlycompare]         Only compare output files (without running)\n";
rlm@67:     print STDERR "   [--nocompare]           Skip comparison of output files\n";
rlm@67:     print STDERR "   [--m5]                  Run workload in m5 without HAsim\n";
rlm@67: 
rlm@67:     if ($mType == $MODEL_FPGA) {
rlm@67:         print STDERR "   [--force-load]          Load a bitfile to the FPGA even if it has errors\n";
rlm@67:     }
rlm@67: 
rlm@67:     if ($mType == $MODEL_BLUESIM) {
rlm@67:         print STDERR "   [--vcdstart=<cycle>]    Generate VCD dump for wave viewer (e.g. gtkwave)\n";
rlm@67:         print STDERR "   [--vcdcycles=<cycles>]  VCD dump length (default = 20000)\n";
rlm@67:     }
rlm@67: 
rlm@67:     if ($config{isHybridModel}) {
rlm@67:         my $cmd = "$config{modelDir}/$config{model} --help-run-append";
rlm@67:         system($cmd);
rlm@67:     }
rlm@67: 
rlm@67:     exit(1);
rlm@67: }
rlm@67: 
rlm@67: #############################################################################
rlm@67: #############################################################################
rlm@67: ##
rlm@67: ## Adjust model arguments
rlm@67: ##
rlm@67: #############################################################################
rlm@67: #############################################################################
rlm@67: 
rlm@67: # Show front panel?
rlm@67: if ($noshowfp) {
rlm@67:     $showfp = 'none';
rlm@67: }
rlm@67: elsif (defined($showfp)) {
rlm@67:     # Specified on the run command line
rlm@67:     $showfp = 'gui' if ($showfp eq '');
rlm@67: }
rlm@67: elsif ($config{feeder} eq 'none') {
rlm@67:     # For null feeder default to showing LEDs on stdout
rlm@67:     $showfp = 'stdout';
rlm@67: }
rlm@67: else {
rlm@67:     # Other models have heartbeats
rlm@67:     $showfp = 'none';
rlm@67: }
rlm@67: 
rlm@67: my $feedFlags = "${funcpPrefix} $config{feedFlags} ${funcpSuffix}";
rlm@67: 
rlm@67: if ($config{feeder} eq 'm5') {
rlm@67:     # Tell m5 to be quiet and not to enable the remote gdb port.  Under netbatch
rlm@67:     # it appears there are sometimes attempts to connect to the port, which
rlm@67:     # stops simulation.
rlm@67:     $feedFlags = "--quiet --remote-gdb-port=0 ${feedFlags}";
rlm@67: }
rlm@67: 
rlm@67: my $cmd;
rlm@67: 
rlm@67: if ($m5run == 0) {
rlm@67:     # Normal run
rlm@67:     $cmd = "$config{modelDir}/$config{model} --modeldir=$config{modelDir} --workload=$config{workload} --showfp=${showfp} --funcp=\"${feedFlags}\" $config{genFlags}";
rlm@67:     foreach my $c (@ARGV) {
rlm@67:         $cmd .= " $c";
rlm@67:     }
rlm@67: 
rlm@67:     if (defined($printCycle)) {
rlm@67:         $cmd .= " --pc=${printCycle}";
rlm@67:     }
rlm@67: }
rlm@67: else {
rlm@67:     if ($config{feeder} ne 'm5') {
rlm@67:         die("This workload does not use m5");
rlm@67:     }
rlm@67: 
rlm@67:     # Running inside m5 without HAsim
rlm@67:     my $m5cmd;
rlm@67:     if (exists($ENV{M5BIN})) {
rlm@67:         $m5cmd = $ENV{M5BIN};
rlm@67:     }
rlm@67:     else {
rlm@67:         my $m5bin = "platform/m5/build/ALPHA_SE/m5." . ($gdb ? "debug" : "opt");
rlm@67:         $m5cmd = `awb-resolver ${m5bin}`;
rlm@67:         chomp($m5cmd);
rlm@67:         die("Failed to find $m5bin") if ($m5cmd eq '');
rlm@67:     }
rlm@67: 
rlm@67:     $cmd = "${m5cmd} ${feedFlags}";
rlm@67: 
rlm@67:     # Drop --hasim-sim
rlm@67:     $cmd =~ s/--hasim-sim //;
rlm@67:     # Drop escaping of quotes
rlm@67:     $cmd =~ s/\\"/"/g;
rlm@67: 
rlm@67:     $noProgram = 1;
rlm@67: }
rlm@67: 
rlm@67: #
rlm@67: # Bluesim arguments
rlm@67: #
rlm@67: 
rlm@67: # Generate dump.vcd for wave viewer (e.g. gtkwave)?
rlm@67: if (defined($vcdStart)) {
rlm@67:     my $vcdCmd = "sim vcd on; sim step $vcdCycles; sim stop";
rlm@67:     if ($vcdStart > 0) {
rlm@67:         $vcdCmd = "sim step ${vcdStart}; ${vcdCmd}";
rlm@67:     }
rlm@67: 
rlm@67:     if (defined($bluesimCmd)) {
rlm@67:         $bluesimCmd .= " ";
rlm@67:     }
rlm@67:     else {
rlm@67:         $bluesimCmd = "";
rlm@67:     }
rlm@67:     $bluesimCmd .= "-c \"$vcdCmd\"";
rlm@67: }
rlm@67: 
rlm@67: if (defined($bluesimCmd)) {
rlm@67:     $cmd .= " --bluesim=\'$bluesimCmd\'";
rlm@67: }
rlm@67: 
rlm@67: # Adjust the arguments for Bluesim if it is being invoked directly
rlm@67: if (! $config{isHybridModel} && ($mType == $MODEL_BLUESIM)) {
rlm@67:     $cmd =~ s/\s--/ +--/g;
rlm@67: 
rlm@67:     # Bluesim may expect to load a program from a well known file
rlm@67:     unlink('program.vmh');
rlm@67:     link("program/$config{workload}.$config{ISA}.vmh", 'program.vmh');
rlm@67: }
rlm@67: 
rlm@67: 
rlm@67: #############################################################################
rlm@67: #############################################################################
rlm@67: ##
rlm@67: ## Load the FPGA and run the model
rlm@67: ##
rlm@67: #############################################################################
rlm@67: #############################################################################
rlm@67: 
rlm@67: #
rlm@67: # Move old stats file so we are sure statistics come from this run
rlm@67: #
rlm@67: if (-f "$config{workload}.stats") {
rlm@67:     rename("$config{workload}.stats", "$config{workload}.stats.old");
rlm@67: }
rlm@67: 
rlm@67: if ($mType == $MODEL_FPGA) {
rlm@67:     if (! defined($printCycle)) {
rlm@67:         # User didn't specify a cycle printing interval.  Pick one more reasonable
rlm@67:         # for HW.
rlm@67:         #$cmd .= " --pc=10000000";
rlm@67:     }
rlm@67: 
rlm@67:     # Load FPGA
rlm@67:     $ENV{FPGA_BIT_FILE} = "$config{modelDir}/.xilinx/$config{model}_par.bit";
rlm@67: 
rlm@67:     if (! $noProgram) {
rlm@67:         if (! $forceLoad && -s "$config{modelDir}/$config{model}_hw.errinfo") {
rlm@67:             print STDERR "FPGA bit file has errors:\n\n";
rlm@67:             system("cat $config{modelDir}/$config{model}_hw.errinfo > /dev/stderr");
rlm@67:             print STDERR "\nUse --force-load to ignore the error.\n";
rlm@67:             exit(1);
rlm@67:         }
rlm@67: 
rlm@67:         if (! $noReserve) {
rlm@67:             Exec("hasim-fpga-ctrl --reserve", "Failed to reserve FPGA");
rlm@67:         }
rlm@67: 
rlm@67:         # Does a download script exist to program the FPGA?
rlm@67:         my $needProgram = (-f "$config{modelDir}/config/$config{model}.download");
rlm@67:         $needProgram = 1;
rlm@67: 
rlm@67:         # Is the FPGA already programmed with the correct bit file?
rlm@67:         if (exists($config{signature})) {
rlm@67:             my $curSignature = `hasim-fpga-ctrl --getsignature`;
rlm@67:             chomp($curSignature);
rlm@67:             if ($curSignature eq $config{signature}) {
rlm@67:                 print "FPGA is already programmed (signature match)...\n";
rlm@67:                 #$needProgram = 0;
rlm@67:             }
rlm@67:         }
rlm@67: 
rlm@67:         if ($needProgram) {
rlm@67:             Exec("hasim-fpga-ctrl --program", "Failed to enter FPGA programming mode");
rlm@67: 
rlm@67:             my $dir = `pwd`;
rlm@67:             chomp($dir);
rlm@67:             Exec("(cd $config{modelDir}; ./config/$config{model}.download ${dir}/FPGA_programming.log)", "Failed to program FPGA");
rlm@67: 
rlm@67:             if (exists($config{signature})) {
rlm@67:                 Exec("hasim-fpga-ctrl --setsignature=$config{signature}", "Failed to set FPGA bit image signature");
rlm@67:             }
rlm@67:         }
rlm@67: 
rlm@67:         Exec("hasim-fpga-ctrl --activate", "Failed to activate FPGA or driver");
rlm@67:     }
rlm@67: }
rlm@67: 
rlm@67: # Run the software side or a hardware simulator
rlm@67: my $run_status = 0;
rlm@67: if ($config{isHybridModel} || ($mType != $MODEL_FPGA)) {
rlm@67:     $cmd = $cmd." 2> proc.trace\n";
rlm@67:     $run_status = ExecModel($cmd);
rlm@67: }
rlm@67: 
rlm@67: # Create a stats file for null workloads to make regression.launcher happy (HACK)
rlm@67: if ( $config{workload} eq "null" ) {
rlm@67:   system("touch null.stats");
rlm@67: }
rlm@67: 
rlm@67: if (-f "hasim_events.out") {
rlm@67:     system("sort hasim_events.out -o hasim_events.out.$$; mv -f hasim_events.out.$$ hasim_events.out");
rlm@67: }
rlm@67: if (-f "$config{workload}.stats") {
rlm@67:     system("sort $config{workload}.stats -o $config{workload}.stats.$$; mv -f $config{workload}.stats.$$ $config{workload}.stats");
rlm@67: }
rlm@67: 
rlm@67: if (($mType == $MODEL_FPGA) && ! $noProgram && ! $noReserve) {
rlm@67:     Exec("hasim-fpga-ctrl --drop-reservation", "Failed to drop FPGA reservation");
rlm@67: }
rlm@67: 
rlm@67: if ($run_status != 0) {
rlm@67:     exit($run_status);
rlm@67: }
rlm@67: else {
rlm@67:     exit(CompareOutput());
rlm@67: }
rlm@67: 
rlm@67: 
rlm@67: sub ErrorExit($) {
rlm@67:     my $msg = shift;
rlm@67: 
rlm@67:     print STDERR "${msg}\n";
rlm@67: 
rlm@67:     if (($mType == $MODEL_FPGA) && ! $noProgram && ! $noReserve) {
rlm@67:         system("hasim-fpga-ctrl --drop-reservation");
rlm@67:     }
rlm@67: 
rlm@67:     exit(1);
rlm@67: }
rlm@67: 
rlm@67: 
rlm@67: ##
rlm@67: ## ExecModel --
rlm@67: ##   This is the routine that actually invokes the model.  stdout and stderr
rlm@67: ##   are logged in a file.  The return value is the exit status of the model.
rlm@67: ##
rlm@67: sub ExecModel($) {
rlm@67:     my $cmd = shift;
rlm@67: 
rlm@67:     if ($gdb) {
rlm@67:         ## gdb needs stdin.  Just use system() and don't do logging.
rlm@67:         system("gdb -args " . $cmd);
rlm@67:         return 0;
rlm@67:     }
rlm@67: 
rlm@67:     ##
rlm@67:     ## Invoke the model, but log its output both to stdout and to a file.
rlm@67:     ## Use a pty so the invoked program will use line buffering instead
rlm@67:     ## of fully buffered writes.  (Libc sets up stdout line buffered when
rlm@67:     ## it thinks it is writing to a terminal.  It uses fully buffered
rlm@67:     ## writing to a pipe.)
rlm@67:     ##
rlm@67: 
rlm@67:     my $pty = new IO::Pty;
rlm@67:     my $slave = $pty->slave();
rlm@67: 
rlm@67:     my $pid = fork();
rlm@67:     die "Couldn't fork: $!" unless defined $pid;
rlm@67: 
rlm@67:     if (! $pid) {
rlm@67:         # Child process is the monitoring process
rlm@67:         $pty->make_slave_controlling_terminal();
rlm@67: 
rlm@67:         my $output = "$config{workload}.$config{ISA}.out";  
rlm@67:         if(exists($config{silent})) {
rlm@67: 	    $output = "/dev/null";
rlm@67: 	}
rlm@67: 
rlm@67:         if (! open(LOG, ">$output")) {
rlm@67:             print STDERR "Error opening log file $output\n";
rlm@67:         }
rlm@67: 
rlm@67:         # Unbuffered I/O loop
rlm@67:         while (1) {
rlm@67:             my $buf;
rlm@67:             my $n = sysread($slave, $buf, 4096);
rlm@67: 
rlm@67:             last if ($n == 0);
rlm@67: 
rlm@67:             syswrite(STDOUT, $buf);
rlm@67:             syswrite(LOG, $buf);
rlm@67:         }
rlm@67: 
rlm@67:         close(LOG);
rlm@67:         exit(0);
rlm@67:     }
rlm@67: 
rlm@67:     # Bind new PTY to STDOUT (but save old STDOUT)
rlm@67:     $pty->close_slave();
rlm@67:     open(my $oldOut, ">&", STDOUT) or die $!;
rlm@67:     open(STDOUT, ">&", $pty) or die $!;
rlm@67: 
rlm@67:     # Run model
rlm@67:     my $result = system("${cmd} 2>&1");
rlm@67: 
rlm@67:     # Send ^d to end child logging thread
rlm@67:     print "\cD";
rlm@67: 
rlm@67:     # Return to normal STDOUT
rlm@67:     close(STDOUT);
rlm@67:     open(STDOUT, ">&", $oldOut) or die $!;
rlm@67:     close($oldOut);
rlm@67: 
rlm@67:     # Compute exit status of model
rlm@67:     my $status = 0;
rlm@67:     if ($result == -1) {
rlm@67:         print STDERR "Model execution failed\n";
rlm@67:         $status = 1;
rlm@67:     }
rlm@67:     elsif ($result & 127) {
rlm@67:         print STDERR "Child died with signal " . ($result & 127) . ", " . (($result & 128) ? 'with' : 'without') . " coredump\n";
rlm@67:         $status = 1;
rlm@67:     }
rlm@67:     elsif (($result >> 8) != 0) {
rlm@67:         $status = $result >> 8;
rlm@67:         print "Model exited with status $status\n";
rlm@67:     }
rlm@67: 
rlm@67:     return $status;
rlm@67: }
rlm@67: 
rlm@67: 
rlm@67: sub Exec($$) {
rlm@67:     my $cmd = shift;
rlm@67:     my $errmsg = shift;
rlm@67: 
rlm@67:     system($cmd);
rlm@67:     if ($? == -1) {
rlm@67:         ErrorExit("Failed to execute $cmd: $!");
rlm@67:     }
rlm@67:     elsif ($? & 127) {
rlm@67:         ErrorExit("Child died with signal " . ($? & 127) . ", " . (($? & 128) ? 'with' : 'without') . " coredump");
rlm@67:     }
rlm@67:     elsif (($? >> 8) != 0) {
rlm@67:         ErrorExit("${errmsg}");
rlm@67:     }
rlm@67: }
rlm@67: 
rlm@67: 
rlm@67: sub CompareOutput() {
rlm@67:     return 0 if ($noCompare != 0);
rlm@67:     return 0 if (! exists($config{compare}) || ($config{compare} eq ''));
rlm@67: 
rlm@67:     # run the checker 
rlm@67:     `cd ./checker/ && make clean && make`;
rlm@67:     `./checker/checker input.wav out_gold.wav`;
rlm@67:     print "about to call compare_wavs\n";
rlm@67:     my $out=`./checker/compare_wavs/compare_wavs 10 out_gold.wav out_hw.wav`;
rlm@67:    
rlm@67:     if ($out !~ /fail/) {
rlm@67:         print "*** Output comparison passed ***\n";
rlm@67:         system("touch $config{workload}.stats");
rlm@67:         return 0;
rlm@67:     }
rlm@67:     else {
rlm@67:         print "*** Output comparison failed ***\n";
rlm@67:         return 1;
rlm@67:     }
rlm@67: }
rlm@67: 
rlm@67: 
rlm@67: #
rlm@67: # Read the configuration file
rlm@67: #
rlm@67: sub ReadConfig($$) {
rlm@67:     my $conf = shift;
rlm@67:     my $required = shift;
rlm@67: 
rlm@67:     my $status = open(CONFIG, "< $conf");
rlm@67:     if (! $status) {
rlm@67:         return if (! $required);
rlm@67:         die("Failed to open $conf");
rlm@67:     }
rlm@67: 
rlm@67:     while (<CONFIG>) {
rlm@67:         chomp;
rlm@67:         my $t = $_;
rlm@67:         $t =~ s/#.*//;
rlm@67:         if ($t =~ /^\s*([^\s]+)\s*=\s*"(.*)"\s*$/) {
rlm@67:             my $c = $1;
rlm@67:             my $v = $2;
rlm@67:             $v =~ s/^["'](.*)["']$/$1/;         # Drop quotation marks
rlm@67:             $config{$c} = $v;
rlm@67:         }
rlm@67:         elsif ($t =~ /^\s*([^\s]+)\s*=\s*([^\s]+)\s*$/) {
rlm@67:             my $c = $1;
rlm@67:             my $v = $2;
rlm@67:             $config{$c} = $v;
rlm@67:         }
rlm@67:     }
rlm@67: }