diff options
Diffstat (limited to 'src/amd/registers/makeregheader.py')
-rw-r--r-- | src/amd/registers/makeregheader.py | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/src/amd/registers/makeregheader.py b/src/amd/registers/makeregheader.py new file mode 100644 index 00000000000..006fee3eb9e --- /dev/null +++ b/src/amd/registers/makeregheader.py @@ -0,0 +1,384 @@ +from __future__ import absolute_import, division, print_function, unicode_literals + +COPYRIGHT = ''' +/* + * Copyright 2015-2019 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +''' +""" +Create the (combined) register header from register JSON. Use --help for usage. +""" + +import argparse +from collections import defaultdict +import itertools +import json +import re +import sys + +from regdb import Object, RegisterDatabase, deduplicate_enums, deduplicate_register_types + + +######### BEGIN HARDCODED CONFIGURATION + +# Chips are sorted chronologically +CHIPS = [ + Object(name='si', disambiguation='GFX6'), + Object(name='cik', disambiguation='GFX6'), + Object(name='vi', disambiguation='GFX6'), + Object(name='fiji', disambiguation='GFX6'), + Object(name='stoney', disambiguation='GFX6'), + Object(name='gfx9', disambiguation='GFX9'), +] + +######### END HARDCODED CONFIGURATION + +def get_chip_index(chip): + """ + Given a chip name, return its index in the global CHIPS list. + """ + return next(idx for idx, obj in enumerate(CHIPS) if obj.name == chip) + +def get_disambiguation_suffix(chips): + """ + Disambiguation suffix to be used for an enum entry or field name that + is supported in the given set of chips. + """ + oldest_chip_index = min([get_chip_index(chip) for chip in chips]) + return CHIPS[oldest_chip_index].disambiguation + +def get_chips_comment(chips, parent=None): + """ + Generate a user-friendly comment describing the given set of chips. + + The return value may be None, if such a comment is deemed unnecessary. + + parent is an optional set of chips supporting a parent structure, e.g. + where chips may be the set of chips supporting a specific enum value, + parent would be the set of chips supporting the field containing the enum, + the idea being that no comment is necessary if all chips that support the + parent also support the child. + """ + chipflags = [chip.name in chips for chip in CHIPS] + if all(chipflags): + return None + + if parent is not None: + parentflags = [chip.name in parent for chip in CHIPS] + if all(childflag or not parentflag for childflag, parentflag in zip(chipflags, parentflags)): + return None + + prefix = 0 + for idx, chip, flag in zip(itertools.count(), CHIPS, chipflags): + if not flag: + break + prefix = idx + 1 + + suffix = len(CHIPS) + for idx, chip, flag in zip(itertools.count(), reversed(CHIPS), reversed(chipflags)): + if not flag: + break + suffix = len(CHIPS) - idx - 1 + + comment = [] + if prefix > 0: + comment.append('<= {0}'.format(CHIPS[prefix - 1].name)) + for chip, flag in zip(CHIPS[prefix:suffix], chipflags[prefix:suffix]): + if flag: + comment.append(chip.name) + if suffix < len(CHIPS): + comment.append('>= {0}'.format(CHIPS[suffix].name)) + + return ', '.join(comment) + + +class HeaderWriter(object): + def __init__(self, regdb, guard=None): + self.guard = guard + + # The following contain: Object(address, chips, name, regmap/field/enumentry) + self.register_lines = [] + self.field_lines = [] + self.value_lines = [] + + regtype_emit = defaultdict(set) + enum_emit = defaultdict(set) + + for regmap in regdb.register_mappings(): + type_ref = getattr(regmap, 'type_ref', None) + self.register_lines.append(Object( + address=regmap.map.at, + chips=set(regmap.chips), + name=regmap.name, + regmap=regmap, + type_refs=set([type_ref]) if type_ref else set(), + )) + + basename = re.sub(r'[0-9]+', '', regmap.name) + key = '{type_ref}::{basename}'.format(**locals()) + if type_ref is not None and regtype_emit[key].isdisjoint(regmap.chips): + regtype_emit[key].update(regmap.chips) + + regtype = regdb.register_type(type_ref) + for field in regtype.fields: + if field.name == 'RESERVED': + continue + + enum_ref = getattr(field, 'enum_ref', None) + self.field_lines.append(Object( + address=regmap.map.at, + chips=set(regmap.chips), + name=field.name, + field=field, + bits=field.bits[:], + type_refs=set([type_ref]) if type_ref else set(), + enum_refs=set([enum_ref]) if enum_ref else set(), + )) + + key = '{type_ref}::{basename}::{enum_ref}'.format(**locals()) + if enum_ref is not None and enum_emit[key].isdisjoint(regmap.chips): + enum_emit[key].update(regmap.chips) + + enum = regdb.enum(enum_ref) + for entry in enum.entries: + self.value_lines.append(Object( + address=regmap.map.at, + chips=set(regmap.chips), + name=entry.name, + enumentry=entry, + enum_refs=set([enum_ref]) if enum_ref else set(), + )) + + # Merge register lines + lines = self.register_lines + lines.sort(key=lambda line: (line.address, line.name)) + + self.register_lines = [] + for line in lines: + prev = self.register_lines[-1] if self.register_lines else None + if prev and prev.address == line.address and prev.name == line.name: + prev.chips.update(line.chips) + prev.type_refs.update(line.type_refs) + continue + self.register_lines.append(line) + + # Merge field lines + lines = self.field_lines + lines.sort(key=lambda line: (line.address, line.name)) + + self.field_lines = [] + for line in lines: + merged = False + for prev in reversed(self.field_lines): + if prev.address != line.address or prev.name != line.name: + break + + # Can merge fields if they have the same starting bit and the + # range of the field as intended by the current line does not + # conflict with any of the regtypes covered by prev. + if prev.bits[0] != line.bits[0]: + continue + + if prev.bits[1] < line.bits[1]: + # Current line's field extends beyond the range of prev. + # Need to check for conflicts + conflict = False + for type_ref in prev.type_refs: + for field in regdb.register_type(type_ref).fields: + # The only possible conflict is for a prev field + # that starts at a higher bit. + if (field.bits[0] > line.bits[0] and + field.bits[0] <= line.bits[1]): + conflict = True + break + if conflict: + break + if conflict: + continue + + prev.bits[1] = max(prev.bits[1], line.bits[1]) + prev.chips.update(line.chips) + prev.type_refs.update(line.type_refs) + prev.enum_refs.update(line.enum_refs) + merged = True + break + if not merged: + self.field_lines.append(line) + + # Merge value lines + lines = self.value_lines + lines.sort(key=lambda line: (line.address, line.name)) + + self.value_lines = [] + for line in lines: + for prev in reversed(self.value_lines): + if prev.address == line.address and prev.name == line.name and\ + prev.enumentry.value == line.enumentry.value: + prev.chips.update(line.chips) + prev.enum_refs.update(line.enum_refs) + break + else: + self.value_lines.append(line) + + # Disambiguate field and value lines + for idx, line in enumerate(self.field_lines): + prev = self.field_lines[idx - 1] if idx > 0 else None + next = self.field_lines[idx + 1] if idx + 1 < len(self.field_lines) else None + if (prev and prev.address == line.address and prev.field.name == line.field.name) or\ + (next and next.address == line.address and next.field.name == line.field.name): + line.name += '_' + get_disambiguation_suffix(line.chips) + + for idx, line in enumerate(self.value_lines): + prev = self.value_lines[idx - 1] if idx > 0 else None + next = self.value_lines[idx + 1] if idx + 1 < len(self.value_lines) else None + if (prev and prev.address == line.address and prev.enumentry.name == line.enumentry.name) or\ + (next and next.address == line.address and next.enumentry.name == line.enumentry.name): + line.name += '_' + get_disambiguation_suffix(line.chips) + + def print(self, filp, sort='address'): + """ + Print out the entire register header. + """ + if sort == 'address': + self.register_lines.sort(key=lambda line: (line.address, line.name)) + else: + assert sort == 'name' + self.register_lines.sort(key=lambda line: (line.name, line.address)) + + # Collect and sort field lines by address + field_lines_by_address = defaultdict(list) + for line in self.field_lines: + field_lines_by_address[line.address].append(line) + for field_lines in field_lines_by_address.values(): + if sort == 'address': + field_lines.sort(key=lambda line: (line.bits[0], line.name)) + else: + field_lines.sort(key=lambda line: (line.name, line.bits[0])) + + # Collect and sort value lines by address + value_lines_by_address = defaultdict(list) + for line in self.value_lines: + value_lines_by_address[line.address].append(line) + for value_lines in value_lines_by_address.values(): + if sort == 'address': + value_lines.sort(key=lambda line: (line.enumentry.value, line.name)) + else: + value_lines.sort(key=lambda line: (line.name, line.enumentry.value)) + + print('/* Automatically generated by amd/registers/makeregheader.py */\n', file=filp) + print(file=filp) + print(COPYRIGHT.strip(), file=filp) + print(file=filp) + + if self.guard: + print('#ifndef {self.guard}'.format(**locals()), file=filp) + print('#define {self.guard}\n'.format(**locals()), file=filp) + + for register_line in self.register_lines: + comment = get_chips_comment(register_line.chips) + + address = '{0:X}'.format(register_line.address) + address = address.rjust(3 if register_line.regmap.map.to == 'pkt3' else 6, '0') + + define_name = 'R_{address}_{register_line.name}'.format(**locals()).ljust(63) + comment = ' /* {0} */'.format(comment) if comment else '' + print('#define {define_name} 0x{address}{comment}'.format(**locals()), file=filp) + + field_lines = field_lines_by_address[register_line.address] + field_idx = 0 + while field_idx < len(field_lines): + field_line = field_lines[field_idx] + + if field_line.type_refs.isdisjoint(register_line.type_refs): + field_idx += 1 + continue + del field_lines[field_idx] + + comment = get_chips_comment(field_line.chips, register_line.chips) + + mask = (1 << (field_line.bits[1] - field_line.bits[0] + 1)) - 1 + define_name = '_{address}_{field_line.name}(x)'.format(**locals()).ljust(58) + comment = ' /* {0} */'.format(comment) if comment else '' + print( + '#define S{define_name} (((unsigned)(x) & 0x{mask:X}) << {field_line.bits[0]}){comment}' + .format(**locals()), file=filp) + print('#define G{define_name} (((x) >> {field_line.bits[0]}) & 0x{mask:X})' + .format(**locals()), file=filp) + + complement = ((1 << 32) - 1) ^ (mask << field_line.bits[0]) + define_name = '_{address}_{field_line.name}'.format(**locals()).ljust(58) + print('#define C{define_name} 0x{complement:08X}' + .format(**locals()), file=filp) + + value_lines = value_lines_by_address[register_line.address] + value_idx = 0 + while value_idx < len(value_lines): + value_line = value_lines[value_idx] + + if value_line.enum_refs.isdisjoint(field_line.enum_refs): + value_idx += 1 + continue + del value_lines[value_idx] + + comment = get_chips_comment(value_line.chips, field_line.chips) + + define_name = 'V_{address}_{value_line.name}'.format(**locals()).ljust(55) + comment = ' /* {0} */'.format(comment) if comment else '' + print('#define {define_name} {value_line.enumentry.value}{comment}' + .format(**locals()), file=filp) + + if self.guard: + print('\n#endif // {self.guard}'.format(**locals()), file=filp) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--chip', dest='chips', type=str, nargs='*', + help='Chip for which to generate the header (all chips if unspecified)') + parser.add_argument('--sort', choices=['name', 'address'], default='address', + help='Sort key for registers, fields, and enum values') + parser.add_argument('--guard', type=str, help='Name of the #include guard') + parser.add_argument('files', metavar='FILE', type=str, nargs='+', + help='Register database file') + args = parser.parse_args() + + regdb = None + for filename in args.files: + with open(filename, 'r') as filp: + db = RegisterDatabase.from_json(json.load(filp)) + if regdb is None: + regdb = db + else: + regdb.update(db) + + deduplicate_enums(regdb) + deduplicate_register_types(regdb) + + w = HeaderWriter(regdb, guard=args.guard) + w.print(sys.stdout, sort=args.sort) + + +if __name__ == '__main__': + main() + +# kate: space-indent on; indent-width 4; replace-tabs on; |