1#!/usr/bin/env perl 2 3my $NAME = $0; 4my $VERSION = '0.01'; 5my $DATE = '2009-09-04'; 6my $AUTHOR = "Ward Vandewege <ward\@jhvc.com>"; 7my $COPYRIGHT = "2009"; 8my $LICENSE = "GPL v3 - http://www.fsf.org/licenses/gpl.txt"; 9my $URL = "https://coreboot.org"; 10 11my $DEBUG = 0; 12 13use strict; 14use warnings; 15 16# Run the bkdg for k8 through pdftotext first (from the poppler package) 17 18my @registers = (); 19my $raw_register = ''; 20 21my $name = ''; 22my $description = ''; 23my $step = 0; 24my $oldstep = 0; 25 26my $previous_res = 0; 27my $previous_start = 0; 28my $previous_stop = 0; 29my $strip_empties = 0; 30 31my $previous_bits = ''; 32 33our %info; 34 35my %typos; 36 37$typos{'CkeDreStrength'} = 'CkeDrvStrength'; 38 39while (<>) { 40 my $line = $_; 41 chomp($line); 42 43 foreach my $k (keys %typos) { 44 $line =~ s/$k/$typos{$k}/; 45 } 46 47 # Make sure we do not include headers in our output 48 if (($line =~ /^Chapter 4/) || ($line =~ /Chapter 4$/)) { 49 $oldstep = $step; 50 $step = 99; 51 next; 52 } 53 if ($step == 99) { # header 54 if ($line =~ /Processors$/) { 55 $step = $oldstep; 56 $strip_empties = 1; 57 } 58 next; 59 } 60 61 if ($strip_empties) { 62 # Headers are followed by two blank lines. Strip them. 63 if ($line =~ /^\s*$/) { 64 next; 65 } else { 66 $strip_empties = 0; 67 } 68 } 69 70 71 if (($step % 6 == 0) && ($line =~ /^\d+\.\d+\.\d+\s+(.*)$/)) { 72 $step = 1; 73 next; 74 } 75 if ($step == 1) { 76 $description = "$line\n"; 77 $step = 2; 78 next; 79 } 80 #print STDERR "STEP: $step\n"; 81 #print STDERR "$line\n"; 82 83 if ((($step == 0) || ($step == 6) || ($step == 2)) && ($line =~ /^(.*)\s+Function\s+\d+:\s+Offset\s+..h$/)) { 84 $name = $1; 85 $name =~ s/ +$//; 86 $step = 3; 87 $description =~ s/\n+$//ms; 88 89 if ($previous_bits ne '') { 90 &finish_record($previous_bits); 91 $previous_bits = ''; # reset previous_bits (used in step 6) 92 } 93 94 95 next; 96 } elsif ($step == 2) { 97 $description .= "$line\n"; 98 next; 99 } 100 101 if (($step == 3) && ($line =~ /^\s+Index (.+h)$/)) { 102 $raw_register= $1; 103 @registers = split(/,/,$raw_register); 104 for (my $i=0;$i<=$#registers;$i++) { 105 $registers[$i] =~ s/ //g; 106 $registers[$i] =~ s/h$//; 107 } 108 # OK, we have our register(s), so now we can print out the name and description lines. 109 print "\$info{'$registers[0]'}{'name'} = \"$name\";\n"; 110 print "\$info{'$registers[0]'}{'description'} = \"$description\";\n"; 111 $step = 4; 112 next; 113 } 114 115 if (($step == 4) && ($line =~ /^Bits\s+Mnemonic\s+Function\s+R\/W\s+Reset$/)) { 116 $step = 5; 117 next; 118 } 119 120 if (($step == 5) && (!($line =~ /^Field Descriptions$/))) { 121 $line =~ s/^ +//; # Strip leading spaces 122 my @f = split(/ +/,$line); 123 124 # skip blank lines 125 next if (!exists($f[0])); 126 127 # skip headers (they could be repeated if the table crosses a page boundary 128 next if ($f[0] eq 'Bits'); 129 130 # Clean up funky field separator 131 if ($f[0] =~ /\d+.+\d+/) { 132 $f[0] =~ s/[^\d]+/-/g; 133 } 134 135 my ($start, $stop, $width) = (0,0,0); 136 if ($f[0] =~ /-/) { 137 $f[0] =~ s/^(\d+)[^\d]+(\d+)$/$1-$2/; 138 ($stop,$start) = ($1,$2); 139 $width = $stop-$start+1; 140 } else { 141 if ($f[0] =~ /^\d+$/) { 142 $start = $stop = $f[0]; 143 $width = 1; 144 } else { 145 # continuation from previous line 146 $start = $stop = $width = 0; 147 } 148 } 149 150 # Some lines have only bit entries 151 if (($#f < 1) && ($f[0] =~ /^\d+(|\-\d+)/)) { 152 $f[4] = ''; 153 $f[3] = ''; 154 $f[2] = ''; 155 $f[1] = ''; 156 } elsif ($#f < 1) { 157 # Some lines are a continuation of the function field a line above 158 $f[4] = ''; 159 $f[3] = ''; 160 $f[2] = $f[0]; 161 $f[1] = ''; 162 $f[0] = ''; 163 my $tmp = "\$info{'$registers[0]'}{'ranges'}{" . $previous_res . "}{'function'} .= \"" . $f[2] . "\";\n"; 164 print &multiply($tmp,$previous_res,$previous_start,$previous_stop); 165 next; 166 } 167 168 # Some lines have only bit and reset entries 169 if ($#f < 2) { 170 $f[4] = $f[1]; 171 $f[3] = ''; 172 $f[2] = ''; 173 $f[1] = ''; 174 } 175 176 # Some lines have no mnemonic and no function 177 if ($#f < 3) { 178 $f[4] = $f[2]; 179 $f[3] = $f[1]; 180 $f[2] = ''; 181 $f[1] = ''; 182 } 183 184 # functions with 'reserved' mnemonic have no function 185 if ($f[1] =~ /^reserved$/i) { 186 $f[4] = $f[3]; 187 $f[3] = $f[2]; 188 $f[2] = ''; 189 } 190 191 $previous_res = $f[0]; 192 $previous_start = $start; 193 $previous_stop = $stop; 194 195 # the 'range' field is not useful in this instance, but used in the 'fields' version of this block to easily go 196 # from a bit position to the corresponding range. 197 my $str = " 198\$info{'$registers[0]'}{'ranges'}{'" . $f[0] . "'}{'function'} = \"" . $f[2] . "\"; 199\$info{'$registers[0]'}{'ranges'}{'" . $f[0] . "'}{'mnemonic'} = \"" . $f[1] . "\"; 200\$info{'$registers[0]'}{'ranges'}{'" . $f[0] . "'}{'description'} = \"" . "\"; 201\$info{'$registers[0]'}{'ranges'}{'" . $f[0] . "'}{'begin'} = $start; 202\$info{'$registers[0]'}{'ranges'}{'" . $f[0] . "'}{'end'} = $stop; 203\$info{'$registers[0]'}{'ranges'}{'" . $f[0] . "'}{'width'} = $width; 204\$info{'$registers[0]'}{'ranges'}{'" . $f[0] . "'}{'rw'} = \"" . $f[3] . "\"; 205\$info{'$registers[0]'}{'ranges'}{'" . $f[0] . "'}{'reset'} = \"" . $f[4] . "\"; 206\$info{'$registers[0]'}{'ranges'}{'" . $f[0] . "'}{'range'} = \"" . $f[0] . "\"; 207"; 208 my $output; 209 210 $output = &multiply($str,$f[0],$start,$stop); 211 212 # Load the data structure here, too 213 eval($output); 214 215 print $output . "\n\n"; 216 } elsif (($step == 5) && ($line =~ /^Field Descriptions$/)) { 217 $step = 6; 218 next; 219 } 220 221 if ($step == 6) { 222 if ($line =~ /^(.*?)\((.*?)\).+Bit(s|) +(.*?)\. (.*)$/) { 223 my $bits = $4; 224 my $desc = $5; 225 $bits =~ s/[^\d]+/-/; 226 227 if ($previous_bits ne '') { 228 # We're done with a field description block 229 print "\$info{'$registers[0]'}{'ranges'}{'$previous_bits'}{'description'} = \"" . $info{$registers[0]}{'ranges'}{$previous_bits}{'description'} . "\";\n"; 230 foreach my $k (keys %{$info{$registers[0]}{'ranges'}{$previous_bits}{'values'}}) { 231 print "\$info{'$registers[0]'}{'ranges'}{'$previous_bits'}{'values'}{'$k'} = \"" . $info{$registers[0]}{'ranges'}{$previous_bits}{'values'}{$k} . "\";\n"; 232 } 233 } 234 235 if (exists($info{$registers[0]}{'ranges'}{$bits})) { 236 print STDERR "match ($bits) on $line\n"; 237 $info{$registers[0]}{'ranges'}{$bits}{'description'} = $desc . "\n"; 238 $previous_bits = $bits; 239 } 240 } elsif ($previous_bits ne '') { 241 $info{$registers[0]}{'ranges'}{$previous_bits}{'description'} .= $line . "\n"; 242 if ($line =~ /([0-9a-f]+b|[0-9a-f]+h) = (.*)$/i) { 243 $info{$registers[0]}{'ranges'}{$previous_bits}{'values'}{$1} = $2; 244 } 245 } 246 } 247 248} 249&finish_record($previous_bits); 250 251 252print "1;\n"; 253 254sub multiply { 255 my $str = shift; 256 my $range = shift; 257 my $start = shift; 258 my $stop = shift; 259 my $output = ''; 260 for (my $i=$start;$i<=$stop;$i++) { 261 my $tmp = $str; 262 $tmp =~ s/\{'$range'\}/{'$i'}/g; 263 $tmp =~ s/\{'ranges'\}/{'fields'}/g; 264 $tmp .= 265 $output .= $tmp; 266 } 267 268 #$output .= $str if (($stop - $start + 1) > 1); 269 $output .= $str; 270 271 return $output; 272} 273 274sub finish_record { 275 my $previous_bits = shift; 276 # We're done with a field description block 277 print "\$info{'$registers[0]'}{'ranges'}{'$previous_bits'}{'description'} = \"" . $info{$registers[0]}{'ranges'}{$previous_bits}{'description'} . "\";\n"; 278 foreach my $k (keys %{$info{$registers[0]}{'ranges'}{$previous_bits}{'values'}}) { 279 print "\$info{'$registers[0]'}{'ranges'}{'$previous_bits'}{'values'}{'$k'} = \"" . $info{$registers[0]}{'ranges'}{$previous_bits}{'values'}{$k} . "\";\n"; 280 } 281 282 # End of table. If this data applies to more than one register, print duplication lines. 283 for (my $i=1;$i<=$#registers;$i++) { 284 print "\$info{'$registers[$i]'} = \$info{'$registers[0]'};\n"; 285 } 286 287} 288