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