Add an initial version of some tools to compare (extended) K8 memory settings.
[coreboot.git] / util / amdtools / parse-bkdg.pl
diff --git a/util/amdtools/parse-bkdg.pl b/util/amdtools/parse-bkdg.pl
new file mode 100755 (executable)
index 0000000..d359e4a
--- /dev/null
@@ -0,0 +1,286 @@
+#!/usr/bin/perl -w
+
+my $NAME = $0;
+my $VERSION = '0.01';
+my $DATE = '2009-09-04';
+my $AUTHOR = "Ward Vandewege <ward\@jhvc.com>";
+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";
+          }
+
+}