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