use strict; use IO::Handle; use Getopt::Long; sub update_regs; sub print_regs; sub error; sub b2h; sub format_time; $SIG{__WARN__} = sub {die $_[0]}; # Initialize and defines all kinds of variables #---------------------------------------------- $| = 1; # autoflush my(@gl); # global register level per thread my(@tl); # trap level per thread my(@cwp); # current window pointer per thread my($logfile); # what file to read # global, FP, integer, asi registers my(@gregs, @fpregs, @regs, @asi_reg); my($pc, $inst); # program counter, instructions my(%diag_code, %label); # disassembled diag code and labels my($time, $spc, $thr, $proc, $reg, $val, $win, $instr, $tmp); my(@instr_list); # instruction list my($PROG) = ($0=~ m%([^/]+)$%); # regreport real name my(%miss); # additional info - less important my(%notintlb); # additional info - less important my($i, $k); my $version = '1.NN'; print "===============================================\n"; print "=== OpenSPARC T1 Vlog Version $version ===\n"; print "===============================================\n"; my @Options = qw( debug sas mom h help ccx l2 dram cycles sort! perf); my %opt = (); $opt{debug} = 0; $opt{sas} = 0; $opt{mom} = 0; $opt{h} = 0; $opt{help} = 0; $opt{ccx} = 0; $opt{l2} = 0; $opt{dram} = 0; $opt{cycles} = 0; $opt{sort} = 1; $opt{perf} = 0; GetOptions(\%opt, @Options) or die "Error in arguments!\n"; my($debug) = $opt{debug}; my($sas) = $opt{sas}; my($mom) = $opt{mom}; my($ccx) = $opt{ccx}; my($l2) = $opt{l2}; my($dram) = $opt{dram}; my($h) = $opt{h}; my($help) = $opt{help}; my($cycles) = $opt{cycles}; my($sorti) = $opt{sort}; my($perf) = $opt{perf}; if($h || $help) { print "===============================================\n"; print "=== Version $PROG ===\n"; print "Usage: vlog [logfilename|path_to_sim.log] [-debug -h -ccx -l2 -dram -cycles -[no]sort] [-perf] \n"; print "-ccx prints ccx related messages\n"; print "-l2 prints l2 related messages\n"; print "-dram prints dram related messages\n"; print "-h prints out this screen\n"; print "-debug is for script debug\n"; print " It will probably be obsoleted in the future \n"; print "-cycles prints the cycles and not the time \n"; print "-sort will sort sim.log according to time stamps first [default is on]\n"; print "-perf will print all kinds of performance data - I, D miss e.t.c.\n"; print "-mom Special mom sas.log file processing\n"; print "===============================================\n"; print "Examples:\n"; print "vlog -ccx -l2 -dram >! vlog.log\n"; print "vlog >! vlog.log\n"; print "vlog /sim.log >! vlog.log\n"; print "===============================================\n"; exit 0; } if($debug) { print "Debug mode on\n"; } if($sas) { print "sas mode\n"; } if($mom) { print "mom mode\n"; } if($sorti) { print "sort mode\n"; } if($perf) { print "perf mode\n"; } #========================================== # initialize register hashes. #========================================== for ($i = 0; $i < 32; $i++){ $tl[$i] = 5; $gl[$i] = 3; $cwp[$i] = 0; $asi_reg[$i] = "x"; for ($k = 0; $k < 32; $k++){ $gregs[$i][$k] = "x"; } $gregs[$i][0] = 0x0; $gregs[$i][8] = 0x0; $gregs[$i][16] = 0x0; $gregs[$i][24] = 0x0; for ($k = 0; $k < 64; $k++){ $fpregs[$i][$k] = "x"; } for ($k = 0; $k < 256; $k++){ $regs[$i][$k] = "x"; } } #========================================== # figure out logfile to read #========================================== my $logfilename = $mom ? "sas.log" : "sim.log"; if($#ARGV >= 0){ my @pathname = split '/' , $ARGV[0]; if($pathname[$#pathname] =~ /$logfilename/){ $#pathname--; } if(scalar(@pathname)){ chdir join '/', @pathname or die "cannot change dir\n"; print "changed dir to "; }else{ print "current dir is "; } my $temp = `pwd`; print "$temp\n"; } $logfile= (-f $logfilename) ? $logfilename : (-f "$logfilename.gz") ? "$logfilename.gz" : &error("Logfile $logfilename or $logfilename.gz not found"); print "\n===== Opening $logfile ======\n"; #------------------------------------- # log file in N1 is unsorted mess. # sort it unless soritng is disabled. # sort by forking a sorting process. #------------------------------------- if($sorti && !$mom){ my $pid = open(VCS, "-|"); unless (defined $pid){die "cannot fork $!\n"} if(!$pid){sorti();} } else{ if($logfile =~ /\.gz$/) { open(VCS, "gunzip -c $logfile |") || die "cannot open gunzip -c $logfile"; }else{ open(VCS, "< $logfile") || die "cannot open $logfile"; } } #============================================================ # get the labels inside the diag.s if((! -f "symbol.tbl") &&(-f "symbol.tbl.gz")){ system("gunzip symbol.tbl.gz"); } if(! -f "symbol.tbl") {print "no symbol.tbl\n"; exit 0;} open(SYM , "symbol.tbl") || die " BAD BAD symbol.tbl opening"; while(){ chop; s/\.\w+\.//g; my @symline = split /\s+/; if($#symline != 3){die "something is wrong with symbol.tbl\n";} my $pc = truncate_len($symline[1]); if(exists $label{$pc} and $label{$pc} eq 'main'){ next; } $label{$pc} = $symline[0]; print "label $label{$pc}, $pc\n" if($debug); } # disassemble the diag. #--------------------- my(@diag_exe_files) = `ls diag*.exe*`; if(!(@diag_exe_files)){ print "Warning: No diag.\*exe files!!!\n"; } else{ my($exe_file, $exe_file_gz); foreach $exe_file (@diag_exe_files){ chop $exe_file; $exe_file_gz = $exe_file . ".gz"; if(!(-e $exe_file) && (-e $exe_file_gz)){ system("gunzip $exe_file_gz"); } print "Will extract instruction info from: $exe_file \n"; open(DIS, "g_objdump -m sparc:v9B -d $exe_file | ") || die "BAD objdump"; while(){ s/^\s+//; if(/^([a-f\d]+):\s+.. .. .. ..\s+(.*)$/) { $pc = truncate_len($1); $inst = $2; $inst =~ s/\s*!.*$//; if($inst =~ /\((\d+)\)/){ # convert from decimal to hex. my $inst_left = $` . '(0x'; my $inst_cent = sprintf("%lx",$1); my $inst_right = ')' . $'; my $asi_str = asi_mnem($inst_cent); $inst = $inst_left . $inst_cent . ' ' . $asi_str . $inst_right; } $diag_code{$pc}= $inst; } } ### of while... close(DIS); } ### of foreach } # of if $| = 0; # autoflush #============================================================ my $found_ctime = 0; my $pll_freq = 0; my $cmp_clk_divider = 0; my($ctime) = 0; # the main loop: #--------------- while() { chop; s/-MATCH/-updated/g; if(/MISMATCH/) { print ;print "\n"; next;} elsif(/timeout/i) { print ;print "\n"; next;} elsif(/error/i) { print ;print "\n"; next;} elsif(/fail/i) { print ;print "\n"; next;} elsif(/wrong/i) { print ;print "\n"; next;} elsif(/good trap/i) { print ;print "\n"; next;} # some messing around needed to calculate the clock period. #---------------------------------------------------------- elsif(/cmp_clk period\s*=\s*(\d+)/){ $ctime = $1; print "cycle time is $ctime\n"; die "Found clock period of 0!\n" unless $ctime; $found_ctime = 1; } elsif(!$found_ctime && /cmp_clk divider\s*=\s*(\d+)/){ $cmp_clk_divider = $1; print "cmp_clk_divider is $cmp_clk_divider\n"; } elsif(!$found_ctime && /pll_clk frequency\s*=\s*(\d+)/i){ $pll_freq = $1; print "pll_freq time is $pll_freq\n"; } elsif(!$found_ctime && /Selected Core Clock Frequency\s(\d+)\s*MHz/){ if($pll_freq && $cmp_clk_divider){ my $pll_period; my %pll_periods = ( # HACK! These are periods hardcoded from cmp_clk.v. 2000 => 250, # Hopefully, we're using a version of hte model that has the 2200 => 227, # "cmp_period" defined, so we don't have to use this. 2333 => 214, 2334 => 214, 2400 => 208, 2550 => 196, 2600 => 192, 2800 => 179, default => 208, ); $pll_period = exists $pll_periods{$pll_freq} ? $pll_periods{$pll_freq} : $pll_periods{default}; $ctime = $pll_period * 2 * $cmp_clk_divider; print "Computed cycle_period from PLL ($pll_freq) and div ". "($cmp_clk_divider): $ctime\n"; } else { # Couldn't find anything but the frequency message. Use that. #------------------------------------------------------------ my $freq = $1; $ctime = int(1 / $freq * 1000000); print "Guessed at cycle_period from frequency ($freq): $ctime\n"; } $found_ctime = 1; } #============================= # example line: #567: [th02] < not in tlb > # this is not very important`. It was nice to the perofrmace # debuggers. #============================= elsif(/th([\dabcdef]+)\]\s+\.*not in tlb/i){ $spc = $1 / 4; $thr = $1 % 4; $val = truncate_len($2); $notintlb{$spc}{$thr}{$val} = 1; print "switch on tlb message $_\n" if($debug); } # this is VERY IMPORTANT - the register updates: elsif(/^(\d+):.*reg.updated\s*->\s*spc.(\d).\s*thread.(\d)/){ $time = format_time($1, $ctime, $cycles); $spc = $2; $thr = $3; $proc = 4 * $spc + $thr; if(/float_reg.updated.*reg#\((\w+)\).*val = (\w+)/){ $reg = "%" . $1; $val = $2; printf "%-10s: C%dT%d\t\tFPREG UPDATE <%s = %s>\n", $time, $spc, $thr, $reg, $val; update_regs; my @chars = split //, $val; if($#chars > 7){ if($reg =~ /%[df](\d+)/){ my $newreg = $1 + 1; $reg = "%f" . $newreg; $val = substr $val, 8; update_regs; } else{ die " $time: something is wrong with FP updates\n"; } } } elsif(/window.(\w+).*reg#\((\w+)\).*val = (\w+)/){ $win = $1; $reg = "%" . $2; $val = $3; printf "%-10s: C%dT%d\t\tREG UPDATE <%s = %s> in window %s\n", $time, $spc, $thr, $reg, $val, $win; update_regs; } elsif(/asi_reg.updated.*window.(\w+).*val = 0*(\w+)/){ $asi_reg[$proc] = $2; printf "%-10s: C%dT%d\t\tASI REG UPDATE in window %s\n", $time, $spc, $thr, $1; } elsif(/canrestore_reg.updated.*window.(\w+).*val = 0*(\w+)/){ printf "%-10s: C%dT%d\t\tCANRESTORE REG UPDATE in window %s\n", $time, $spc, $thr, $1; } elsif(/cansave_reg.updated.*window.(\w+).*val = 0*(\w+)/){ printf "%-10s: C%dT%d\t\tCANSAVE REG UPDATE in window %s\n", $time, $spc, $thr, $1; } elsif(/ccr_reg.updated.*window.(\w+).*val = 0*(\w+)/){ printf "%-10s: C%dT%d\t\tCCR REG UPDATE in window %s\n", $time, $spc, $thr, $1; } elsif(/cleanwin_reg.updated.*window.(\w+).*val = 0*(\w+)/){ printf "%-10s: C%dT%d\t\tCLEANWIN REG UPDATE in window %s\n", $time, $spc, $thr, $1; } elsif(/cwp_reg.updated.*window.(\w+).*val = (\w+)/){ printf "%-10s: C%dT%d\t\tCWP REG UPDATE in window %s\n", $time, $spc, $thr, $2, $1; $cwp[$proc] = $1; } elsif(/hpstate_reg.updated.*window.(\w+).*val = (\w+)/){ printf "%-10s: C%dT%d\t\tHPSTATE REG UPDATE in window %s\n", $time, $spc, $thr, $2, $1; } elsif(/htba_reg.updated.*window.(\w+).*val = (\w+)/){ printf "%-10s: C%dT%d\t\tHTBA REG UPDATE in window %s\n", $time, $spc, $thr, $2, $1; } elsif(/hstate2_reg.updated.*window.(\w+).*val = (\w+)/){ printf "%-10s: C%dT%d\t\tHSTATE2 REG UPDATE in window %s\n", $time, $spc, $thr, $2, $1; } elsif(/hstate3_reg.updated.*window.(\w+).*val = (\w+)/){ printf "%-10s: C%dT%d\t\tHSTATE3 REG UPDATE in window %s\n", $time, $spc, $thr, $2, $1; } elsif(/otherwin_reg.updated.*window.(\w+).*val = (\w+)/){ printf "%-10s: C%dT%d\t\tOTHERWIN REG UPDATE in window %s\n", $time, $spc, $thr, $2, $1; } elsif(/pil_reg.updated.*window.(\w+).*val = (\w+)/){ printf "%-10s: C%dT%d\t\tPIL REG UPDATE in window %s\n", $time, $spc, $thr, $2, $1; } elsif(/pstate_reg.updated.*window.(\w+).*val = (\w+)/){ printf "%-10s: C%dT%d\t\tPSTATE REG UPDATE in window %s\n", $time, $spc, $thr, $2, $1; } elsif(/tba_reg.updated.*window.(\w+).*val = (\w+)/){ printf "%-10s: C%dT%d\t\tTBA REG UPDATE in window %s\n", $time, $spc, $thr, $2, $1; } elsif(/tl_reg.updated.*window.(\w+).*val = (\w+)/){ $tl[$proc] = $2; printf "%-10s: C%dT%d\t\tTL REG UPDATE \n", $time, $spc, $thr, $tl[$proc]; } elsif(/gl_reg.updated.*window.(\w+).*val = 0*(\w+)/){ $gl[$proc] = $2; printf "%-10s: C%dT%d\t\tGL REG UPDATE \n", $time, $spc, $thr; } elsif(/tstate1_reg.updated.*window.(\w+).*val = (\w+)/){ printf "%-10s: C%dT%d\t\tTSTATE1 REG UPDATE in window %s\n", $time, $spc, $thr, $2, $1; } elsif(/tstate2_reg.updated.*window.(\w+).*val = (\w+)/){ printf "%-10s: C%dT%d\t\tTSTATE2 REG UPDATE in window %s\n", $time, $spc, $thr, $2, $1; } elsif(/ttype1_reg.updated.*window.(\w+).*val = (\w+)/){ printf "%-10s: C%dT%d\t\tTTYPE1 REG UPDATE in window %s\n", $time, $spc, $thr, $2, $1; } elsif(/ttype2_reg.updated.*window.(\w+).*val = (\w+)/){ printf "%-10s: C%dT%d\t\tTTYPE2 REG UPDATE in window %s\n", $time, $spc, $thr, $2, $1; } elsif(/wstate_reg.updated.*window.(\w+).*val = (\w+)/){ printf "%-10s: C%dT%d\t\tWSTATE REG UPDATE in window %s\n", $time, $spc, $thr, $2, $1; } elsif(/hstick_cmpr_reg.updated.*window.(\w+).*val = (\w+)/){ printf "%-10s: C%dT%d\t\tHSTICK_CMPR REG UPDATE in window %s\n", $time, $spc, $thr, $2, $1; } elsif(/stick_cmpr_reg.updated.*window.(\w+).*val = (\w+)/){ printf "%-10s: C%dT%d\t\tSTICK_CMPR REG UPDATE in window %s\n", $time, $spc, $thr, $2, $1; } elsif(/tick_cmpr_reg.updated.*window.(\w+).*val = (\w+)/){ printf "%-10s: C%dT%d\t\tTICK_CMPR REG UPDATE in window %s\n", $time, $spc, $thr, $2, $1; } elsif(/y_reg.updated.*window.(\w+).*val = (\w+)/){ printf "%-10s: C%dT%d\t\tY REG UPDATE in window %s\n", $time, $spc, $thr, $2, $1; } else{ print "$_ - an undetected register\n" if ($debug); } } #================================ # VERY IMPORTANT - PC extraction # the notintlb and miss info # is just nice to have. # everything else is crucial. #================================ elsif(!$sas && /info-perm/i){ if(/^.(\d+).info-perm\s+thread.([\dabcdef]+).\s+pc.(\w+)/i){ $time = format_time($1, $ctime, $cycles); $proc = hex ($2); $spc = int ($proc / 4); $thr = $proc % 4 ; $val = truncate_len($3); } else{ die "See this\n$_\nsomething is wrong with the PC extraction\n"; } $instr = $diag_code{$val}; if($label{$val}){ printf "%-10s: C%dT%d ======================================\n", $time, $spc, $thr; printf "%-10s: C%dT%d LABEL <%s>:\n", $time, $spc, $thr, $label{$val}; } if($instr){ my($notintlb, $imiss, $dmiss, $short_val); $val =~ /(\w{8})$/; $short_val = $1; if((exists $miss{I}{$spc}{$thr}{$short_val}) && ( $miss{I}{$spc}{$thr}{$short_val} == 1) && $perf){ $imiss = 'IMISS'; $miss{I}{$spc}{$thr}{$short_val} = 0; } else{ $imiss = ''; } if((exists $miss{D}{$spc}{$thr}{$short_val}) && ( $miss{D}{$spc}{$thr}{$short_val} == 1) && $perf){ $dmiss = 'DMISS'; $miss{D}{$spc}{$thr}{$short_val} = 0; } else{ $dmiss = ''; } if(exists $notintlb{$spc}{$thr}{$val} && $notintlb{$spc}{$thr}{$val} && $perf){ $notintlb = "not in tlb"; $notintlb{$spc}{$thr}{$val} = 0; print "switch off tlb message $_\n" if($debug); } else{ $notintlb = ''; } if($instr =~ /%asi/){ my $asi_str = asi_mnem($asi_reg[$proc]); $instr =~ s/%asi/%asi ($asi_reg[$proc] $asi_str)/; } printf "%-10s: C%dT%d v%-12s \t%-30s $notintlb $imiss $dmiss\n", $time, $spc, $thr, $val, $instr; $instr =~ s/[,\[\]]//g; if($instr =~ /%fp\W|%sp\W|%fp$|%sp$/){ $instr =~ s/%fp/%i6/g; $instr =~ s/%sp/%o6/g; } @instr_list = split /\s+/, $instr; print_regs; } else{ printf "%-10s: C%dT%d v%-12s \tWARNING probably illegal instruction - could not translate\n", $time, $spc, $thr, $val; } } #==================================== # also very important - SAS only runs #==================================== elsif($sas && /^\((\d+)\):sas>\s+\d+:\s+\[th([\dabcdef]+)\]\s+\s+\s(.*)/){ $time = format_time($1, $ctime, $cycles); $proc = hex ($2); $spc = $proc / 4; $thr = $2 % 4; $val = truncate_len($3); my($pa) = $4; $instr = $5; if(exists $label{$val}){ printf "%-10s: C%dT%d ======================================\n", $time, $spc, $thr; printf "%-10s: C%dT%d LABEL <%s>:\n", $time, $spc, $thr, $label{$val}; } printf "%-10s: C%dT%d v%-12s \t%-30s \(pa=%-12s\)\n", $time, $spc, $thr, $val, $instr, $pa; $instr =~ s/[,\[\]]//g; @instr_list = split /\s+/, $instr; print_regs; } # example line: #@1101: 0000.pc=00000000000400ac inst[lstore] in switch stage /wo speculation, latency=0 elsif($mom && /^\@(\d+):\s+(\d+)\.pc=(\w+)\s+inst/){ $time = format_time($1, $ctime, $cycles); my $proc = $2 + 0; $spc = $proc / 4; $thr = $proc % 4; $val = truncate_len($3); if(exists $diag_code{$val}){ $instr = $diag_code{$val}; } else{ $instr = "Instruction not found"; } if($label{$val}){ printf "%-10s: C%dT%d ======================================\n", $time, $spc, $thr; printf "%-10s: C%dT%d LABEL <%s>:\n", $time, $spc, $thr, $label{$val}; } printf "%-10s: C%dT%d v%-12s \t%-30s\n", $time, $spc, $thr, $val, $instr; } #============================= # print CCX info. #============================= elsif($ccx && (/^(\d+):Info cpu.(\d). cpx.*->\s*(\w+)/)) { $time = format_time($1, $ctime, $cycles); $spc = $2; my($rawpkt) = $3; my($pkt) = process_cpx_pkt($rawpkt); print "debug cpx: $_\n" if($debug); printf "%-10s: C${spc}T$pkt\n", $time; next; } elsif(/^(\d+):Info cpu.(\d). pcx.*->\s*(\w+)/) { $time = format_time($1, $ctime, $cycles); $spc= $2; my($rawpkt) = $3; my($pkt) = process_pcx_pkt($rawpkt); print "debug pcx: $_\n" if($debug); if($ccx){ printf "%-10s: C$spc$pkt\n", $time; } if($pkt =~ /^T(\d).*([id])fill_req.*addr\s+\w*(\w{8})\s/i){ my $thr = $1; my $iord = $2; my $addr = $3; $miss{$iord}{$spc}{$thr}{$addr} = 1; print " setting $iord $spc $thr $addr miss \n" if($debug); } next; } # l2 bank info #============== elsif($l2 && /^(\d+):\s+(L2 bank.*)/) { $time = format_time($1, $ctime, $cycles); my $rawpkt = $2; if($rawpkt =~ /core\s+(\d+),\s+thread\s+(\d+)/){ $spc = $1; $thr = $2; printf "%-10s: C${spc}T${thr}\t$rawpkt\n", $time; } else{ printf "%-10s: \t$rawpkt\n", $time; } next; } # dram info elsif($dram && /^(\d+):\s*(DRAM.*)/) { $time = format_time($1, $ctime, $cycles); my $rawpkt = $2; printf "%-10s: $rawpkt\n", $time; next; } elsif($dram && /^(\d+):\s*(L2_DRAM.*)/) { $time = format_time($1, $ctime, $cycles); my $rawpkt = $2; printf "%-10s: $rawpkt\n", $time; next; } elsif(/bad/i) { print "$_\n"; } } # while close(VCS); print "\nBye\n"; #========================== sub error { my($msg)= @_; STDOUT->autoflush(1); STDERR->autoflush(1); print STDERR "ERR: $msg!\n"; exit(1); } #========================== # updates the register values # this is per thread. # I am modeling all registers per thread as one big flat # register file, index by register window number * N + register number # where N = 8 for globals and 16 for others #========================== sub update_regs { my($reg_num); if($reg =~ /%r(\d+)/){ if($1 < 8){ # global reg. $reg_num = 8 *$win + $1; $gregs[$proc][$reg_num] = $val; print "C${spc}T${thr} debug updating global $reg to $gregs[$proc][$1] reg num = $reg_num\n" if($debug); } else{ # not a global reg. $reg_num = $win*16 + 31 - $1; $regs[$proc][$reg_num] = $val; print "C${spc}T${thr} debug updating $reg to $regs[$proc][$reg_num] reg_num = $reg_num\n" if($debug); } } elsif($reg =~ /%g(\d+)/){ # global reg $reg_num = 8 * $win + $1; $gregs[$proc][$reg_num] = $val; print "C${spc}T${thr} debug updating $reg to $gregs[$proc][$reg_num] reg_num: $reg_num\n" if($debug); } elsif($reg =~ /%i(\d+)/){ # input reg $reg_num = $win*16 + $1; $regs[$proc][$reg_num] = $val; print "C${spc}T${thr} debug updating $reg to $regs[$proc][$reg_num] reg_num: $reg_num\n" if($debug); } elsif($reg =~ /%l(\d+)/){ # local reg $reg_num = $win*16 + 8 + $1; $regs[$proc][$reg_num] = $val; print "C${spc}T${thr} debug updating $reg to $regs[$proc][$reg_num] reg_num: $reg_num\n" if($debug); } elsif($reg =~ /%o(\d+)/){ # output reg $reg_num = $win*16 + 16 + $1; $regs[$proc][$reg_num] = $val; print "C${spc}T${thr} debug updating $reg to $regs[$proc][$reg_num] reg_num: $reg_num\n" if($debug); } elsif($reg =~ /%f(\d+)/){ # FP reg. $reg_num = $1; $fpregs[$proc][$reg_num] = $val; print "C${spc}T${thr} debug updating $reg to $fpregs[$proc][$reg_num] reg_num: $reg_num\n" if($debug); } elsif($reg =~ /%d(\d+)/){ # should not come here. print "%d stuff, man, don't know what to do yet.\n"; exit 1; } } #----------------------------------------------------------------- # prints the relevant registers for the instruction/PC which found it. sub print_regs{ my($needs_print) = 0; my($e, $reg_num); if($instr =~ /%(r|g|l|i|o|f|d)\d+/){ $needs_print = 1; printf " C%dT%d\t\told reg values: ", $spc, $thr; } if($needs_print){ foreach $e (@instr_list){ if($e =~ /%r(\d+)/){ my $rnumh = $1; if($rnumh < 8) { $e =~ s/%r/%g/; } elsif(($rnumh >= 8) && ($rnumh < 16)) { $e =~ s/%r/%o/; } elsif(($rnumh >= 16) && ($rnumh < 24)) { $e =~ s/%r/%l/; } elsif(($rnumh >= 24) && ($rnumh < 32)) { $e =~ s/%r/%i/; } else { die "something is wrong with register number $rnumh\n";} } if($e =~ /%g(\d+)/){ $reg_num = $gl[$proc] * 8 + $1; printf "%s= %s ", $e, $gregs[$proc][$reg_num]; } elsif($e =~ /%i(\d+)/){ $reg_num = $cwp[$proc]*16 + $1; printf "%s= %s ", $e, $regs[$proc][$reg_num]; } elsif($e =~ /%l(\d+)/){ $reg_num = $cwp[$proc]*16 + 8 + $1; printf "%s= %s ", $e, $regs[$proc][$reg_num]; } elsif($e =~ /%o(\d+)/){ $reg_num = $cwp[$proc]*16 + 16 + $1; printf "%s= %s ", $e, $regs[$proc][$reg_num]; } elsif($e =~ /%f(\d+)/){ $reg_num = $1; printf "%s= %s ", $e, $fpregs[$proc][$reg_num]; } elsif($e =~ /%d(\d+)/){ print "%d stuff, man, don't know what to do yet.\n"; exit 1; } } print "\n"; } } #==================================================================== # makes sense of the PCX packet #==================================================================== sub process_pcx_pkt{ my %ReqTyps = ( '00000' => 'Dfill_Req', '10000' => 'Ifill_Req', '00001' => 'ST', '00111' => 'STQ(1)', '00010' => 'CAS(1)', '00011' => 'CAS(2)', '00110' => 'SWP_Ldstb', '00100' => 'Stream_loads', '00101' => 'Stream_store', '01001' => 'Int', '01010' => 'FP(1)', '01011' => 'FP(2)', '01100' => 'blank', '01101' => 'Fwd_req', '01110' => 'Fwd_reply' ); my $rawpkt = shift @_; my $vld = substr $rawpkt, 0, 1; my $rawtype = substr $rawpkt, 1, 5; my $nc = substr $rawpkt, 6, 1; my $cpuid = substr $rawpkt, 7, 3; my $thrid = substr $rawpkt, 10, 2; my $inv = substr $rawpkt, 12, 1; my $pf = substr $rawpkt, 13, 1; my $binit = substr $rawpkt, 14, 1; my $repl = substr $rawpkt, 15, 2; my $size = substr $rawpkt, 17, 3; my $rawaddr = substr $rawpkt, 20, 40; my $rawdata = substr $rawpkt, 60, 64; my $addr = b2h($rawaddr); my $data = b2h($rawdata); my $type = "illegal"; $type = $ReqTyps{$rawtype} if (defined $ReqTyps{$rawtype}); my $reqid = 'TX'; if ($thrid eq '00'){ $reqid = 'T0'; } elsif($thrid eq '01'){ $reqid = 'T1'; } elsif($thrid eq '10'){ $reqid = 'T2'; } elsif($thrid eq '11'){ $reqid = 'T3'; } my $pkt = "$reqid\tPCX: $type nc $nc cpuid $cpuid thrid $thrid inv $inv pf $pf binit $binit repl $repl size $size addr $addr data $data"; return $pkt; } #==================================================================== # makes sense of the CPX packet #==================================================================== sub process_cpx_pkt{ my %RtnTyps = ( '0000' => 'Dfill', '0001' => 'Ifill', '0010' => 'Strm_Load', '0011' => 'Evict_Inv', '0100' => 'Store_Ack', '0101' => 'Flush', '0110' => 'Strm_Store_Ack', '0111' => 'Int', '1000' => 'FP', '1001' => 'blank', '1010' => 'Fwd_req', '1011' => 'Fwd_Reply', '1100' => 'Err', ); my $rawpkt = shift @_; my $vld = substr $rawpkt, 0, 1; my $rawtype = substr $rawpkt, 1, 4; my $l2miss = substr $rawpkt, 5, 1; my $err = substr $rawpkt, 6, 2; my $nc = substr $rawpkt, 8, 1; my $shared = substr $rawpkt, 9, 7; my $thrbits = substr $rawpkt, 9, 2; my $rsvd = substr $rawpkt, 16, 1; my $rawdata = substr $rawpkt, 17, 128; my $type = "illegal"; $type = $RtnTyps{$rawtype} if (defined $RtnTyps{$rawtype}); my $data = b2h($rawdata); my $thread = 'x'; if($thrbits eq '00'){ $thread = '0'; } elsif($thrbits eq '01'){ $thread = '1'; } elsif($thrbits eq '10'){ $thread = '2'; } elsif($thrbits eq '11'){ $thread = '3'; } my $pkt = "$thread\t\tCPX: $type l2miss $l2miss err $err nc $nc shared $shared rsvd $rsvd data $data"; return $pkt; } #==================================================================== # binary to hex. #==================================================================== sub b2h{ my $in = shift @_; if($in =~ /x/i){ return "xxxxxxxx"; } my @inlist = split //, $in; my ($res, $i, $digit, @num, $strres); while (@inlist){ $res = 0; $i = shift @inlist; $res = 2 * $res + $i; $i = shift @inlist; $res = 2 * $res + $i; $i = shift @inlist; $res = 2 * $res + $i; $i = shift @inlist; $res = 2 * $res + $i; $digit = sprintf "%lx", $res; push @num, $digit; } $strres = join '', @num; return $strres; } #==================================================================== #truncate PC length from the MSB zeroes (or FF-s) #==================================================================== sub truncate_len{ my $pc = $_[0]; my($length) = length($pc); my($va_length) = 12; if($length>$va_length){ $pc =~ /(.{$va_length})$/; $pc = $1; } elsif($pc=~ /^f/i) { $pc= 'f' x ($va_length-$length) . $pc; } else { $pc= '0' x ($va_length-$length) . $pc; } print "pc= $pc\n" if($debug); $pc; } #==================================================================== #==================================================================== # sorti: sorts the input file in a special way. # very simple - the RTL messages are ordered and the SAS # messages are ordered, so all we need is merge sorting. # Notes: #------- # disambiguate lines on the same timetick. - register updates, # and info-perm messages #==================================================================== sub sorti{ if($logfile =~ /\.gz$/) { open(VCSRAW1, "gunzip -c $logfile | egrep 'sas|MAT|trig' | ") || die "cannot open gunzip -c $logfile"; open(VCSRAW2, "gunzip -c $logfile | egrep -v 'sas|MAT|trig' | ") || die "cannot open gunzip -c $logfile"; }else{ open(VCSRAW1, "egrep 'sas|MAT|trig' $logfile | ") || die "cannot open $logfile"; open(VCSRAW2, "egrep -v 'sas|MAT|trig' $logfile | ") || die "cannot open $logfile"; } my $curtime1 = 0; my $curtime2 = 0; my $gets1 = 1; my $gets2 = 1; my $s1line; my $s2line; while(1) { # get the vcsraw1 #---------------- if($gets1){ $s1line = ; if(!$s1line){ if($s2line){ print $s2line; } while(){ print; } last; } else{ if($s1line =~ /^(\d+)\s*:.*(gl|tl|pstate)_reg-M/) { $curtime1 = $1 + 3; }elsif($s1line =~ /^(\d+)\s*:.*reg-M/) { $curtime1 = $1 + 10; }elsif($s1line =~ /^(\d+)\s*:/) { $curtime1 = $1 + 0; }elsif($s1line =~ /^\((\d+)\)\s*:/) { $curtime1 = $1 + 0; } } } # get the vcsraw2 #---------------- if($gets2){ $s2line = ; if(!$s2line){ if($s1line){ print $s1line; } while(){ print; } last; } else{ if($s2line =~ /^\((\d+)\)Info-p/) { $curtime2 = $1 + 5; }elsif($s2line =~ /^(\d+)\s*:/) { $curtime2 = $1 + 0; }elsif($s2line =~ /^\((\d+)\)\s*:/) { $curtime2 = $1 + 0; } } } if($curtime1 < $curtime2){ print $s1line; $gets2 = 0; $gets1 = 1; } else{ print $s2line; $gets2 = 1; $gets1 = 0; } } close(VCSRAW1); close(VCSRAW2); exit; } #======================================================= # print ASI mnemonics to make debugging more pleasurable. #======================================================= sub asi_mnem{ my %asis = ( "4", "ASI_NUCLEUS", "c", "ASI_NUCLEUS_LITTLE", "10", "ASI_AS_IF_USER_PRIMARY", "11", "ASI_AS_IF_USER_SECONDARY", "14", "ASI_REAL_MEM", "15", "ASI_REAL_IO", "16", "ASI_BLOCK_AS_IF_USER_PRIMARY", "17", "ASI_BLOCK_AS_IF_USER_SECONDARY", "18", "ASI_AS_IF_USER_PRIMARY_LITTLE", "19", "ASI_AS_IF_USER_SECONDARY_LITTLE", "1c", "ASI_REAL_MEM_LITTLE", "1d", "ASI_REAL_IO_LITTLE", "1e", "ASI_BLOCK_AS_IF_USER_P_LITTLE", "1f", "ASI_BLOCK_AS_IF_USER_S_LITTLE", "20", "ASI_SCRATCHPAD", "21", "ASI_MMU", "22", "ASI_AS_IF_USER_BLK_INIT_ST_QUAD_LDD_P", "23", "ASI_AS_IF_USER_BLK_INIT_ST_QUAD_LDD_S", "24", "ASI_QUAD_LDD", "25", "ASI_QUEUE", "26", "ASI_QUAD_LDD_REAL", "27", "ASI_NUCLEUS_BLK_INIT_ST_QUAD_LDD", "2a", "ASI_AS_IF_USER_BLK_INIT_ST_QUAD_LDD_P_LITTLE", "2b", "ASI_AS_IF_USER_BLK_INIT_ST_QUAD_LDD_S_LITTLE", "2c", "ASI_QUAD_LDD_LITTLE", "2e", "ASI_QUAD_LDD_REAL_LITTLE", "2f", "ASI_NUCLEUS_BLK_INIT_ST_QUAD_LDD_P_LITTLE", "30", "UNDEFINED", "31", "ASI_DMMU_CTXT_ZERO_TSB_BASE_PS0", "32", "ASI_DMMU_CTXT_ZERO_TSB_BASE_PS1", "33", "ASI_DMMU_CTXT_ZERO_CONFIG", "35", "ASI_IMMU_CTXT_ZERO_TSB_BASE_PS0", "36", "ASI_IMMU_CTXT_ZERO_TSB_BASE_PS1", "37", "ASI_IMMU_CTXT_ZERO_CONFIG", "38", "ASI_DTSB_TAG_TARGET", "39", "ASI_DMMU_CTXT_NONZERO_TSB_BASE_PS0", "3a", "ASI_DMMU_CTXT_NONZERO_TSB_BASE_PS1", "3b", "ASI_DMMU_CTXT_NONZERO_CONFIG", "3d", "ASI_IMMU_CTXT_NONZERO_TSB_BASE_PS0", "3e", "ASI_IMMU_CTXT_NONZERO_TSB_BASE_PS1", "3f", "ASI_IMMU_CTXT_NONZERO_CONFIG", "40", "ASI_STREAM_MA", "42", "ASI_SPARC_BIST_CONTROL_OR_INST_MASK_REG_OR_LSU_DIAG_REG", "43", "ASI_ERROR_INJECT_REG", "44", "ASI_STM_CTL_REG", "45", "ASI_LSU_CTL_REG", "46", "ASI_DCACHE_DATA", "47", "ASI_DCACHE_TAG", "48", "ASI_INTR_DISPATCH_STATUS_OBSOLETE", "49", "ASI_INTR_RECEIVE_OBSOLETE", "4a", "ASI_OBSOLETE", "4b", "ASI_SPARC_ERROR_EN_REG", "4c", "ASI_SPARC_ERROR_STAT_REG", "4d", "ASI_SPARC_ERROR_ADDR_REG", "4e", "ASI_ECACHE_TAG_DATA", "4f", "ASI_HYP_SCRATCHPAD", "50", "ASI_IMMU", "51", "ASI_IMMU_TSB_8KB_PTR_REG", "52", "ASI_IMMU_TSB_64KB_PTR_REG", "54", "ASI_ITLB_DATA_IN_REG", "55", "ASI_ITLB_DATA_ACCESS_REG", "56", "ASI_ITLB_TAG_READ_REG", "57", "ASI_IMMU_DEMAP", "58", "ASI_DMMU", "59", "ASI_DMMU_TSB_8KB_PTR_REG", "5a", "ASI_DMMU_TSB_64KB_PTR_REG", "5b", "ASI_DMMU_TSB_DIRECT_PTR_REG", "5c", "ASI_DTLB_DATA_IN_REG", "5d", "ASI_DTLB_DATA_ACCESS_REG", "5e", "ASI_DTLB_TAG_READ_REG", "5f", "ASI_DMMU_DEMAP", "60", "ASI_TLB_INVALIDATE_ALL", "66", "ASI_ICACHE_INSTR", "67", "ASI_ICACHE_TAG", "72", "ASI_SWVR_INTR_RECEIVE", "73", "ASI_SWVR_UDB_INTR_W", "74", "ASI_SWVR_UDB_INTR_R", "80", "ASI_PRIMARY", "81", "ASI_SECONDARY", "82", "ASI_PRIMARY_NO_FAULT", "83", "ASI_SECONDARY_NO_FAULT", "88", "ASI_PRIMARY_LITTLE", "89", "ASI_SECONDARY_LITTLE", "8a", "ASI_PRIMARY_NOFAULT_LITTLE", "8b", "ASI_SECONDARY_NOFAULT_LITTLE", "e2", "ASI_BLK_INIT_ST_QUAD_LDD_P", "e3", "ASI_BLK_INIT_ST_QUAD_LDD_S", "ea", "ASI_BLK_INIT_ST_QUAD_LDD_P_LITTLE", "eb", "ASI_BLK_INIT_ST_QUAD_LDD_S_LITTLE", "f0", "ASI_BLK_P", "f1", "ASI_BLK_S", "f8", "ASI_BLK_P_LITTLE", "f9", "ASI_BLK_S_LITTLE" ); my $mykey = $_[0]; if(exists $asis{$mykey}){ return $asis{$mykey}; } else { return ""; } } #============================================ # print cycles or timeticks. #============================================ sub format_time { my $time = $_[0]; my $ctime = $_[1]; my $cycles = $_[2]; if($cycles){ if($ctime){ $time = int ($time /$ctime); $time = 'c' . $time; } else { die "something is wrong with ctime\n" } } $time; } #============================================