xref: /aosp_15_r20/external/mesa3d/src/etnaviv/hwdb/hwdb.h.py (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1#!/usr/bin/env python3
2#
3# Copyright © 2024 Igalia S.L.
4# SPDX-License-Identifier: MIT
5
6import argparse
7import ctypes
8import logging
9import re
10from datetime import datetime
11from mako.template import Template
12from pycparser import parse_file, c_ast
13
14logger = logging.getLogger('hwdb')
15
16template = """/*
17 * Copyright © 2024 Igalia S.L.
18 * SPDX-License-Identifier: MIT
19 */
20
21#pragma once
22
23#include <stdint.h>
24
25typedef struct
26{
27% for type, name in struct:
28   ${type} ${name};
29% endfor
30} gcsFEATURE_DATABASE;
31
32static gcsFEATURE_DATABASE gChipInfo[] = {
33% for entry in entries:
34   {
35% for name, value in entry:
36      ${value}, /* ${name} */
37% endfor
38   },
39% endfor
40};
41
42static inline gcsFEATURE_DATABASE *
43gcQueryFeatureDB(
44    uint32_t ChipID,
45    uint32_t ChipVersion,
46    uint32_t ProductID,
47    uint32_t EcoID,
48    uint32_t CustomerID
49    )
50{
51    int entryNum = sizeof(gChipInfo) / sizeof(gChipInfo[0]);
52
53    /* check formal release entries first */
54    for (int i = 0; i < entryNum; ++i)
55    {
56        if ((gChipInfo[i].chipID == ChipID)
57            && (gChipInfo[i].chipVersion == ChipVersion)
58            && (gChipInfo[i].productID == ProductID)
59            && (gChipInfo[i].ecoID == EcoID)
60            && (gChipInfo[i].customerID == CustomerID)
61            && (gChipInfo[i].formalRelease)
62           )
63        {
64            return &gChipInfo[i];
65        }
66    }
67
68    /* check informal release entries if we dont find in formal entries */
69    for (int i = 0; i < entryNum; ++i)
70    {
71        if ((gChipInfo[i].chipID == ChipID)
72            && ((gChipInfo[i].chipVersion & 0xFFF0) == (ChipVersion & 0xFFF0))
73            && (gChipInfo[i].productID == ProductID)
74            && (gChipInfo[i].ecoID == EcoID)
75            && (gChipInfo[i].customerID == CustomerID)
76            && (!gChipInfo[i].formalRelease)
77           )
78        {
79            return &gChipInfo[i];
80        }
81    }
82
83    return 0;
84}
85"""
86
87
88class HeaderFile(c_ast.NodeVisitor):
89    """Class representing a complete header file"""
90
91    # Regular expression to match the date and time in the comment
92    _DATE_RE = re.compile(r'/\*Auto created on (\d{4}-\d{2}-\d{2} \d{2}:\d{2})\*/')
93
94    def __init__(self, filename):
95        self.filename = filename
96        self.structs = {}
97        self.data = []
98        self.date_time = None
99        self.database_struct = None
100
101        self._read_date()
102        self._parse()
103
104        logger.debug('Parsed %s (autogenerated at %s, %u struct members, %u entries)', self.filename, self.date_time, len(self.database_struct._fields_), len(self.data))
105
106    def _read_date(self):
107        """Function parsing the creation date with re."""
108        # Read the content of the file and search for pattern
109        with open(self.filename, 'r', encoding="utf-8") as file:
110            file_content = file.read()
111
112        match = self._DATE_RE.search(file_content)
113
114        if match:
115            self.date_time = datetime.strptime(match.group(1), '%Y-%m-%d %H:%M')
116
117    def _parse(self):
118        ast = parse_file(self.filename, use_cpp=True, cpp_args=['-E', r'-I./utils/fake_libc_include', '-DgctUINT32=unsigned int', '-DgctINT=int'])
119        self.visit(ast)
120
121        self.database_struct = self.structs['gcsFEATURE_DATABASE']
122
123    def visit_Typedef(self, node):
124        if isinstance(node.type, c_ast.TypeDecl) and isinstance(node.type.type, c_ast.Struct):
125            struct_node = node.type.type
126            struct_name = node.name  # Typedef name as the struct name
127            fields = self._extract_fields_from_struct(struct_node)
128            if fields is not None:
129                # Create the ctypes.Structure subclass and add it to the structures dictionary
130                self.structs[struct_name] = type(struct_name, (ctypes.Structure,), {'_fields_': fields})
131
132    def _extract_fields_from_struct(self, struct_node):
133        """Function returning all fields of struct."""
134        fields = []
135        for decl in (struct_node.decls or []):
136            if isinstance(decl.type, c_ast.TypeDecl) or isinstance(decl.type, c_ast.PtrDecl):
137                field_name = decl.name
138                field_type = self._map_type_to_ctypes(decl.type.type, decl.bitsize)
139                if field_type:
140                    fields.append((field_name, field_type))
141            elif isinstance(decl.type, c_ast.ArrayDecl):
142                # Handle array type
143                field_name = decl.type.type.declname
144                element_type = self._map_type_to_ctypes(decl.type.type.type)
145                array_size = int(decl.type.dim.value)  # Assuming dim is a Constant node with the size as its value
146                if element_type:
147                    fields.append((field_name, element_type * array_size))
148
149        return fields if fields else None
150
151    def _map_type_to_ctypes(self, type_node, bitsize=None):
152        """Function returning a ctype type based node type."""
153        type_mappings = {
154            'bool': ctypes.c_bool,
155            'unsigned int': ctypes.c_uint,
156            'int': ctypes.c_int,
157        }
158
159        if isinstance(type_node, c_ast.IdentifierType):
160            c_type = ' '.join(type_node.names)
161
162            if bitsize and bitsize.value == '1':
163                c_type = 'bool'
164
165            return type_mappings.get(c_type)
166        elif isinstance(type_node, c_ast.TypeDecl):
167            return ctypes.c_char_p
168
169        return None
170
171    def visit_Decl(self, node):
172        # Check if the node is a declaration of an array of structs
173        if isinstance(node.type, c_ast.ArrayDecl) and isinstance(node.type.type, c_ast.TypeDecl):
174            struct_name = node.type.type.type.names[0]
175            if struct_name in self.structs:
176                elements = self._parse_array_initializer(node.init, self.structs[struct_name])
177                self.data.extend(elements)
178
179    def _parse_initializer(self, initializer, struct):
180        """Function returning one parsed struct initializer."""
181        return [
182            (param if not isinstance(param, str) else param.encode('utf-8'))
183            for index, expr in enumerate(initializer.exprs)
184            for param in [self._parse_expr(expr, getattr(struct._fields_[index][1], '_length_', None))]
185        ]
186
187    def _parse_array_initializer(self, init, struct_class):
188        """Function returning a fully processed struct initializer list."""
189        assert (isinstance(init, c_ast.InitList))
190        return [struct_class(*self._parse_initializer(initializer, struct_class)) for initializer in init.exprs]
191
192    def _parse_expr(self, expr, expected_size=None):
193        """Function returning parsed expression."""
194        # Direct handling of constant types
195        if isinstance(expr, c_ast.Constant):
196            if expr.type == "int":
197                # Base 0 automatically handles decimal, hex, and octal
198                return int(expr.value, 0)
199            elif expr.type == "string":
200                return expr.value.strip('"')
201
202        # Handling arrays with potential conversion to ctypes arrays
203        elif isinstance(expr, c_ast.InitList) and expected_size is not None:
204            element_list = [self._parse_expr(e) for e in expr.exprs]
205
206            # Ensure the list matches expected size, filling with zeros if necessary
207            if len(element_list) < expected_size:
208                element_list.extend([0] * (expected_size - len(element_list)))
209
210            # Convert to ctypes array, dynamically adjusting type based on context if needed
211            return (ctypes.c_uint * expected_size)(*element_list)
212
213        # Fallback or default return for unhandled cases
214        return None
215
216
217def merge_structures(structures):
218    """Function creating a new type by merging provided types."""
219    combined_fields = []
220    for struct in structures:
221        for field_name, field_type in struct._fields_:
222            # Check if the field name already exists
223            if field_name not in [field[0] for field in combined_fields]:
224                combined_fields.append((field_name, field_type))
225
226    # Create a new structure dynamically
227    return type("MergedStructure", (ctypes.Structure,), {'_fields_': combined_fields})
228
229
230def get_field_type(c, field_name):
231    """Function returning the type of a field type based on its name."""
232    for field in c._fields_:
233        if field[0] == field_name:
234            return field[1]
235
236    return None
237
238
239def create_merged_struct(c, entry):
240    """Function returning a fully populate instance of MergedStructure."""
241    fields = []
242    for field_name, field_type in c._fields_:
243        # We might need to 'upgrade' to an array - check field type too.
244        # e.g. VIP_SRAM_SIZE_ARRAY -> VIP_SRAM_SIZE_ARRAY[9]
245
246        if hasattr(entry, field_name) and get_field_type(entry, field_name) is field_type:
247            fields.append(getattr(entry, field_name))
248        else:
249            # Add a suitable default value
250            if field_type == ctypes.c_uint or field_type == ctypes.c_bool:
251                fields.append(0)
252            else:
253                # It must be an array
254                expected_size = getattr(field_type, '_length_')
255                fields.append((ctypes.c_uint * expected_size)(*[0] * expected_size))
256
257    return c(*fields)
258
259
260def enumerate_struct(obj):
261    for field_name, field_type in obj._fields_:
262        type = 'uint32_t'
263
264        if field_type == ctypes.c_char_p:
265            type = 'const char *'
266
267        if field_type == ctypes.c_bool:
268            field_name = field_name + ':1'
269
270        if hasattr(field_type, '_length_'):
271            field_name = f'{field_name}[{field_type._length_}]'
272
273        yield type, field_name
274
275
276def enumerate_values(obj):
277    for field_name, field_type in obj._fields_:
278        value = getattr(obj, field_name)
279
280        if field_type in {ctypes.c_uint, ctypes.c_bool}:
281            value = hex(value)
282        elif field_type == ctypes.c_char_p:
283            value = '"{}"'.format(value.decode('utf-8'))
284        elif isinstance(value, ctypes.Array):
285            value = '{{{}}}'.format(', '.join(str(element) for element in value))
286
287        yield field_name, value
288
289
290def main():
291    parser = argparse.ArgumentParser()
292    parser.add_argument('--output', required=True, type=str, action="store",
293                        help='output C header file')
294    parser.add_argument('headers', metavar='header', type=str, nargs='+',
295                        help='gc database header to process')
296    parser.add_argument('--verbose', "-v", action="store_true",
297                        help='be verbose')
298    args = parser.parse_args()
299
300    if args.verbose:
301        logging.basicConfig(level=logging.DEBUG)
302
303    files = [HeaderFile(header) for header in args.headers]
304    structs = [file.database_struct for file in files]
305    merged = merge_structures(structs)
306    logger.debug('merged struct: %u members', len(merged._fields_))
307
308    entries = []
309    for file in files:
310        logger.debug('Processing %s', file.filename)
311        for entry in file.data:
312            merged_entry = create_merged_struct(merged, entry)
313            entry_data = list(enumerate_values(merged_entry))
314            entries.append(entry_data)
315
316    logger.debug('Total entries: %u', len(entries))
317
318    with open(args.output, "w", encoding="UTF-8") as fh:
319        print(Template(template).render(struct=enumerate_struct(merged), entries=entries), file=fh)
320
321
322if __name__ == '__main__':
323    main()
324