amd/registers: scripts for processing register descriptions in JSON
[mesa.git] / src / amd / registers / parseheader.py
diff --git a/src/amd/registers/parseheader.py b/src/amd/registers/parseheader.py
new file mode 100644 (file)
index 0000000..24ec6a9
--- /dev/null
@@ -0,0 +1,199 @@
+#
+# Copyright 2017-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.
+#
+"""
+Helper script that parses a register header and produces a register database
+as output. Use as:
+
+  python3 parseheader.py ADDRESS_SPACE < header.h
+
+This script is included for reference -- we should be able to remove this in
+the future.
+"""
+
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+import json
+import math
+import re
+import sys
+
+from regdb import Object, RegisterDatabase, deduplicate_enums, deduplicate_register_types
+
+
+RE_comment = re.compile(r'(/\*(.*)\*/)$|(//(.*))$')
+RE_prefix = re.compile(r'([RSV])_([0-9a-fA-F]+)_')
+RE_set_value = re.compile(r'\(\(\(unsigned\)\(x\) & ([0-9a-fA-Fx]+)\) << ([0-9]+)\)')
+RE_set_value_no_shift = re.compile(r'\((\(unsigned\))?\(x\) & ([0-9a-fA-Fx]+)\)')
+
+class HeaderParser(object):
+    def __init__(self, address_space):
+        self.regdb = RegisterDatabase()
+        self.chips = ['si', 'cik', 'vi', 'fiji', 'stoney', 'gfx9']
+        self.address_space = address_space
+
+    def __fini_field(self):
+        if self.__field is None:
+            return
+
+        if self.__enumentries:
+            self.__field.enum_ref = self.__regmap.name + '__' + self.__field.name
+            self.regdb.add_enum(self.__field.enum_ref, Object(
+                entries=self.__enumentries
+            ))
+        self.__fields.append(self.__field)
+
+        self.__enumentries = None
+        self.__field = None
+
+    def __fini_register(self):
+        if self.__regmap is None:
+            return
+
+        if self.__fields:
+            self.regdb.add_register_type(self.__regmap.name, Object(
+                fields=self.__fields
+            ))
+            self.__regmap.type_ref = self.__regmap.name
+        self.regdb.add_register_mapping(self.__regmap)
+
+        self.__regmap = None
+        self.__fields = None
+
+    def parse_header(self, filp):
+        regdb = RegisterDatabase()
+        chips = ['si', 'cik', 'vi', 'fiji', 'stoney', 'gfx9']
+
+        self.__regmap = None
+        self.__fields = None
+        self.__field = None
+        self.__enumentries = None
+
+        for line in filp:
+            if not line.startswith('#define '):
+                continue
+
+            line = line[8:].strip()
+
+            comment = None
+            m = RE_comment.search(line)
+            if m is not None:
+                comment = m.group(2) or m.group(4)
+                comment = comment.strip()
+                line = line[:m.span()[0]].strip()
+
+            split = line.split(None, 1)
+            name = split[0]
+
+            m = RE_prefix.match(name)
+            if m is None:
+                continue
+
+            prefix = m.group(1)
+            prefix_address = int(m.group(2), 16)
+            name = name[m.span()[1]:]
+
+            if prefix == 'V':
+                value = int(split[1], 0)
+
+                for entry in self.__enumentries:
+                    if name == entry.name:
+                        sys.exit('Duplicate value define: name = {0}'.format(name))
+
+                entry = Object(name=name, value=value)
+                if comment is not None:
+                    entry.comment = comment
+                self.__enumentries.append(entry)
+                continue
+
+            if prefix == 'S':
+                self.__fini_field()
+
+                if not name.endswith('(x)'):
+                    sys.exit('Missing (x) in S line: {0}'.line)
+                name = name[:-3]
+
+                for field in self.__fields:
+                    if name == field.name:
+                        sys.exit('Duplicate field define: {0}'.format(name))
+
+                m = RE_set_value.match(split[1])
+                if m is not None:
+                    unshifted_mask = int(m.group(1), 0)
+                    shift = int(m.group(2), 0)
+                else:
+                    m = RE_set_value_no_shift.match(split[1])
+                    if m is not None:
+                        unshifted_mask = int(m.group(2), 0)
+                        shift = 0
+                    else:
+                        sys.exit('Bad S_xxx_xxx define: {0}'.format(line))
+
+                num_bits = int(math.log2(unshifted_mask + 1))
+                if unshifted_mask != (1 << num_bits) - 1:
+                    sys.exit('Bad unshifted mask in {0}'.format(line))
+
+                self.__field = Object(
+                    name=name,
+                    bits=[shift, shift + num_bits - 1],
+                )
+                if comment is not None:
+                    self.__field.comment = comment
+                self.__enumentries = []
+
+            if prefix == 'R':
+                self.__fini_field()
+                self.__fini_register()
+
+                if regdb.register_mappings_by_name(name):
+                    sys.exit('Duplicate register define: {0}'.format(name))
+
+                address = int(split[1], 0)
+                if address != prefix_address:
+                    sys.exit('Inconsistent register address: {0}'.format(line))
+
+                self.__regmap = Object(
+                    name=name,
+                    chips=self.chips,
+                    map=Object(to=self.address_space, at=address),
+                )
+                self.__fields = []
+
+        self.__fini_field()
+        self.__fini_register()
+
+def main():
+    map_to = sys.argv[1]
+
+    parser = HeaderParser(map_to)
+    parser.parse_header(sys.stdin)
+
+    deduplicate_enums(parser.regdb)
+    deduplicate_register_types(parser.regdb)
+
+    print(parser.regdb.encode_json_pretty())
+
+
+if __name__ == '__main__':
+    main()
+
+# kate: space-indent on; indent-width 4; replace-tabs on;