• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from __future__ import absolute_import, division, print_function, 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"""
29Create the (combined) register header from register JSON. Use --help for usage.
30"""
31
32import argparse
33from collections import defaultdict
34import itertools
35import json
36import re
37import sys
38
39from regdb import Object, RegisterDatabase, deduplicate_enums, deduplicate_register_types
40
41
42######### BEGIN HARDCODED CONFIGURATION
43
44# Chips are sorted chronologically
45CHIPS = [
46    Object(name='gfx6', disambiguation='GFX6'),
47    Object(name='gfx7', disambiguation='GFX7'),
48    Object(name='gfx8', disambiguation='GFX8'),
49    Object(name='gfx81', disambiguation='GFX81'),
50    Object(name='gfx9', disambiguation='GFX9'),
51    Object(name='gfx10', disambiguation='GFX10'),
52    Object(name='gfx103', disambiguation='GFX103'),
53]
54
55######### END HARDCODED CONFIGURATION
56
57def get_chip_index(chip):
58    """
59    Given a chip name, return its index in the global CHIPS list.
60    """
61    return next(idx for idx, obj in enumerate(CHIPS) if obj.name == chip)
62
63def get_disambiguation_suffix(chips):
64    """
65    Disambiguation suffix to be used for an enum entry or field name that
66    is supported in the given set of chips.
67    """
68    oldest_chip_index = min([get_chip_index(chip) for chip in chips])
69    return CHIPS[oldest_chip_index].disambiguation
70
71def get_chips_comment(chips, parent=None):
72    """
73    Generate a user-friendly comment describing the given set of chips.
74
75    The return value may be None, if such a comment is deemed unnecessary.
76
77    parent is an optional set of chips supporting a parent structure, e.g.
78    where chips may be the set of chips supporting a specific enum value,
79    parent would be the set of chips supporting the field containing the enum,
80    the idea being that no comment is necessary if all chips that support the
81    parent also support the child.
82    """
83    chipflags = [chip.name in chips for chip in CHIPS]
84    if all(chipflags):
85        return None
86
87    if parent is not None:
88        parentflags = [chip.name in parent for chip in CHIPS]
89        if all(childflag or not parentflag for childflag, parentflag in zip(chipflags, parentflags)):
90            return None
91
92    prefix = 0
93    for idx, chip, flag in zip(itertools.count(), CHIPS, chipflags):
94        if not flag:
95            break
96        prefix = idx + 1
97
98    suffix = len(CHIPS)
99    for idx, chip, flag in zip(itertools.count(), reversed(CHIPS), reversed(chipflags)):
100        if not flag:
101            break
102        suffix = len(CHIPS) - idx - 1
103
104    comment = []
105    if prefix > 0:
106        comment.append('<= {0}'.format(CHIPS[prefix - 1].name))
107    for chip, flag in zip(CHIPS[prefix:suffix], chipflags[prefix:suffix]):
108        if flag:
109            comment.append(chip.name)
110    if suffix < len(CHIPS):
111        comment.append('>= {0}'.format(CHIPS[suffix].name))
112
113    return ', '.join(comment)
114
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                    conflict = False
207                    for type_ref in prev.type_refs:
208                        for field in regdb.register_type(type_ref).fields:
209                            # The only possible conflict is for a prev field
210                            # that starts at a higher bit.
211                            if (field.bits[0] > line.bits[0] and
212                                field.bits[0] <= line.bits[1]):
213                                conflict = True
214                                break
215                        if conflict:
216                            break
217                    if conflict:
218                        continue
219
220                prev.bits[1] = max(prev.bits[1], line.bits[1])
221                prev.chips.update(line.chips)
222                prev.type_refs.update(line.type_refs)
223                prev.enum_refs.update(line.enum_refs)
224                merged = True
225                break
226            if not merged:
227                self.field_lines.append(line)
228
229        # Merge value lines
230        lines = self.value_lines
231        lines.sort(key=lambda line: (line.address, line.name))
232
233        self.value_lines = []
234        for line in lines:
235            for prev in reversed(self.value_lines):
236                if prev.address == line.address and prev.name == line.name and\
237                   prev.enumentry.value == line.enumentry.value:
238                    prev.chips.update(line.chips)
239                    prev.enum_refs.update(line.enum_refs)
240                    break
241            else:
242                self.value_lines.append(line)
243
244        # Disambiguate field and value lines
245        for idx, line in enumerate(self.field_lines):
246            prev = self.field_lines[idx - 1] if idx > 0 else None
247            next = self.field_lines[idx + 1] if idx + 1 < len(self.field_lines) else None
248            if (prev and prev.address == line.address and prev.field.name == line.field.name) or\
249               (next and next.address == line.address and next.field.name == line.field.name):
250                line.name += '_' + get_disambiguation_suffix(line.chips)
251
252        for idx, line in enumerate(self.value_lines):
253            prev = self.value_lines[idx - 1] if idx > 0 else None
254            next = self.value_lines[idx + 1] if idx + 1 < len(self.value_lines) else None
255            if (prev and prev.address == line.address and prev.enumentry.name == line.enumentry.name) or\
256               (next and next.address == line.address and next.enumentry.name == line.enumentry.name):
257                line.name += '_' + get_disambiguation_suffix(line.chips)
258
259    def print(self, filp, sort='address'):
260        """
261        Print out the entire register header.
262        """
263        if sort == 'address':
264            self.register_lines.sort(key=lambda line: (line.address, line.name))
265        else:
266            assert sort == 'name'
267            self.register_lines.sort(key=lambda line: (line.name, line.address))
268
269        # Collect and sort field lines by address
270        field_lines_by_address = defaultdict(list)
271        for line in self.field_lines:
272            field_lines_by_address[line.address].append(line)
273        for field_lines in field_lines_by_address.values():
274            if sort == 'address':
275                field_lines.sort(key=lambda line: (line.bits[0], line.name))
276            else:
277                field_lines.sort(key=lambda line: (line.name, line.bits[0]))
278
279        # Collect and sort value lines by address
280        value_lines_by_address = defaultdict(list)
281        for line in self.value_lines:
282            value_lines_by_address[line.address].append(line)
283        for value_lines in value_lines_by_address.values():
284            if sort == 'address':
285                value_lines.sort(key=lambda line: (line.enumentry.value, line.name))
286            else:
287                value_lines.sort(key=lambda line: (line.name, line.enumentry.value))
288
289        print('/* Automatically generated by amd/registers/makeregheader.py */\n', file=filp)
290        print(file=filp)
291        print(COPYRIGHT.strip(), file=filp)
292        print(file=filp)
293
294        if self.guard:
295            print('#ifndef {self.guard}'.format(**locals()), file=filp)
296            print('#define {self.guard}\n'.format(**locals()), file=filp)
297
298        for register_line in self.register_lines:
299            comment = get_chips_comment(register_line.chips)
300
301            address = '{0:X}'.format(register_line.address)
302            address = address.rjust(3 if register_line.regmap.map.to == 'pkt3' else 6, '0')
303
304            define_name = 'R_{address}_{register_line.name}'.format(**locals()).ljust(63)
305            comment = ' /* {0} */'.format(comment) if comment else ''
306            print('#define {define_name} 0x{address}{comment}'.format(**locals()), file=filp)
307
308            field_lines = field_lines_by_address[register_line.address]
309            field_idx = 0
310            while field_idx < len(field_lines):
311                field_line = field_lines[field_idx]
312
313                if field_line.type_refs.isdisjoint(register_line.type_refs):
314                    field_idx += 1
315                    continue
316                del field_lines[field_idx]
317
318                comment = get_chips_comment(field_line.chips, register_line.chips)
319
320                mask = (1 << (field_line.bits[1] - field_line.bits[0] + 1)) - 1
321                define_name = '_{address}_{field_line.name}(x)'.format(**locals()).ljust(58)
322                comment = ' /* {0} */'.format(comment) if comment else ''
323                print(
324                    '#define   S{define_name} (((unsigned)(x) & 0x{mask:X}) << {field_line.bits[0]}){comment}'
325                    .format(**locals()), file=filp)
326                print('#define   G{define_name} (((x) >> {field_line.bits[0]}) & 0x{mask:X})'
327                         .format(**locals()), file=filp)
328
329                complement = ((1 << 32) - 1) ^ (mask << field_line.bits[0])
330                define_name = '_{address}_{field_line.name}'.format(**locals()).ljust(58)
331                print('#define   C{define_name} 0x{complement:08X}'
332                         .format(**locals()), file=filp)
333
334                value_lines = value_lines_by_address[register_line.address]
335                value_idx = 0
336                while value_idx < len(value_lines):
337                    value_line = value_lines[value_idx]
338
339                    if value_line.enum_refs.isdisjoint(field_line.enum_refs):
340                        value_idx += 1
341                        continue
342                    del value_lines[value_idx]
343
344                    comment = get_chips_comment(value_line.chips, field_line.chips)
345
346                    define_name = 'V_{address}_{value_line.name}'.format(**locals()).ljust(55)
347                    comment = ' /* {0} */'.format(comment) if comment else ''
348                    print('#define     {define_name} {value_line.enumentry.value}{comment}'
349                          .format(**locals()), file=filp)
350
351        if self.guard:
352            print('\n#endif // {self.guard}'.format(**locals()), file=filp)
353
354
355def main():
356    parser = argparse.ArgumentParser()
357    parser.add_argument('--chip', dest='chips', type=str, nargs='*',
358                        help='Chip for which to generate the header (all chips if unspecified)')
359    parser.add_argument('--sort', choices=['name', 'address'], default='address',
360                        help='Sort key for registers, fields, and enum values')
361    parser.add_argument('--guard', type=str, help='Name of the #include guard')
362    parser.add_argument('files', metavar='FILE', type=str, nargs='+',
363                        help='Register database file')
364    args = parser.parse_args()
365
366    regdb = None
367    for filename in args.files:
368        with open(filename, 'r') as filp:
369            db = RegisterDatabase.from_json(json.load(filp))
370            if regdb is None:
371                regdb = db
372            else:
373                regdb.update(db)
374
375    deduplicate_enums(regdb)
376    deduplicate_register_types(regdb)
377
378    w = HeaderWriter(regdb, guard=args.guard)
379    w.print(sys.stdout, sort=args.sort)
380
381
382if __name__ == '__main__':
383    main()
384
385# kate: space-indent on; indent-width 4; replace-tabs on;
386