• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from __future__ import print_function, division, unicode_literals
2
3CopyRight = '''
4/*
5 * Copyright 2015-2019 Advanced Micro Devices, Inc.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * on the rights to use, copy, modify, merge, publish, distribute, sub
11 * license, and/or sell copies of the Software, and to permit persons to whom
12 * the Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the next
15 * paragraph) shall be included in all copies or substantial portions of the
16 * Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24 * USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 */
27'''
28
29from collections import defaultdict
30import functools
31import itertools
32import json
33import os.path
34import re
35import sys
36
37AMD_REGISTERS = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "../registers"))
38sys.path.append(AMD_REGISTERS)
39
40from regdb import Object, RegisterDatabase
41
42
43class StringTable:
44    """
45    A class for collecting multiple strings in a single larger string that is
46    used by indexing (to avoid relocations in the resulting binary)
47    """
48    def __init__(self):
49        self.table = []
50        self.length = 0
51
52    def add(self, string):
53        # We might get lucky with string being a suffix of a previously added string
54        for te in self.table:
55            if te[0].endswith(string):
56                idx = te[1] + len(te[0]) - len(string)
57                te[2].add(idx)
58                return idx
59
60        idx = self.length
61        self.table.append((string, idx, set((idx,))))
62        self.length += len(string) + 1
63
64        return idx
65
66    def emit(self, filp, name, static=True):
67        """
68        Write
69        [static] const char name[] = "...";
70        to filp.
71        """
72        fragments = [
73            '"%s\\0" /* %s */' % (
74                te[0].encode('unicode_escape').decode(),
75                ', '.join(str(idx) for idx in sorted(te[2]))
76            )
77            for te in self.table
78        ]
79        filp.write('%sconst char %s[] =\n%s;\n' % (
80            'static ' if static else '',
81            name,
82            '\n'.join('\t' + fragment for fragment in fragments)
83        ))
84
85class IntTable:
86    """
87    A class for collecting multiple arrays of integers in a single big array
88    that is used by indexing (to avoid relocations in the resulting binary)
89    """
90    def __init__(self, typename):
91        self.typename = typename
92        self.table = []
93        self.idxs = set()
94
95    def add(self, array):
96        # We might get lucky and find the array somewhere in the existing data
97        try:
98            idx = 0
99            while True:
100                idx = self.table.index(array[0], idx, len(self.table) - len(array) + 1)
101
102                for i in range(1, len(array)):
103                    if array[i] != self.table[idx + i]:
104                        break
105                else:
106                    self.idxs.add(idx)
107                    return idx
108
109                idx += 1
110        except ValueError:
111            pass
112
113        idx = len(self.table)
114        self.table += array
115        self.idxs.add(idx)
116        return idx
117
118    def emit(self, filp, name, static=True):
119        """
120        Write
121        [static] const typename name[] = { ... };
122        to filp.
123        """
124        idxs = sorted(self.idxs) + [len(self.table)]
125
126        fragments = [
127            ('\t/* %s */ %s' % (
128                idxs[i],
129                ' '.join((str(elt) + ',') for elt in self.table[idxs[i]:idxs[i+1]])
130            ))
131            for i in range(len(idxs) - 1)
132        ]
133
134        filp.write('%sconst %s %s[] = {\n%s\n};\n' % (
135            'static ' if static else '',
136            self.typename, name,
137            '\n'.join(fragments)
138        ))
139
140class Field:
141    def __init__(self, name, bits):
142        self.name = name
143        self.bits = bits   # [first, last]
144        self.values = []   # [(name, value), ...]
145
146    def format(self, string_table, idx_table):
147        mask = ((1 << (self.bits[1] - self.bits[0] + 1)) - 1) << self.bits[0]
148        if len(self.values):
149            values_offsets = []
150            for value in self.values:
151                while value[1] >= len(values_offsets):
152                    values_offsets.append(-1)
153                values_offsets[value[1]] = string_table.add(value[0])
154            return '{{{0}, 0x{mask:X}, {1}, {2}}}'.format(
155                string_table.add(self.name),
156                len(values_offsets), idx_table.add(values_offsets),
157                **locals()
158            )
159        else:
160            return '{{{0}, 0x{mask:X}}}'.format(string_table.add(self.name), **locals())
161
162    def __eq__(self, other):
163        return (self.name == other.name and
164                self.bits[0] == other.bits[0] and self.bits[1] == other.bits[1] and
165                len(self.values) == len(other.values) and
166                all(a[0] == b[0] and a[1] == b[1] for a, b, in zip(self.values, other.values)))
167
168    def __ne__(self, other):
169        return not (self == other)
170
171
172class FieldTable:
173    """
174    A class for collecting multiple arrays of register fields in a single big
175    array that is used by indexing (to avoid relocations in the resulting binary)
176    """
177    def __init__(self):
178        self.table = []
179        self.idxs = set()
180        self.name_to_idx = defaultdict(lambda: [])
181
182    def add(self, array):
183        """
184        Add an array of Field objects, and return the index of where to find
185        the array in the table.
186        """
187        # Check if we can find the array in the table already
188        for base_idx in self.name_to_idx.get(array[0].name, []):
189            if base_idx + len(array) > len(self.table):
190                continue
191
192            for i, a in enumerate(array):
193                b = self.table[base_idx + i]
194                if a != b:
195                    break
196            else:
197                return base_idx
198
199        base_idx = len(self.table)
200        self.idxs.add(base_idx)
201
202        for field in array:
203            self.name_to_idx[field.name].append(len(self.table))
204            self.table.append(field)
205
206        return base_idx
207
208    def emit(self, filp, string_table, idx_table):
209        """
210        Write
211        static const struct si_field sid_fields_table[] = { ... };
212        to filp.
213        """
214        idxs = sorted(self.idxs) + [len(self.table)]
215
216        filp.write('static const struct si_field sid_fields_table[] = {\n')
217
218        for start, end in zip(idxs, idxs[1:]):
219            filp.write('\t/* %s */\n' % (start))
220            for field in self.table[start:end]:
221                filp.write('\t%s,\n' % (field.format(string_table, idx_table)))
222
223        filp.write('};\n')
224
225
226def parse_packet3(filp):
227    """
228    Parse PKT3 commands from the given header file.
229    """
230    packets = []
231    for line in filp:
232        if not line.startswith('#define '):
233            continue
234
235        line = line[8:].strip()
236
237        if line.startswith('PKT3_') and line.find('0x') != -1 and line.find('(') == -1:
238            packets.append(line.split()[0])
239    return packets
240
241
242class TableWriter(object):
243    def __init__(self):
244        self.__strings = StringTable()
245        self.__strings_offsets = IntTable('int')
246        self.__fields = FieldTable()
247
248    def write(self, regdb, packets, file=sys.stdout):
249        def out(*args):
250            print(*args, file=file)
251
252        out('/* This file is autogenerated by sid_tables.py from sid.h. Do not edit directly. */')
253        out()
254        out(CopyRight.strip())
255        out('''
256#ifndef SID_TABLES_H
257#define SID_TABLES_H
258
259struct si_field {
260        unsigned name_offset;
261        unsigned mask;
262        unsigned num_values;
263        unsigned values_offset; /* offset into sid_strings_offsets */
264};
265
266struct si_reg {
267        unsigned name_offset;
268        unsigned offset;
269        unsigned num_fields;
270        unsigned fields_offset;
271};
272
273struct si_packet3 {
274        unsigned name_offset;
275        unsigned op;
276};
277''')
278
279        out('static const struct si_packet3 packet3_table[] = {')
280        for pkt in packets:
281            out('\t{%s, %s},' % (self.__strings.add(pkt[5:]), pkt))
282        out('};')
283        out()
284
285        regmaps_by_chip = defaultdict(list)
286
287        for regmap in regdb.register_mappings():
288            for chip in regmap.chips:
289                regmaps_by_chip[chip].append(regmap)
290
291        regtypes = {}
292
293        # Sorted iteration over chips for deterministic builds
294        for chip in sorted(regmaps_by_chip.keys()):
295            regmaps = regmaps_by_chip[chip]
296            regmaps.sort(key=lambda regmap: (regmap.map.to, regmap.map.at))
297
298            out('static const struct si_reg {chip}_reg_table[] = {{'.format(**locals()))
299
300            for regmap in regmaps:
301                if hasattr(regmap, 'type_ref'):
302                    if not regmap.type_ref in regtypes:
303                        regtype = regdb.register_type(regmap.type_ref)
304                        fields = []
305                        for dbfield in regtype.fields:
306                            field = Field(dbfield.name, dbfield.bits)
307                            if hasattr(dbfield, 'enum_ref'):
308                                enum = regdb.enum(dbfield.enum_ref)
309                                for entry in enum.entries:
310                                    field.values.append((entry.name, entry.value))
311                            fields.append(field)
312
313                        num_fields = len(regtype.fields)
314                        fields_offset = self.__fields.add(fields)
315                        regtypes[regmap.type_ref] = (num_fields, fields_offset)
316                    else:
317                        num_fields, fields_offset = regtypes[regmap.type_ref]
318
319                    print('\t{{{0}, {regmap.map.at}, {num_fields}, {fields_offset}}},'
320                          .format(self.__strings.add(regmap.name), **locals()))
321                else:
322                    print('\t{{{0}, {regmap.map.at}}},'
323                          .format(self.__strings.add(regmap.name), **locals()))
324
325            out('};\n')
326
327        self.__fields.emit(file, self.__strings, self.__strings_offsets)
328
329        out()
330
331        self.__strings.emit(file, "sid_strings")
332
333        out()
334
335        self.__strings_offsets.emit(file, "sid_strings_offsets")
336
337        out()
338        out('#endif')
339
340
341def main():
342    # Parse PKT3 types
343    with open(sys.argv[1], 'r') as filp:
344        packets = parse_packet3(filp)
345
346    # Register database parse
347    regdb = None
348    for filename in sys.argv[2:]:
349        with open(filename, 'r') as filp:
350            try:
351                db = RegisterDatabase.from_json(json.load(filp))
352                if regdb is None:
353                    regdb = db
354                else:
355                    regdb.update(db)
356            except json.JSONDecodeError as e:
357                print('Error reading {}'.format(sys.argv[1]), file=sys.stderr)
358                raise
359
360    # The ac_debug code only distinguishes by chip_class
361    regdb.merge_chips(['gfx8', 'fiji', 'stoney'], 'gfx8')
362
363    # Write it all out
364    w = TableWriter()
365    w.write(regdb, packets)
366
367if __name__ == '__main__':
368    main()
369
370# kate: space-indent on; indent-width 4; replace-tabs on;
371