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