xref: /aosp_15_r20/external/mesa3d/src/amd/registers/makeregheader.py (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1COPYRIGHT = '''
2/*
3 * Copyright 2015-2019 Advanced Micro Devices, Inc.
4 *
5 * SPDX-License-Identifier: MIT
6 */
7'''
8"""
9Create the (combined) register header from register JSON. Use --help for usage.
10"""
11
12import argparse
13from collections import defaultdict
14import itertools
15import json
16import re
17import sys
18
19from regdb import Object, RegisterDatabase, deduplicate_enums, deduplicate_register_types
20
21
22######### BEGIN HARDCODED CONFIGURATION
23
24# Chips are sorted chronologically
25CHIPS = [
26    Object(name='gfx6', disambiguation='GFX6'),
27    Object(name='gfx7', disambiguation='GFX7'),
28    Object(name='gfx8', disambiguation='GFX8'),
29    Object(name='gfx81', disambiguation='GFX81'),
30    Object(name='gfx9', disambiguation='GFX9'),
31    Object(name='gfx940', disambiguation='GFX940'),
32    Object(name='gfx10', disambiguation='GFX10'),
33    Object(name='gfx103', disambiguation='GFX103'),
34    Object(name='gfx11', disambiguation='GFX11'),
35    Object(name='gfx115', disambiguation='GFX115'),
36    Object(name='gfx12', disambiguation='GFX12'),
37]
38
39######### END HARDCODED CONFIGURATION
40
41def get_chip_index(chip):
42    """
43    Given a chip name, return its index in the global CHIPS list.
44    """
45    return next(idx for idx, obj in enumerate(CHIPS) if obj.name == chip)
46
47def get_disambiguation_suffix(chips):
48    """
49    Disambiguation suffix to be used for an enum entry or field name that
50    is supported in the given set of chips.
51    """
52    oldest_chip_index = min([get_chip_index(chip) for chip in chips])
53    return CHIPS[oldest_chip_index].disambiguation
54
55def get_chips_comment(chips, parent=None):
56    """
57    Generate a user-friendly comment describing the given set of chips.
58
59    The return value may be None, if such a comment is deemed unnecessary.
60
61    parent is an optional set of chips supporting a parent structure, e.g.
62    where chips may be the set of chips supporting a specific enum value,
63    parent would be the set of chips supporting the field containing the enum,
64    the idea being that no comment is necessary if all chips that support the
65    parent also support the child.
66    """
67    chipflags = [chip.name in chips for chip in CHIPS]
68    if all(chipflags):
69        return None
70
71    if parent is not None:
72        parentflags = [chip.name in parent for chip in CHIPS]
73        if all(childflag or not parentflag for childflag, parentflag in zip(chipflags, parentflags)):
74            return None
75
76    prefix = 0
77    for idx, chip, flag in zip(itertools.count(), CHIPS, chipflags):
78        if not flag:
79            break
80        prefix = idx + 1
81
82    suffix = len(CHIPS)
83    for idx, chip, flag in zip(itertools.count(), reversed(CHIPS), reversed(chipflags)):
84        if not flag:
85            break
86        suffix = len(CHIPS) - idx - 1
87
88    comment = []
89    if prefix > 0:
90        comment.append('<= {0}'.format(CHIPS[prefix - 1].name))
91    for chip, flag in zip(CHIPS[prefix:suffix], chipflags[prefix:suffix]):
92        if flag:
93            comment.append(chip.name)
94    if suffix < len(CHIPS):
95        comment.append('>= {0}'.format(CHIPS[suffix].name))
96
97    return ', '.join(comment)
98
99def detect_conflict(regdb, field_in_type1, field_in_type2):
100    """
101    Returns False if field_in_type1 and field_in_type2 can be merged
102    into a single field = if writing to field_in_type1 bits won't
103    overwrite adjacent fields in type2, and the other way around.
104    """
105    for idx, type_refs in enumerate([field_in_type1.type_refs, field_in_type2.type_refs]):
106        ref = field_in_type2 if idx == 0 else field_in_type1
107        for type_ref in type_refs:
108            for field in regdb.register_type(type_ref).fields:
109                # If a different field in the other type starts in
110                # the tested field's bits[0, 1] interval
111                if (field.bits[0] > ref.bits[0] and
112                    field.bits[0] <= ref.bits[1]):
113                    return True
114
115    return False
116
117class HeaderWriter(object):
118    def __init__(self, regdb, guard=None):
119        self.guard = guard
120
121        # The following contain: Object(address, chips, name, regmap/field/enumentry)
122        self.register_lines = []
123        self.field_lines = []
124        self.value_lines = []
125
126        regtype_emit = defaultdict(set)
127        enum_emit = defaultdict(set)
128
129        for regmap in regdb.register_mappings():
130            type_ref = getattr(regmap, 'type_ref', None)
131            self.register_lines.append(Object(
132                address=regmap.map.at,
133                chips=set(regmap.chips),
134                name=regmap.name,
135                regmap=regmap,
136                type_refs=set([type_ref]) if type_ref else set(),
137            ))
138
139            basename = re.sub(r'[0-9]+', '', regmap.name)
140            key = '{type_ref}::{basename}'.format(**locals())
141            if type_ref is not None and regtype_emit[key].isdisjoint(regmap.chips):
142                regtype_emit[key].update(regmap.chips)
143
144                regtype = regdb.register_type(type_ref)
145                for field in regtype.fields:
146                    if field.name == 'RESERVED':
147                        continue
148
149                    enum_ref = getattr(field, 'enum_ref', None)
150                    self.field_lines.append(Object(
151                        address=regmap.map.at,
152                        chips=set(regmap.chips),
153                        name=field.name,
154                        field=field,
155                        bits=field.bits[:],
156                        type_refs=set([type_ref]) if type_ref else set(),
157                        enum_refs=set([enum_ref]) if enum_ref else set(),
158                    ))
159
160                    key = '{type_ref}::{basename}::{enum_ref}'.format(**locals())
161                    if enum_ref is not None and enum_emit[key].isdisjoint(regmap.chips):
162                        enum_emit[key].update(regmap.chips)
163
164                        enum = regdb.enum(enum_ref)
165                        for entry in enum.entries:
166                            self.value_lines.append(Object(
167                                address=regmap.map.at,
168                                chips=set(regmap.chips),
169                                name=entry.name,
170                                enumentry=entry,
171                                enum_refs=set([enum_ref]) if enum_ref else set(),
172                            ))
173
174        # Merge register lines
175        lines = self.register_lines
176        lines.sort(key=lambda line: (line.address, line.name))
177
178        self.register_lines = []
179        for line in lines:
180            prev = self.register_lines[-1] if self.register_lines else None
181            if prev and prev.address == line.address and prev.name == line.name:
182                prev.chips.update(line.chips)
183                prev.type_refs.update(line.type_refs)
184                continue
185            self.register_lines.append(line)
186
187        # Merge field lines
188        lines = self.field_lines
189        lines.sort(key=lambda line: (line.address, line.name))
190
191        self.field_lines = []
192        for line in lines:
193            merged = False
194            for prev in reversed(self.field_lines):
195                if prev.address != line.address or prev.name != line.name:
196                    break
197
198                # Can merge fields if they have the same starting bit and the
199                # range of the field as intended by the current line does not
200                # conflict with any of the regtypes covered by prev.
201                if prev.bits[0] != line.bits[0]:
202                    continue
203
204                if prev.bits[1] != line.bits[1]:
205                    # Current line's field extends beyond the range of prev.
206                    # Need to check for conflicts
207                    if detect_conflict(regdb, prev, line):
208                        continue
209
210                prev.bits[1] = max(prev.bits[1], line.bits[1])
211                prev.chips.update(line.chips)
212                prev.type_refs.update(line.type_refs)
213                prev.enum_refs.update(line.enum_refs)
214                merged = True
215                break
216            if not merged:
217                self.field_lines.append(line)
218
219        # Merge value lines
220        lines = self.value_lines
221        lines.sort(key=lambda line: (line.address, line.name))
222
223        self.value_lines = []
224        for line in lines:
225            for prev in reversed(self.value_lines):
226                if prev.address == line.address and prev.name == line.name and\
227                   prev.enumentry.value == line.enumentry.value:
228                    prev.chips.update(line.chips)
229                    prev.enum_refs.update(line.enum_refs)
230                    break
231            else:
232                self.value_lines.append(line)
233
234        # Disambiguate field and value lines
235        for idx, line in enumerate(self.field_lines):
236            prev = self.field_lines[idx - 1] if idx > 0 else None
237            next = self.field_lines[idx + 1] if idx + 1 < len(self.field_lines) else None
238            if (prev and prev.address == line.address and prev.field.name == line.field.name) or\
239               (next and next.address == line.address and next.field.name == line.field.name):
240                line.name += '_' + get_disambiguation_suffix(line.chips)
241
242        for idx, line in enumerate(self.value_lines):
243            prev = self.value_lines[idx - 1] if idx > 0 else None
244            next = self.value_lines[idx + 1] if idx + 1 < len(self.value_lines) else None
245            if (prev and prev.address == line.address and prev.enumentry.name == line.enumentry.name) or\
246               (next and next.address == line.address and next.enumentry.name == line.enumentry.name):
247                line.name += '_' + get_disambiguation_suffix(line.chips)
248
249    def print(self, filp, sort='address'):
250        """
251        Print out the entire register header.
252        """
253        if sort == 'address':
254            self.register_lines.sort(key=lambda line: (line.address, line.name))
255        else:
256            assert sort == 'name'
257            self.register_lines.sort(key=lambda line: (line.name, line.address))
258
259        # Collect and sort field lines by address
260        field_lines_by_address = defaultdict(list)
261        for line in self.field_lines:
262            field_lines_by_address[line.address].append(line)
263        for field_lines in field_lines_by_address.values():
264            if sort == 'address':
265                field_lines.sort(key=lambda line: (line.bits[0], line.name))
266            else:
267                field_lines.sort(key=lambda line: (line.name, line.bits[0]))
268
269        # Collect and sort value lines by address
270        value_lines_by_address = defaultdict(list)
271        for line in self.value_lines:
272            value_lines_by_address[line.address].append(line)
273        for value_lines in value_lines_by_address.values():
274            if sort == 'address':
275                value_lines.sort(key=lambda line: (line.enumentry.value, line.name))
276            else:
277                value_lines.sort(key=lambda line: (line.name, line.enumentry.value))
278
279        print('/* Automatically generated by amd/registers/makeregheader.py */\n', file=filp)
280        print(file=filp)
281        print(COPYRIGHT.strip(), file=filp)
282        print(file=filp)
283
284        if self.guard:
285            print('#ifndef {self.guard}'.format(**locals()), file=filp)
286            print('#define {self.guard}\n'.format(**locals()), file=filp)
287
288        for register_line in self.register_lines:
289            comment = get_chips_comment(register_line.chips)
290
291            address = '{0:X}'.format(register_line.address)
292            address = address.rjust(3 if register_line.regmap.map.to == 'pkt3' else 6, '0')
293
294            define_name = 'R_{address}_{register_line.name}'.format(**locals()).ljust(63)
295            comment = ' /* {0} */'.format(comment) if comment else ''
296            print('#define {define_name} 0x{address}{comment}'.format(**locals()), file=filp)
297
298            field_lines = field_lines_by_address[register_line.address]
299            field_idx = 0
300            while field_idx < len(field_lines):
301                field_line = field_lines[field_idx]
302
303                if field_line.type_refs.isdisjoint(register_line.type_refs):
304                    field_idx += 1
305                    continue
306                del field_lines[field_idx]
307
308                comment = get_chips_comment(field_line.chips, register_line.chips)
309
310                mask = (1 << (field_line.bits[1] - field_line.bits[0] + 1)) - 1
311                define_name = '_{address}_{field_line.name}(x)'.format(**locals()).ljust(58)
312                comment = ' /* {0} */'.format(comment) if comment else ''
313                print(
314                    '#define   S{define_name} (((unsigned)(x) & 0x{mask:X}) << {field_line.bits[0]}){comment}'
315                    .format(**locals()), file=filp)
316                print('#define   G{define_name} (((x) >> {field_line.bits[0]}) & 0x{mask:X})'
317                         .format(**locals()), file=filp)
318
319                complement = ((1 << 32) - 1) ^ (mask << field_line.bits[0])
320                define_name = '_{address}_{field_line.name}'.format(**locals()).ljust(58)
321                print('#define   C{define_name} 0x{complement:08X}'
322                         .format(**locals()), file=filp)
323
324                value_lines = value_lines_by_address[register_line.address]
325                value_idx = 0
326                while value_idx < len(value_lines):
327                    value_line = value_lines[value_idx]
328
329                    if value_line.enum_refs.isdisjoint(field_line.enum_refs):
330                        value_idx += 1
331                        continue
332                    del value_lines[value_idx]
333
334                    comment = get_chips_comment(value_line.chips, field_line.chips)
335
336                    define_name = 'V_{address}_{value_line.name}'.format(**locals()).ljust(55)
337                    comment = ' /* {0} */'.format(comment) if comment else ''
338                    print('#define     {define_name} {value_line.enumentry.value}{comment}'
339                          .format(**locals()), file=filp)
340
341        if self.guard:
342            print('\n#endif // {self.guard}'.format(**locals()), file=filp)
343
344
345def main():
346    parser = argparse.ArgumentParser()
347    parser.add_argument('--chip', dest='chips', type=str, nargs='*',
348                        help='Chip for which to generate the header (all chips if unspecified)')
349    parser.add_argument('--sort', choices=['name', 'address'], default='address',
350                        help='Sort key for registers, fields, and enum values')
351    parser.add_argument('--guard', type=str, help='Name of the #include guard')
352    parser.add_argument('files', metavar='FILE', type=str, nargs='+',
353                        help='Register database file')
354    args = parser.parse_args()
355
356    regdb = None
357    for filename in args.files:
358        with open(filename, 'r') as filp:
359            db = RegisterDatabase.from_json(json.load(filp))
360            if regdb is None:
361                regdb = db
362            else:
363                regdb.update(db)
364
365    deduplicate_enums(regdb)
366    deduplicate_register_types(regdb)
367
368    w = HeaderWriter(regdb, guard=args.guard)
369    w.print(sys.stdout, sort=args.sort)
370
371
372if __name__ == '__main__':
373    main()
374
375# kate: space-indent on; indent-width 4; replace-tabs on;
376