diff options
Diffstat (limited to 'util/amdtools/parse-bkdg.pl')
-rwxr-xr-x | util/amdtools/parse-bkdg.pl | 286 |
1 files changed, 286 insertions, 0 deletions
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 <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"; + } + +} |