diff --git a/util/amdtools/README b/util/amdtools/README new file mode 100644 index 0000000000..a1601fe8c7 --- /dev/null +++ b/util/amdtools/README @@ -0,0 +1,32 @@ + + +This is a set of tools to compare (extended) K8 memory settings. + +Before you can use them, you need to massage the relevant BKDG sections into +useable data. Here's how. + +First, you need to acquire a copy of the K8 BKDG. Go here: + + Rev F: http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/32559.pdf + +Then make sure pdftotext is installed (it's in the poppler-utils package on Debian/Ubuntu). + +Now run the bkdg through pdftotext: + + pdftotext -layout 32559.pdf 32559.txt + +Now extract sections 4.5.15 - 4.5.19 from the file, and save it separately, say as bkdg-raw.data. + +Finally run the txt file through the parse-bkdg.pl script like so: + + parse-bkdg.pl < bkdg-raw.data > bkdg.data + +Now we have the bkdg.data file that is used by the other scripts. + +If you want to test the scripts without doing all this work, you can use some +sample input files from the 'example_input/' directory. + +-- +Ward Vandewege, 2009-10-28. +ward@jhvc.com + diff --git a/util/amdtools/example_input/coreboot-48G-667MHz-memsettings b/util/amdtools/example_input/coreboot-48G-667MHz-memsettings new file mode 100644 index 0000000000..812e7db6ea --- /dev/null +++ b/util/amdtools/example_input/coreboot-48G-667MHz-memsettings @@ -0,0 +1,96 @@ +0:18.2 98.l: 80000000 +0:18.2 9C.l: 00111222 +0:18.2 98.l: 80000001 +0:18.2 9C.l: 16171715 +0:18.2 98.l: 80000002 +0:18.2 9C.l: 1716131a +0:18.2 98.l: 80000003 +0:18.2 9C.l: 00000019 +0:18.2 98.l: 80000004 +0:18.2 9C.l: 002f0000 +0:18.2 98.l: 80000005 +0:18.2 9C.l: 18191918 +0:18.2 98.l: 80000006 +0:18.2 9C.l: 16161917 +0:18.2 98.l: 80000007 +0:18.2 9C.l: 00000017 +0:18.2 98.l: 80000020 +0:18.2 9C.l: 00111222 +0:18.2 98.l: 80000021 +0:18.2 9C.l: 00000000 +0:18.2 98.l: 80000022 +0:18.2 9C.l: 00000000 +0:18.2 98.l: 80000023 +0:18.2 9C.l: 00000000 +0:18.2 98.l: 80000024 +0:18.2 9C.l: 00000000 +0:18.2 98.l: 80000025 +0:18.2 9C.l: 2f2f2f2f +0:18.2 98.l: 80000026 +0:18.2 9C.l: 2f2f2f2f +0:18.2 98.l: 80000027 +0:18.2 9C.l: 00000000 +0:18.2 98.l: 80000010 +0:18.2 9C.l: 00000000 +0:18.2 98.l: 80000013 +0:18.2 9C.l: 00000000 +0:18.2 98.l: 80000016 +0:18.2 9C.l: 0000003f +0:18.2 98.l: 80000019 +0:18.2 9C.l: 00000046 +0:18.2 98.l: 80000030 +0:18.2 9C.l: 00000000 +0:18.2 98.l: 80000033 +0:18.2 9C.l: 00000000 +0:18.2 98.l: 80000036 +0:18.2 9C.l: 00000053 +0:18.2 98.l: 80000039 +0:18.2 9C.l: 00000053 +0:19.2 98.l: 80000000 +0:19.2 9C.l: 00111222 +0:19.2 98.l: 80000001 +0:19.2 9C.l: 15151515 +0:19.2 98.l: 80000002 +0:19.2 9C.l: 15151515 +0:19.2 98.l: 80000003 +0:19.2 9C.l: 00000015 +0:19.2 98.l: 80000004 +0:19.2 9C.l: 002f0000 +0:19.2 98.l: 80000005 +0:19.2 9C.l: 19181918 +0:19.2 98.l: 80000006 +0:19.2 9C.l: 191a1817 +0:19.2 98.l: 80000007 +0:19.2 9C.l: 00000017 +0:19.2 98.l: 80000020 +0:19.2 9C.l: 00111222 +0:19.2 98.l: 80000021 +0:19.2 9C.l: 00000000 +0:19.2 98.l: 80000022 +0:19.2 9C.l: 00000000 +0:19.2 98.l: 80000023 +0:19.2 9C.l: 00000000 +0:19.2 98.l: 80000024 +0:19.2 9C.l: 00000000 +0:19.2 98.l: 80000025 +0:19.2 9C.l: 2f2f2f2f +0:19.2 98.l: 80000026 +0:19.2 9C.l: 2f2f2f2f +0:19.2 98.l: 80000027 +0:19.2 9C.l: 00000000 +0:19.2 98.l: 80000010 +0:19.2 9C.l: 00000000 +0:19.2 98.l: 80000013 +0:19.2 9C.l: 00000000 +0:19.2 98.l: 80000016 +0:19.2 9C.l: 0000003b +0:19.2 98.l: 80000019 +0:19.2 9C.l: 00000047 +0:19.2 98.l: 80000030 +0:19.2 9C.l: 00000000 +0:19.2 98.l: 80000033 +0:19.2 9C.l: 00000000 +0:19.2 98.l: 80000036 +0:19.2 9C.l: 00000053 +0:19.2 98.l: 80000039 +0:19.2 9C.l: 00000053 diff --git a/util/amdtools/example_input/coreboot-48G-667MHz-memsettings-20090909h b/util/amdtools/example_input/coreboot-48G-667MHz-memsettings-20090909h new file mode 100644 index 0000000000..b47858d432 --- /dev/null +++ b/util/amdtools/example_input/coreboot-48G-667MHz-memsettings-20090909h @@ -0,0 +1,96 @@ +0:18.2 98.l: 80000000 +0:18.2 9C.l: 10111222 +0:18.2 98.l: 80000001 +0:18.2 9C.l: 17171716 +0:18.2 98.l: 80000002 +0:18.2 9C.l: 17161619 +0:18.2 98.l: 80000003 +0:18.2 9C.l: 00000018 +0:18.2 98.l: 80000004 +0:18.2 9C.l: 002f2f00 +0:18.2 98.l: 80000005 +0:18.2 9C.l: 17171716 +0:18.2 98.l: 80000006 +0:18.2 9C.l: 16171717 +0:18.2 98.l: 80000007 +0:18.2 9C.l: 00000017 +0:18.2 98.l: 80000020 +0:18.2 9C.l: 10111222 +0:18.2 98.l: 80000021 +0:18.2 9C.l: 15151515 +0:18.2 98.l: 80000022 +0:18.2 9C.l: 14161514 +0:18.2 98.l: 80000023 +0:18.2 9C.l: 00000014 +0:18.2 98.l: 80000024 +0:18.2 9C.l: 002f2f00 +0:18.2 98.l: 80000025 +0:18.2 9C.l: 1817171a +0:18.2 98.l: 80000026 +0:18.2 9C.l: 1a191719 +0:18.2 98.l: 80000027 +0:18.2 9C.l: 00000018 +0:18.2 98.l: 80000010 +0:18.2 9C.l: 00000000 +0:18.2 98.l: 80000013 +0:18.2 9C.l: 00000033 +0:18.2 98.l: 80000016 +0:18.2 9C.l: 00000065 +0:18.2 98.l: 80000019 +0:18.2 9C.l: 00000026 +0:18.2 98.l: 80000030 +0:18.2 9C.l: 00000000 +0:18.2 98.l: 80000033 +0:18.2 9C.l: 00000033 +0:18.2 98.l: 80000036 +0:18.2 9C.l: 00000065 +0:18.2 98.l: 80000039 +0:18.2 9C.l: 00000024 +0:19.2 98.l: 80000000 +0:19.2 9C.l: 10111222 +0:19.2 98.l: 80000001 +0:19.2 9C.l: 17171717 +0:19.2 98.l: 80000002 +0:19.2 9C.l: 17161617 +0:19.2 98.l: 80000003 +0:19.2 9C.l: 00000016 +0:19.2 98.l: 80000004 +0:19.2 9C.l: 002f2f00 +0:19.2 98.l: 80000005 +0:19.2 9C.l: 17171717 +0:19.2 98.l: 80000006 +0:19.2 9C.l: 17171617 +0:19.2 98.l: 80000007 +0:19.2 9C.l: 00000016 +0:19.2 98.l: 80000020 +0:19.2 9C.l: 10111222 +0:19.2 98.l: 80000021 +0:19.2 9C.l: 15151514 +0:19.2 98.l: 80000022 +0:19.2 9C.l: 14141514 +0:19.2 98.l: 80000023 +0:19.2 9C.l: 00000014 +0:19.2 98.l: 80000024 +0:19.2 9C.l: 002f2f00 +0:19.2 98.l: 80000025 +0:19.2 9C.l: 1717191a +0:19.2 98.l: 80000026 +0:19.2 9C.l: 1a1a1919 +0:19.2 98.l: 80000027 +0:19.2 9C.l: 00000019 +0:19.2 98.l: 80000010 +0:19.2 9C.l: 00000000 +0:19.2 98.l: 80000013 +0:19.2 9C.l: 00000031 +0:19.2 98.l: 80000016 +0:19.2 9C.l: 0000003a +0:19.2 98.l: 80000019 +0:19.2 9C.l: 00000046 +0:19.2 98.l: 80000030 +0:19.2 9C.l: 00000000 +0:19.2 98.l: 80000033 +0:19.2 9C.l: 00000032 +0:19.2 98.l: 80000036 +0:19.2 9C.l: 0000003a +0:19.2 98.l: 80000039 +0:19.2 9C.l: 00000043 diff --git a/util/amdtools/example_input/lspci-cb-48G-667MHz-18.2-20090909e b/util/amdtools/example_input/lspci-cb-48G-667MHz-18.2-20090909e new file mode 100644 index 0000000000..a415e7680d --- /dev/null +++ b/util/amdtools/example_input/lspci-cb-48G-667MHz-18.2-20090909e @@ -0,0 +1,20 @@ +00:18.2 Host bridge: Advanced Micro Devices [AMD] K8 [Athlon64/Opteron] DRAM Controller + Control: I/O- Mem- BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx- + Status: Cap- 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- SERR- TAbort- SERR- ) { + chomp; + $device = $1 if (/^([a-f0-9]+:[a-f0-9]+\.[a-f0-9]+) /i); + next if (!(/^([a-f0-9]{2}): ([[a-f0-9 ]+)$/i)); + # Line format + # 00: 22 10 02 11 00 00 00 00 00 00 00 06 00 00 80 00 +#print STDERR hex($1) . " ($1): $2\n"; + my $regoffset = hex($1); + my @values = split(/ /,$2); + for (my $i=0;$i<=$#values;$i++) { + $register = sprintf("%02x",$regoffset+$i); + my $packed = pack("H*",$values[$i]); # Pack our number so we can easily represent it in binary + $data{$device} = {} if (!defined($data{$device})); + $data{$device}{$register} = {} if (!defined($data{$device}{$register})); + $data{$device}{$register}{$filename} = $packed; +#print STDERR "$device -> $register -> ($filename) setting to $values[$i]\n"; + } + } + return %data; +} + +sub parse_file_old { + my $register = ''; + my $devreg = ''; + my $filename = shift; + my %data = @_; + open(TMP, $filename) || die "Could not open $filename: $!\n"; + while () { + chomp; + # Line format - pairs of lines: + # 0:18.2 98.l: 80000000 + # 0:18.2 9C.l: 10111222 + # First field is pci device. Second field is register offset (hex) + # where third field value (in hex) was read from. + my @tmp = split(/ /); + $tmp[1] =~ s/:$//; # strip optional trailing colon on second field + + my $device = $tmp[0]; + my $packed = pack("H*",$tmp[2]); # Pack our number so we can easily represent it in binary + my $binrep = unpack("B*", $packed); # Binary string representation + + if ($tmp[1] eq '98.l') { + $register = ($tmp[2] =~ /(..)$/)[0]; # last 2 digits are (hex) of what we wrote to the register, if second field is 98.l + $devreg = "$device $register"; + if ("$binrep" =~ /^1/) { + # bit 31 *must* be 1 if readout is to be correct + print "$tmp[0] - $register
\n" if ($DEBUG); + } else { + print "ERROR: we read too fast: $tmp[2] does not have bit 31 set ($binrep)\n"; + exit; + } + } else { + # last field is register value (hex) + print "$tmp[2]h ($binrep)
\n" if ($DEBUG); + $data{$devreg} = {} if (!defined($data{$devreg})); + $data{$devreg}{$filename} = $packed; + } + } + return %data; +} + +sub interpret_differences { + my $dev = shift; + my $reg = shift; + $reg = sprintf("%02s",$reg); + my $tag1 = shift; + my $val1 = shift; + my $tag2 = shift; + my $val2 = shift; + my $retval = ''; + my $retval2 = ''; + + # XOR values together - the positions with 1 after the XOR are the ones with the differences + my $xor = $val1 ^ $val2; + + my @val1 = split(//,unpack("B*",$val1)); + my @val2 = split(//,unpack("B*",$val2)); + my @xor = split(//,unpack("B*",$xor)); + + my %changed; + + my $decregbase = hex($reg) - (hex($reg) % 4); + + if (!exists($printed{$decregbase})) { + print "$dev $reg\n"; + print STDERR "$dev $reg\n"; + my $tmp = sprintf("%44s: %02x", $tag1, $decregbase) . ": "; + $tmp .= unpack("H*",$data{$dev}{sprintf("%02x", $decregbase+3)}{$tag1}) . " "; + $tmp .= unpack("H*",$data{$dev}{sprintf("%02x", $decregbase+2)}{$tag1}) . " "; + $tmp .= unpack("H*",$data{$dev}{sprintf("%02x", $decregbase+1)}{$tag1}) . " "; + $tmp .= unpack("H*",$data{$dev}{sprintf("%02x", $decregbase)}{$tag1}) . "\n"; + $tmp .= sprintf("%44s: %02x", $tag2, $decregbase) . ": "; + $tmp .= unpack("H*",$data{$dev}{sprintf("%02x", $decregbase+3)}{$tag2}) . " "; + $tmp .= unpack("H*",$data{$dev}{sprintf("%02x", $decregbase+2)}{$tag2}) . " "; + $tmp .= unpack("H*",$data{$dev}{sprintf("%02x", $decregbase+1)}{$tag2}) . " "; + $tmp .= unpack("H*",$data{$dev}{sprintf("%02x", $decregbase)}{$tag2}) . "\n"; + print "
$tmp
\n"; + $tmp = sprintf("%44s: %02x", $tag1, $decregbase) . ": "; + $tmp .= unpack("B*",$data{$dev}{sprintf("%02x", $decregbase+3)}{$tag1}) . " "; + $tmp .= unpack("B*",$data{$dev}{sprintf("%02x", $decregbase+2)}{$tag1}) . " "; + $tmp .= unpack("B*",$data{$dev}{sprintf("%02x", $decregbase+1)}{$tag1}) . " "; + $tmp .= unpack("B*",$data{$dev}{sprintf("%02x", $decregbase)}{$tag1}) . "\n"; + $tmp .= sprintf("%44s: %02x", $tag2, $decregbase) . ": "; + $tmp .= unpack("B*",$data{$dev}{sprintf("%02x", $decregbase+3)}{$tag2}) . " "; + $tmp .= unpack("B*",$data{$dev}{sprintf("%02x", $decregbase+2)}{$tag2}) . " "; + $tmp .= unpack("B*",$data{$dev}{sprintf("%02x", $decregbase+1)}{$tag2}) . " "; + $tmp .= unpack("B*",$data{$dev}{sprintf("%02x", $decregbase)}{$tag2}) . "\n"; + print "
$tmp
\n"; + $printed{$decregbase} = 1; + } + + if (!exists($info{$reg})) { + print STDERR "
MISSING DATA for register $reg ($tag1) --- ";
+        print STDERR "$reg: " . unpack("H*",$data{$dev}{$reg}{$tag1}) . "
\n"; + return ''; + } + + for (my $i=0; $i<=$#xor;$i++) { + my $invi = 31 - $i; + if ($xor[$i] eq '1') { +#print STDERR "REG: $reg INVI: $invi\n"; +#print STDERR $info{$reg}{'fields'}{$invi} . "\n"; +#print STDERR $info{$reg}{'fields'}{$invi}{'range'} . "\n"; + my $r = $info{$reg}{'fields'}{$invi}{'range'}; +# if (!exists($changed{$r})) { +# $changed{$r}{'v1'} = ''; +# $changed{$r}{'v2'} = ''; +# } +# $changed{$r}{'v1'} .= $val1[$i]; +# $changed{$r}{'v2'} .= $val2[$i]; + $changed{$r}{'v1'} = 1; + $changed{$r}{'v2'} = 1; + } + } + + foreach my $r (keys %changed) { + my $width = $info{$reg}{'ranges'}{$r}{'width'}; + #$changed{$r}{'v1'} = sprintf("%0" . $width . "sb",$changed{$r}{'v1'}); + #$changed{$r}{'v2'} = sprintf("%0" . $width . "sb",$changed{$r}{'v2'}); + #my $v1 = $changed{$r}{'v1'}; + #my $v2 = $changed{$r}{'v2'}; + my $v1 = substr(unpack("B*",$val1),31-$info{$reg}{'ranges'}{$r}{'end'},$info{$reg}{'ranges'}{$r}{'width'}) . 'b'; + my $v2 = substr(unpack("B*",$val2),31-$info{$reg}{'ranges'}{$r}{'end'},$info{$reg}{'ranges'}{$r}{'width'}) . 'b'; + + my $desc = $info{$reg}{'ranges'}{$r}{'description'}; + $desc =~ s/\n+/
/g; + + $retval2 .= $info{$reg}{'ranges'}{$r}{'function'} . " (" . $info{$reg}{'ranges'}{$r}{'mnemonic'} . ") - Bits ($r)" . "
"; + $retval2 .= "  $desc

" if ($desc ne ''); + + $v1 = $v1 . " (" . $info{$reg}{'ranges'}{$r}{'values'}{$v1} . ")" if (exists($info{$reg}{'ranges'}{$r}{'values'}{$v1})); + $v2 = $v2 . " (" . $info{$reg}{'ranges'}{$r}{'values'}{$v2} . ")" if (exists($info{$reg}{'ranges'}{$r}{'values'}{$v2})); + $retval2 .= sprintf("%44s: %s\n",$tag1, $v1); + $retval2 .= sprintf("%44s: %s\n",$tag2, $v2); + $retval2 .= "

"; + } + + +# this prints out the bitwise differences. TODO: clean up + +# for (my $i=0; $i<=$#xor;$i++) { +# my $invi = 31 - $i; +# if ($xor[$i] eq '1') { +# my $m = $info{$reg}{'fields'}{$invi}{'mnemonic'}; +# my $f = $info{$reg}{'fields'}{$invi}{'function'}; +# my $range = $info{$reg}{'fields'}{$invi}{'range'}; +# if ($m && $f) { +# $retval2 .= "Bit $invi ($info{$reg}{'fields'}{$invi}{'mnemonic'} - $info{$reg}{'fields'}{$invi}{'function'}):\n"; +# $retval2 .= sprintf("%32s: %d\n",$tag1, $val1[$i]); +# $retval2 .= sprintf("%32s: %d\n",$tag2, $val2[$i]); +# } else { +# $retval2 .= "Bit $invi:\n"; +# $retval2 .= sprintf("%32s: %d\n",$tag1, $val1[$i]); +# $retval2 .= sprintf("%32s: %d\n",$tag2, $val2[$i]); +# } +# } +# } + + $retval .= "\n"; + if ($retval2 ne '') { + $retval .= "\n\n$retval2\n"; + my $n = $info{$reg}{'name'}; + my $d = $info{$reg}{'description'}; + $n ||= ''; + $d ||= ''; + my $old = $retval; + $retval = ''; + $retval .= sprintf("%40s -> %s
\n","XOR",unpack("B*",$xor)) if ($DEBUG); + $retval .= "\n$n\n" if ($n ne ''); + $retval .= " $d" if ($d ne ''); + $retval .= $old; + $retval .= "\n"; + } + + return "

$retval
"; +} + +sub load_datafile { + my $file = 'bkdg.data'; + my $return = ''; + + if (-f $file) { + unless ($return = do $file) { + warn "couldn't parse $file: $@" if $@; + warn "couldn't do $file: $!" unless defined $return; + warn "couldn't run $file" unless $return; + } + } else { + print "Warning: data file '$file' not found - $0 will only report on differing bits without explanation.\n"; + } + +} + +sub main { + my @filenames; + my $version = 0; + + GetOptions ("filename=s" => \@filenames, "version" => \$version); + + &version_information($NAME,$VERSION,$DATE,$COPYRIGHT,$AUTHOR,$LICENSE,$URL) if ($version); + + &usage_information() if ($#filenames < 1); + + &load_datafile(); + + foreach my $file (@filenames) { + print STDERR "processing $file\n"; + %data = &parse_file($file,%data); + } + + print "\n\n"; + + foreach my $dev (sort keys %data) { + + foreach my $reg (sort keys %{$data{$dev}}) { + my $first = pack("H*",'00000000'); + my $firstfile = ''; + foreach my $file (reverse sort keys %{$data{$dev}{$reg}}) { + if (unpack("H*",$first) eq '00000000') { + $first = $data{$dev}{$reg}{$file}; + $firstfile = $file; + } + if (unpack("H*",$first) ne unpack("H*",$data{$dev}{$reg}{$file})) { + #my $reg = ($key =~ /\s+([a-z0-9]+)$/i)[0]; + if ($DEBUG) { + print "
";
+                    printf("%44s -> %s (%s)\n",$firstfile,unpack("B*",$first),unpack("H*",$first));
+                    printf("%44s -> %s (%s)\n",$file,unpack("B*",$data{$dev}{$reg}{$file}),unpack("H*",$data{$dev}{$reg}{$file}));
+                    print "
"; + } + + print &interpret_differences($dev,$reg,$firstfile,$first,$file,$data{$dev}{$reg}{$file}); + } + } + } + } + print "\n\n"; + +} + diff --git a/util/amdtools/k8-interpret-extended-memory-settings.pl b/util/amdtools/k8-interpret-extended-memory-settings.pl new file mode 100755 index 0000000000..0bb4e62a44 --- /dev/null +++ b/util/amdtools/k8-interpret-extended-memory-settings.pl @@ -0,0 +1,248 @@ +#!/usr/bin/perl -w +use Getopt::Long; + +use strict; + +my $NAME = $0; +my $VERSION = '0.01'; +my $DATE = '2009-09-04'; +my $AUTHOR = "Ward Vandewege "; +my $COPYRIGHT = "2009"; +my $LICENSE = "GPL v3 - http://www.fsf.org/licenses/gpl.txt"; +my $URL = "http://coreboot.org"; + +my $DEBUG = 0; + +our %info; + +$|=1; + +&main(); + +sub version_information { + my ($NAME,$VERSION,$DATE,$COPYRIGHT,$AUTHOR,$LICENSE,$URL) = (shift,shift,shift,shift,shift,shift,shift); + print "\nThis is $NAME version $VERSION ($DATE)\n"; + print "Copyright (c) $COPYRIGHT by $AUTHOR\n"; + print "License: $LICENSE\n"; + print "More information at $URL\n\n"; + exit; +} + +sub usage_information { + my $retval = "\n$NAME v$VERSION ($DATE)\n"; + $retval .= "\nYou have not supplied all required parameters. $NAME takes these arguments:\n"; + $retval .= " $NAME -f -f \n\n"; + $retval .= " -f is the name of a file with k8 memory configuration values\n"; + $retval .= " -f is the name of a second file with k8 memory configuration values, to compare with filename1\n"; + $retval .= " -v (optional) provides version information\n"; + $retval .= "\nSee the k8-read-mem-settings.sh script for an example of how to generate the input files to this script.\n\n"; + print $retval; + exit; +} + +sub parse_file { + my $register = ''; + my $devreg = ''; + my $filename = shift; + my %data = @_; + open(TMP, $filename) || die "Could not open $filename: $!\n"; + while () { + chomp; + # Line format - pairs of lines: + # 0:18.2 98.l: 80000000 + # 0:18.2 9C.l: 10111222 + # First field is pci device. Second field is register offset (hex) + # where third field value (in hex) was read from. + my @tmp = split(/ /); + $tmp[1] =~ s/:$//; # strip optional trailing colon on second field + + my $device = $tmp[0]; + my $packed = pack("H*",$tmp[2]); # Pack our number so we can easily represent it in binary + my $binrep = unpack("B*", $packed); # Binary string representation + + if ($tmp[1] eq '98.l') { + $register = ($tmp[2] =~ /(..)$/)[0]; # last 2 digits are (hex) of what we wrote to the register, if second field is 98.l + $devreg = "$device $register"; + if ("$binrep" =~ /^1/) { + # bit 31 *must* be 1 if readout is to be correct + print "$tmp[0] - $register
\n" if ($DEBUG); + } else { + print "ERROR: we read too fast: $tmp[2] does not have bit 31 set ($binrep)\n"; + exit; + } + } else { + # last field is register value (hex) + print "$tmp[2]h ($binrep)
\n" if ($DEBUG); + $data{$devreg} = {} if (!defined($data{$devreg})); + $data{$devreg}{$filename} = $packed; + } + } + return %data; +} + +sub interpret_differences { + my $reg = shift; + $reg = sprintf("%02s",$reg); + my $tag1 = shift; + my $val1 = shift; + my $tag2 = shift; + my $val2 = shift; + my $retval = ''; + my $retval2 = ''; + + # XOR values together - the positions with 1 after the XOR are the ones with the differences + my $xor = $val1 ^ $val2; + + my @val1 = split(//,unpack("B*",$val1)); + my @val2 = split(//,unpack("B*",$val2)); + my @xor = split(//,unpack("B*",$xor)); + + my %changed; + + if (!exists($info{$reg})) { + print STDERR "MISSING DATA for register $reg\n"; + return ''; + } + + for (my $i=0; $i<=$#xor;$i++) { + my $invi = 31 - $i; + if ($xor[$i] eq '1') { +#print STDERR "REG: $reg INVI: $invi\n"; +#print STDERR $info{$reg}{'fields'}{$invi} . "\n"; +#print STDERR $info{$reg}{'fields'}{$invi}{'range'} . "\n"; + my $r = $info{$reg}{'fields'}{$invi}{'range'}; +# if (!exists($changed{$r})) { +# $changed{$r}{'v1'} = ''; +# $changed{$r}{'v2'} = ''; +# } +# $changed{$r}{'v1'} .= $val1[$i]; +# $changed{$r}{'v2'} .= $val2[$i]; + $changed{$r}{'v1'} = 1; + $changed{$r}{'v2'} = 1; + } + } + + foreach my $r (keys %changed) { + my $width = $info{$reg}{'ranges'}{$r}{'width'}; + #$changed{$r}{'v1'} = sprintf("%0" . $width . "sb",$changed{$r}{'v1'}); + #$changed{$r}{'v2'} = sprintf("%0" . $width . "sb",$changed{$r}{'v2'}); + #my $v1 = $changed{$r}{'v1'}; + #my $v2 = $changed{$r}{'v2'}; + my $v1 = substr(unpack("B*",$val1),31-$info{$reg}{'ranges'}{$r}{'end'},$info{$reg}{'ranges'}{$r}{'width'}) . 'b'; + my $v2 = substr(unpack("B*",$val2),31-$info{$reg}{'ranges'}{$r}{'end'},$info{$reg}{'ranges'}{$r}{'width'}) . 'b'; + + my $desc = $info{$reg}{'ranges'}{$r}{'description'}; + $desc =~ s/\n+/
/g; + + $retval2 .= $info{$reg}{'ranges'}{$r}{'function'} . " (" . $info{$reg}{'ranges'}{$r}{'mnemonic'} . ") - Bits ($r)" . "
"; + $retval2 .= "  $desc

" if ($desc ne ''); + + $v1 = $v1 . " (" . $info{$reg}{'ranges'}{$r}{'values'}{$v1} . ")" if (exists($info{$reg}{'ranges'}{$r}{'values'}{$v1})); + $v2 = $v2 . " (" . $info{$reg}{'ranges'}{$r}{'values'}{$v2} . ")" if (exists($info{$reg}{'ranges'}{$r}{'values'}{$v2})); + $retval2 .= sprintf("%44s: %s\n",$tag1, $v1); + $retval2 .= sprintf("%44s: %s\n",$tag2, $v2); + $retval2 .= "

"; + } + + +# this prints out the bitwise differences. TODO: clean up + +# for (my $i=0; $i<=$#xor;$i++) { +# my $invi = 31 - $i; +# if ($xor[$i] eq '1') { +# my $m = $info{$reg}{'fields'}{$invi}{'mnemonic'}; +# my $f = $info{$reg}{'fields'}{$invi}{'function'}; +# my $range = $info{$reg}{'fields'}{$invi}{'range'}; +# if ($m && $f) { +# $retval2 .= "Bit $invi ($info{$reg}{'fields'}{$invi}{'mnemonic'} - $info{$reg}{'fields'}{$invi}{'function'}):\n"; +# $retval2 .= sprintf("%32s: %d\n",$tag1, $val1[$i]); +# $retval2 .= sprintf("%32s: %d\n",$tag2, $val2[$i]); +# } else { +# $retval2 .= "Bit $invi:\n"; +# $retval2 .= sprintf("%32s: %d\n",$tag1, $val1[$i]); +# $retval2 .= sprintf("%32s: %d\n",$tag2, $val2[$i]); +# } +# } +# } + + $retval .= "\n"; + if ($retval2 ne '') { + $retval .= "\n\n$retval2\n"; + my $n = $info{$reg}{'name'}; + my $d = $info{$reg}{'description'}; + $n ||= ''; + $d ||= ''; + my $old = $retval; + $retval = ''; + $retval .= sprintf("%40s -> %s
\n","XOR",unpack("B*",$xor)) if ($DEBUG); + $retval .= "\n$n\n" if ($n ne ''); + $retval .= " $d" if ($d ne ''); + $retval .= $old; + $retval .= "\n"; + } + + return "

$retval
"; +} + +sub load_datafile { + my $file = 'bkdg.data'; + my $return = ''; + + if (-f $file) { + unless ($return = do $file) { + warn "couldn't parse $file: $@" if $@; + warn "couldn't do $file: $!" unless defined $return; + warn "couldn't run $file" unless $return; + } + } else { + print "Warning: data file '$file' not found - $0 will only report on differing bits without explanation.\n"; + } + +} + +sub main { + my @filenames; + my $version = 0; + my %data; + + GetOptions ("filename=s" => \@filenames, "version" => \$version); + + &version_information($NAME,$VERSION,$DATE,$COPYRIGHT,$AUTHOR,$LICENSE,$URL) if ($version); + + &usage_information() if ($#filenames < 1); + + &load_datafile(); + + foreach my $file (@filenames) { + print STDERR "processing $file\n"; + %data = &parse_file($file,%data); + } + + print "\n\n"; + + foreach my $key (sort keys %data) { + my $first = pack("H*",'00000000'); + my $firstfile = ''; + foreach my $k2 (reverse sort keys %{$data{$key}}) { + if (unpack("H*",$first) eq '00000000') { + $first = $data{$key}{$k2}; + $firstfile = $k2; + } + if (unpack("H*",$first) ne unpack("H*",$data{$key}{$k2})) { + my $reg = ($key =~ /\s+([a-z0-9]+)$/i)[0]; + print "$key\n"; + if ($DEBUG) { + print "
";
+                    printf("%44s -> %s (%s)\n",$firstfile,unpack("B*",$first),unpack("H*",$first));
+                    printf("%44s -> %s (%s)\n",$k2,unpack("B*",$data{$key}{$k2}),unpack("H*",$data{$key}{$k2}));
+                    print "
"; + } + + print &interpret_differences($reg,$firstfile,$first,$k2,$data{$key}{$k2}); + } + } + } + print "\n\n"; + +} + diff --git a/util/amdtools/k8-read-mem-settings.sh b/util/amdtools/k8-read-mem-settings.sh new file mode 100755 index 0000000000..a3d607b7ce --- /dev/null +++ b/util/amdtools/k8-read-mem-settings.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# This is an example that generates data files that are understood by the +# k8-interpret-extended-memory-settings.pl script. Adjust the pci ids for your +# board (0:18.2 and 0:19.2 are correct for supermicro h8dme) + +# Ward Vandewege, 2009-09-04 + + +for OFFSET in 00, 01, 02, 03, 04, 05, 06, 07, 20, 21, 22, 23, 24, 25, 26, 27, 10, 13, 16, 19, 30, 33, 36, 39; do + + setpci -s 0:18.2 98.l=$OFFSET + echo 0:18.2 98.l: `setpci -s 0:18.2 98.l` + echo 0:18.2 9C.l: `setpci -s 0:18.2 9C.l` + +done + +for OFFSET in 00, 01, 02, 03, 04, 05, 06, 07, 20, 21, 22, 23, 24, 25, 26, 27, 10, 13, 16, 19, 30, 33, 36, 39; do + + setpci -s 0:19.2 98.l=$OFFSET + echo 0:19.2 98.l: `setpci -s 0:19.2 98.l` + echo 0:19.2 9C.l: `setpci -s 0:19.2 9C.l` + +done + diff --git a/util/amdtools/parse-bkdg.pl b/util/amdtools/parse-bkdg.pl new file mode 100755 index 0000000000..d359e4abe7 --- /dev/null +++ b/util/amdtools/parse-bkdg.pl @@ -0,0 +1,286 @@ +#!/usr/bin/perl -w + +my $NAME = $0; +my $VERSION = '0.01'; +my $DATE = '2009-09-04'; +my $AUTHOR = "Ward Vandewege "; +my $COPYRIGHT = "2009"; +my $LICENSE = "GPL v3 - http://www.fsf.org/licenses/gpl.txt"; +my $URL = "http://coreboot.org"; + +my $DEBUG = 0; + +use strict; + +# Run the bkdg for k8 through pdftotext first (from the poppler package) + +my @registers = (); +my $raw_register = ''; + +my $name = ''; +my $description = ''; +my $step = 0; +my $oldstep = 0; + +my $previous_res = 0; +my $previous_start = 0; +my $previous_stop = 0; +my $strip_empties = 0; + +my $previous_bits = ''; + +our %info; + +my %typos; + +$typos{'CkeDreStrength'} = 'CkeDrvStrength'; + +while (<>) { + my $line = $_; + chomp($line); + + foreach my $k (keys %typos) { + $line =~ s/$k/$typos{$k}/; + } + + # Make sure we do not include headers in our output + if (($line =~ /^Chapter 4/) || ($line =~ /Chapter 4$/)) { + $oldstep = $step; + $step = 99; + next; + } + if ($step == 99) { # header + if ($line =~ /Processors$/) { + $step = $oldstep; + $strip_empties = 1; + } + next; + } + + if ($strip_empties) { + # Headers are followed by two blank lines. Strip them. + if ($line =~ /^\s*$/) { + next; + } else { + $strip_empties = 0; + } + } + + + if (($step % 6 == 0) && ($line =~ /^\d+\.\d+\.\d+\s+(.*)$/)) { + $step = 1; + next; + } + if ($step == 1) { + $description = "$line\n"; + $step = 2; + next; + } + #print STDERR "STEP: $step\n"; + #print STDERR "$line\n"; + + if ((($step == 0) || ($step == 6) || ($step == 2)) && ($line =~ /^(.*)\s+Function\s+\d+:\s+Offset\s+..h$/)) { + $name = $1; + $name =~ s/ +$//; + $step = 3; + $description =~ s/\n+$//ms; + + if ($previous_bits ne '') { + &finish_record($previous_bits); + $previous_bits = ''; # reset previous_bits (used in step 6) + } + + + next; + } elsif ($step == 2) { + $description .= "$line\n"; + next; + } + + if (($step == 3) && ($line =~ /^\s+Index (.+h)$/)) { + $raw_register= $1; + @registers = split(/,/,$raw_register); + for (my $i=0;$i<=$#registers;$i++) { + $registers[$i] =~ s/ //g; + $registers[$i] =~ s/h$//; + } + # OK, we have our register(s), so now we can print out the name and description lines. + print "\$info{'$registers[0]'}{'name'} = \"$name\";\n"; + print "\$info{'$registers[0]'}{'description'} = \"$description\";\n"; + $step = 4; + next; + } + + if (($step == 4) && ($line =~ /^Bits\s+Mnemonic\s+Function\s+R\/W\s+Reset$/)) { + $step = 5; + next; + } + + if (($step == 5) && (!($line =~ /^Field Descriptions$/))) { + $line =~ s/^ +//; # Strip leading spaces + my @f = split(/ +/,$line); + + # skip blank lines + next if (!exists($f[0])); + + # skip headers (they could be repeated if the table crosses a page boundary + next if ($f[0] eq 'Bits'); + + # Clean up funky field separator + if ($f[0] =~ /\d+.+\d+/) { + $f[0] =~ s/[^\d]+/-/g; + } + + my ($start, $stop, $width) = (0,0,0); + if ($f[0] =~ /-/) { + $f[0] =~ s/^(\d+)[^\d]+(\d+)$/$1-$2/; + ($stop,$start) = ($1,$2); + $width = $stop-$start+1; + } else { + if ($f[0] =~ /^\d+$/) { + $start = $stop = $f[0]; + $width = 1; + } else { + # continuation from previous line + $start = $stop = $width = 0; + } + } + + # Some lines have only bit entries + if (($#f < 1) && ($f[0] =~ /^\d+(|\-\d+)/)) { + $f[4] = ''; + $f[3] = ''; + $f[2] = ''; + $f[1] = ''; + } elsif ($#f < 1) { + # Some lines are a continuation of the function field a line above + $f[4] = ''; + $f[3] = ''; + $f[2] = $f[0]; + $f[1] = ''; + $f[0] = ''; + my $tmp = "\$info{'$registers[0]'}{'ranges'}{" . $previous_res . "}{'function'} .= \"" . $f[2] . "\";\n"; + print &multiply($tmp,$previous_res,$previous_start,$previous_stop); + next; + } + + # Some lines have only bit and reset entries + if ($#f < 2) { + $f[4] = $f[1]; + $f[3] = ''; + $f[2] = ''; + $f[1] = ''; + } + + # Some lines have no mnemonic and no function + if ($#f < 3) { + $f[4] = $f[2]; + $f[3] = $f[1]; + $f[2] = ''; + $f[1] = ''; + } + + # functions with 'reserved' mnemonic have no function + if ($f[1] =~ /^reserved$/i) { + $f[4] = $f[3]; + $f[3] = $f[2]; + $f[2] = ''; + } + + $previous_res = $f[0]; + $previous_start = $start; + $previous_stop = $stop; + + # the 'range' field is not useful in this instance, but used in the 'fields' version of this block to easily go + # from a bit position to the corresponding range. + my $str = " +\$info{'$registers[0]'}{'ranges'}{'" . $f[0] . "'}{'function'} = \"" . $f[2] . "\"; +\$info{'$registers[0]'}{'ranges'}{'" . $f[0] . "'}{'mnemonic'} = \"" . $f[1] . "\"; +\$info{'$registers[0]'}{'ranges'}{'" . $f[0] . "'}{'description'} = \"" . "\"; +\$info{'$registers[0]'}{'ranges'}{'" . $f[0] . "'}{'begin'} = $start; +\$info{'$registers[0]'}{'ranges'}{'" . $f[0] . "'}{'end'} = $stop; +\$info{'$registers[0]'}{'ranges'}{'" . $f[0] . "'}{'width'} = $width; +\$info{'$registers[0]'}{'ranges'}{'" . $f[0] . "'}{'rw'} = \"" . $f[3] . "\"; +\$info{'$registers[0]'}{'ranges'}{'" . $f[0] . "'}{'reset'} = \"" . $f[4] . "\"; +\$info{'$registers[0]'}{'ranges'}{'" . $f[0] . "'}{'range'} = \"" . $f[0] . "\"; +"; + my $output; + + $output = &multiply($str,$f[0],$start,$stop); + + # Load the data structure here, too + eval($output); + + print $output . "\n\n"; + } elsif (($step == 5) && ($line =~ /^Field Descriptions$/)) { + $step = 6; + next; + } + + if ($step == 6) { + if ($line =~ /^(.*?)\((.*?)\).+Bit(s|) +(.*?)\. (.*)$/) { + my $bits = $4; + my $desc = $5; + $bits =~ s/[^\d]+/-/; + + if ($previous_bits ne '') { + # We're done with a field description block + print "\$info{'$registers[0]'}{'ranges'}{'$previous_bits'}{'description'} = \"" . $info{$registers[0]}{'ranges'}{$previous_bits}{'description'} . "\";\n"; + foreach my $k (keys %{$info{$registers[0]}{'ranges'}{$previous_bits}{'values'}}) { + print "\$info{'$registers[0]'}{'ranges'}{'$previous_bits'}{'values'}{'$k'} = \"" . $info{$registers[0]}{'ranges'}{$previous_bits}{'values'}{$k} . "\";\n"; + } + } + + if (exists($info{$registers[0]}{'ranges'}{$bits})) { + print STDERR "match ($bits) on $line\n"; + $info{$registers[0]}{'ranges'}{$bits}{'description'} = $desc . "\n"; + $previous_bits = $bits; + } + } elsif ($previous_bits ne '') { + $info{$registers[0]}{'ranges'}{$previous_bits}{'description'} .= $line . "\n"; + if ($line =~ /([0-9a-f]+b|[0-9a-f]+h) = (.*)$/i) { + $info{$registers[0]}{'ranges'}{$previous_bits}{'values'}{$1} = $2; + } + } + } + +} +&finish_record($previous_bits); + + +print "1;\n"; + +sub multiply { + my $str = shift; + my $range = shift; + my $start = shift; + my $stop = shift; + my $output = ''; + for (my $i=$start;$i<=$stop;$i++) { + my $tmp = $str; + $tmp =~ s/\{'$range'\}/{'$i'}/g; + $tmp =~ s/\{'ranges'\}/{'fields'}/g; + $tmp .= + $output .= $tmp; + } + + #$output .= $str if (($stop - $start + 1) > 1); + $output .= $str; + + return $output; +} + +sub finish_record { + my $previous_bits = shift; + # We're done with a field description block + print "\$info{'$registers[0]'}{'ranges'}{'$previous_bits'}{'description'} = \"" . $info{$registers[0]}{'ranges'}{$previous_bits}{'description'} . "\";\n"; + foreach my $k (keys %{$info{$registers[0]}{'ranges'}{$previous_bits}{'values'}}) { + print "\$info{'$registers[0]'}{'ranges'}{'$previous_bits'}{'values'}{'$k'} = \"" . $info{$registers[0]}{'ranges'}{$previous_bits}{'values'}{$k} . "\";\n"; + } + + # End of table. If this data applies to more than one register, print duplication lines. + for (my $i=1;$i<=$#registers;$i++) { + print "\$info{'$registers[$i]'} = \$info{'$registers[0]'};\n"; + } + +}