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