summaryrefslogtreecommitdiff
path: root/src/amd/registers/makeregheader.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/amd/registers/makeregheader.py')
-rw-r--r--src/amd/registers/makeregheader.py384
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;