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