Path: blob/21.2-virgl/src/amd/registers/makeregheader.py
7097 views
from __future__ import absolute_import, division, print_function, unicode_literals12COPYRIGHT = '''3/*4* Copyright 2015-2019 Advanced Micro Devices, Inc.5*6* Permission is hereby granted, free of charge, to any person obtaining a7* copy of this software and associated documentation files (the "Software"),8* to deal in the Software without restriction, including without limitation9* on the rights to use, copy, modify, merge, publish, distribute, sub10* license, and/or sell copies of the Software, and to permit persons to whom11* the Software is furnished to do so, subject to the following conditions:12*13* The above copyright notice and this permission notice (including the next14* paragraph) shall be included in all copies or substantial portions of the15* Software.16*17* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR18* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,19* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL20* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,21* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR22* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE23* USE OR OTHER DEALINGS IN THE SOFTWARE.24*25*/26'''27"""28Create the (combined) register header from register JSON. Use --help for usage.29"""3031import argparse32from collections import defaultdict33import itertools34import json35import re36import sys3738from regdb import Object, RegisterDatabase, deduplicate_enums, deduplicate_register_types394041######### BEGIN HARDCODED CONFIGURATION4243# Chips are sorted chronologically44CHIPS = [45Object(name='gfx6', disambiguation='GFX6'),46Object(name='gfx7', disambiguation='GFX7'),47Object(name='gfx8', disambiguation='GFX8'),48Object(name='gfx81', disambiguation='GFX81'),49Object(name='gfx9', disambiguation='GFX9'),50Object(name='gfx10', disambiguation='GFX10'),51Object(name='gfx103', disambiguation='GFX103'),52]5354######### END HARDCODED CONFIGURATION5556def get_chip_index(chip):57"""58Given a chip name, return its index in the global CHIPS list.59"""60return next(idx for idx, obj in enumerate(CHIPS) if obj.name == chip)6162def get_disambiguation_suffix(chips):63"""64Disambiguation suffix to be used for an enum entry or field name that65is supported in the given set of chips.66"""67oldest_chip_index = min([get_chip_index(chip) for chip in chips])68return CHIPS[oldest_chip_index].disambiguation6970def get_chips_comment(chips, parent=None):71"""72Generate a user-friendly comment describing the given set of chips.7374The return value may be None, if such a comment is deemed unnecessary.7576parent is an optional set of chips supporting a parent structure, e.g.77where chips may be the set of chips supporting a specific enum value,78parent would be the set of chips supporting the field containing the enum,79the idea being that no comment is necessary if all chips that support the80parent also support the child.81"""82chipflags = [chip.name in chips for chip in CHIPS]83if all(chipflags):84return None8586if parent is not None:87parentflags = [chip.name in parent for chip in CHIPS]88if all(childflag or not parentflag for childflag, parentflag in zip(chipflags, parentflags)):89return None9091prefix = 092for idx, chip, flag in zip(itertools.count(), CHIPS, chipflags):93if not flag:94break95prefix = idx + 19697suffix = len(CHIPS)98for idx, chip, flag in zip(itertools.count(), reversed(CHIPS), reversed(chipflags)):99if not flag:100break101suffix = len(CHIPS) - idx - 1102103comment = []104if prefix > 0:105comment.append('<= {0}'.format(CHIPS[prefix - 1].name))106for chip, flag in zip(CHIPS[prefix:suffix], chipflags[prefix:suffix]):107if flag:108comment.append(chip.name)109if suffix < len(CHIPS):110comment.append('>= {0}'.format(CHIPS[suffix].name))111112return ', '.join(comment)113114def detect_conflict(regdb, field_in_type1, field_in_type2):115"""116Returns False if field_in_type1 and field_in_type2 can be merged117into a single field = if writing to field_in_type1 bits won't118overwrite adjacent fields in type2, and the other way around.119"""120for idx, type_refs in enumerate([field_in_type1.type_refs, field_in_type2.type_refs]):121ref = field_in_type2 if idx == 0 else field_in_type1122for type_ref in type_refs:123for field in regdb.register_type(type_ref).fields:124# If a different field in the other type starts in125# the tested field's bits[0, 1] interval126if (field.bits[0] > ref.bits[0] and127field.bits[0] <= ref.bits[1]):128return True129130return False131132class HeaderWriter(object):133def __init__(self, regdb, guard=None):134self.guard = guard135136# The following contain: Object(address, chips, name, regmap/field/enumentry)137self.register_lines = []138self.field_lines = []139self.value_lines = []140141regtype_emit = defaultdict(set)142enum_emit = defaultdict(set)143144for regmap in regdb.register_mappings():145type_ref = getattr(regmap, 'type_ref', None)146self.register_lines.append(Object(147address=regmap.map.at,148chips=set(regmap.chips),149name=regmap.name,150regmap=regmap,151type_refs=set([type_ref]) if type_ref else set(),152))153154basename = re.sub(r'[0-9]+', '', regmap.name)155key = '{type_ref}::{basename}'.format(**locals())156if type_ref is not None and regtype_emit[key].isdisjoint(regmap.chips):157regtype_emit[key].update(regmap.chips)158159regtype = regdb.register_type(type_ref)160for field in regtype.fields:161if field.name == 'RESERVED':162continue163164enum_ref = getattr(field, 'enum_ref', None)165self.field_lines.append(Object(166address=regmap.map.at,167chips=set(regmap.chips),168name=field.name,169field=field,170bits=field.bits[:],171type_refs=set([type_ref]) if type_ref else set(),172enum_refs=set([enum_ref]) if enum_ref else set(),173))174175key = '{type_ref}::{basename}::{enum_ref}'.format(**locals())176if enum_ref is not None and enum_emit[key].isdisjoint(regmap.chips):177enum_emit[key].update(regmap.chips)178179enum = regdb.enum(enum_ref)180for entry in enum.entries:181self.value_lines.append(Object(182address=regmap.map.at,183chips=set(regmap.chips),184name=entry.name,185enumentry=entry,186enum_refs=set([enum_ref]) if enum_ref else set(),187))188189# Merge register lines190lines = self.register_lines191lines.sort(key=lambda line: (line.address, line.name))192193self.register_lines = []194for line in lines:195prev = self.register_lines[-1] if self.register_lines else None196if prev and prev.address == line.address and prev.name == line.name:197prev.chips.update(line.chips)198prev.type_refs.update(line.type_refs)199continue200self.register_lines.append(line)201202# Merge field lines203lines = self.field_lines204lines.sort(key=lambda line: (line.address, line.name))205206self.field_lines = []207for line in lines:208merged = False209for prev in reversed(self.field_lines):210if prev.address != line.address or prev.name != line.name:211break212213# Can merge fields if they have the same starting bit and the214# range of the field as intended by the current line does not215# conflict with any of the regtypes covered by prev.216if prev.bits[0] != line.bits[0]:217continue218219if prev.bits[1] != line.bits[1]:220# Current line's field extends beyond the range of prev.221# Need to check for conflicts222if detect_conflict(regdb, prev, line):223continue224225prev.bits[1] = max(prev.bits[1], line.bits[1])226prev.chips.update(line.chips)227prev.type_refs.update(line.type_refs)228prev.enum_refs.update(line.enum_refs)229merged = True230break231if not merged:232self.field_lines.append(line)233234# Merge value lines235lines = self.value_lines236lines.sort(key=lambda line: (line.address, line.name))237238self.value_lines = []239for line in lines:240for prev in reversed(self.value_lines):241if prev.address == line.address and prev.name == line.name and\242prev.enumentry.value == line.enumentry.value:243prev.chips.update(line.chips)244prev.enum_refs.update(line.enum_refs)245break246else:247self.value_lines.append(line)248249# Disambiguate field and value lines250for idx, line in enumerate(self.field_lines):251prev = self.field_lines[idx - 1] if idx > 0 else None252next = self.field_lines[idx + 1] if idx + 1 < len(self.field_lines) else None253if (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):255line.name += '_' + get_disambiguation_suffix(line.chips)256257for idx, line in enumerate(self.value_lines):258prev = self.value_lines[idx - 1] if idx > 0 else None259next = self.value_lines[idx + 1] if idx + 1 < len(self.value_lines) else None260if (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):262line.name += '_' + get_disambiguation_suffix(line.chips)263264def print(self, filp, sort='address'):265"""266Print out the entire register header.267"""268if sort == 'address':269self.register_lines.sort(key=lambda line: (line.address, line.name))270else:271assert sort == 'name'272self.register_lines.sort(key=lambda line: (line.name, line.address))273274# Collect and sort field lines by address275field_lines_by_address = defaultdict(list)276for line in self.field_lines:277field_lines_by_address[line.address].append(line)278for field_lines in field_lines_by_address.values():279if sort == 'address':280field_lines.sort(key=lambda line: (line.bits[0], line.name))281else:282field_lines.sort(key=lambda line: (line.name, line.bits[0]))283284# Collect and sort value lines by address285value_lines_by_address = defaultdict(list)286for line in self.value_lines:287value_lines_by_address[line.address].append(line)288for value_lines in value_lines_by_address.values():289if sort == 'address':290value_lines.sort(key=lambda line: (line.enumentry.value, line.name))291else:292value_lines.sort(key=lambda line: (line.name, line.enumentry.value))293294print('/* Automatically generated by amd/registers/makeregheader.py */\n', file=filp)295print(file=filp)296print(COPYRIGHT.strip(), file=filp)297print(file=filp)298299if self.guard:300print('#ifndef {self.guard}'.format(**locals()), file=filp)301print('#define {self.guard}\n'.format(**locals()), file=filp)302303for register_line in self.register_lines:304comment = get_chips_comment(register_line.chips)305306address = '{0:X}'.format(register_line.address)307address = address.rjust(3 if register_line.regmap.map.to == 'pkt3' else 6, '0')308309define_name = 'R_{address}_{register_line.name}'.format(**locals()).ljust(63)310comment = ' /* {0} */'.format(comment) if comment else ''311print('#define {define_name} 0x{address}{comment}'.format(**locals()), file=filp)312313field_lines = field_lines_by_address[register_line.address]314field_idx = 0315while field_idx < len(field_lines):316field_line = field_lines[field_idx]317318if field_line.type_refs.isdisjoint(register_line.type_refs):319field_idx += 1320continue321del field_lines[field_idx]322323comment = get_chips_comment(field_line.chips, register_line.chips)324325mask = (1 << (field_line.bits[1] - field_line.bits[0] + 1)) - 1326define_name = '_{address}_{field_line.name}(x)'.format(**locals()).ljust(58)327comment = ' /* {0} */'.format(comment) if comment else ''328print(329'#define S{define_name} (((unsigned)(x) & 0x{mask:X}) << {field_line.bits[0]}){comment}'330.format(**locals()), file=filp)331print('#define G{define_name} (((x) >> {field_line.bits[0]}) & 0x{mask:X})'332.format(**locals()), file=filp)333334complement = ((1 << 32) - 1) ^ (mask << field_line.bits[0])335define_name = '_{address}_{field_line.name}'.format(**locals()).ljust(58)336print('#define C{define_name} 0x{complement:08X}'337.format(**locals()), file=filp)338339value_lines = value_lines_by_address[register_line.address]340value_idx = 0341while value_idx < len(value_lines):342value_line = value_lines[value_idx]343344if value_line.enum_refs.isdisjoint(field_line.enum_refs):345value_idx += 1346continue347del value_lines[value_idx]348349comment = get_chips_comment(value_line.chips, field_line.chips)350351define_name = 'V_{address}_{value_line.name}'.format(**locals()).ljust(55)352comment = ' /* {0} */'.format(comment) if comment else ''353print('#define {define_name} {value_line.enumentry.value}{comment}'354.format(**locals()), file=filp)355356if self.guard:357print('\n#endif // {self.guard}'.format(**locals()), file=filp)358359360def main():361parser = argparse.ArgumentParser()362parser.add_argument('--chip', dest='chips', type=str, nargs='*',363help='Chip for which to generate the header (all chips if unspecified)')364parser.add_argument('--sort', choices=['name', 'address'], default='address',365help='Sort key for registers, fields, and enum values')366parser.add_argument('--guard', type=str, help='Name of the #include guard')367parser.add_argument('files', metavar='FILE', type=str, nargs='+',368help='Register database file')369args = parser.parse_args()370371regdb = None372for filename in args.files:373with open(filename, 'r') as filp:374db = RegisterDatabase.from_json(json.load(filp))375if regdb is None:376regdb = db377else:378regdb.update(db)379380deduplicate_enums(regdb)381deduplicate_register_types(regdb)382383w = HeaderWriter(regdb, guard=args.guard)384w.print(sys.stdout, sort=args.sort)385386387if __name__ == '__main__':388main()389390# kate: space-indent on; indent-width 4; replace-tabs on;391392393