#!/usr/bin/env perl 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 = "https://coreboot.org"; my $DEBUG = 0; use strict; use warnings; # 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"; } }