xref: /aosp_15_r20/external/coreboot/util/amdtools/parse-bkdg.pl (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
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