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=\"\"]\n"; rlm@67: print STDERR " Prepend prefix to HAsim's --funcp argument\n"; rlm@67: print STDERR " [--funcp-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=] Generate VCD dump for wave viewer (e.g. gtkwave)\n"; rlm@67: print STDERR " [--vcdcycles=] 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 () { 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: }