xref: /third_party/mesa3d/src/amd/registers/regdb.py (revision bf215546)
1bf215546Sopenharmony_ci#
2bf215546Sopenharmony_ci# Copyright 2017-2019 Advanced Micro Devices, Inc.
3bf215546Sopenharmony_ci#
4bf215546Sopenharmony_ci# Permission is hereby granted, free of charge, to any person obtaining a
5bf215546Sopenharmony_ci# copy of this software and associated documentation files (the "Software"),
6bf215546Sopenharmony_ci# to deal in the Software without restriction, including without limitation
7bf215546Sopenharmony_ci# on the rights to use, copy, modify, merge, publish, distribute, sub
8bf215546Sopenharmony_ci# license, and/or sell copies of the Software, and to permit persons to whom
9bf215546Sopenharmony_ci# the Software is furnished to do so, subject to the following conditions:
10bf215546Sopenharmony_ci#
11bf215546Sopenharmony_ci# The above copyright notice and this permission notice (including the next
12bf215546Sopenharmony_ci# paragraph) shall be included in all copies or substantial portions of the
13bf215546Sopenharmony_ci# Software.
14bf215546Sopenharmony_ci#
15bf215546Sopenharmony_ci# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16bf215546Sopenharmony_ci# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17bf215546Sopenharmony_ci# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18bf215546Sopenharmony_ci# THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19bf215546Sopenharmony_ci# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20bf215546Sopenharmony_ci# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21bf215546Sopenharmony_ci# USE OR OTHER DEALINGS IN THE SOFTWARE.
22bf215546Sopenharmony_ci#
23bf215546Sopenharmony_ci"""
24bf215546Sopenharmony_ciPython package containing common tools for manipulating register JSON.
25bf215546Sopenharmony_ci"""
26bf215546Sopenharmony_ci
27bf215546Sopenharmony_ciimport itertools
28bf215546Sopenharmony_ciimport json
29bf215546Sopenharmony_ciimport re
30bf215546Sopenharmony_ciimport sys
31bf215546Sopenharmony_ci
32bf215546Sopenharmony_cifrom collections import defaultdict
33bf215546Sopenharmony_cifrom contextlib import contextmanager
34bf215546Sopenharmony_ci
35bf215546Sopenharmony_ciclass UnionFind(object):
36bf215546Sopenharmony_ci    """
37bf215546Sopenharmony_ci    Simplistic implementation of a union-find data structure that also keeps
38bf215546Sopenharmony_ci    track of the sets that have been unified.
39bf215546Sopenharmony_ci
40bf215546Sopenharmony_ci    - add: add an element to the implied global set of elements
41bf215546Sopenharmony_ci    - union: unify the sets containing the two given elements
42bf215546Sopenharmony_ci    - find: return the representative element of the set containing the
43bf215546Sopenharmony_ci            given element
44bf215546Sopenharmony_ci    - get_set: get the set containing the given element
45bf215546Sopenharmony_ci    - sets: iterate over all sets (the sets form a partition of the set of all
46bf215546Sopenharmony_ci            elements that have ever been added)
47bf215546Sopenharmony_ci    """
48bf215546Sopenharmony_ci    def __init__(self):
49bf215546Sopenharmony_ci        self.d = {}
50bf215546Sopenharmony_ci
51bf215546Sopenharmony_ci    def add(self, k):
52bf215546Sopenharmony_ci        if k not in self.d:
53bf215546Sopenharmony_ci            self.d[k] = set([k])
54bf215546Sopenharmony_ci
55bf215546Sopenharmony_ci    def union(self, k1, k2):
56bf215546Sopenharmony_ci        k1 = self.find(k1)
57bf215546Sopenharmony_ci        k2 = self.find(k2)
58bf215546Sopenharmony_ci        if k1 == k2:
59bf215546Sopenharmony_ci            return
60bf215546Sopenharmony_ci        if len(k1) < len(k2):
61bf215546Sopenharmony_ci            k1, k2 = k2, k1
62bf215546Sopenharmony_ci        self.d[k1].update(self.d[k2])
63bf215546Sopenharmony_ci        self.d[k2] = (k1,)
64bf215546Sopenharmony_ci
65bf215546Sopenharmony_ci    def find(self, k):
66bf215546Sopenharmony_ci        e = self.d[k]
67bf215546Sopenharmony_ci        if isinstance(e, set):
68bf215546Sopenharmony_ci            return k
69bf215546Sopenharmony_ci        assert isinstance(e, tuple)
70bf215546Sopenharmony_ci        r = self.find(e[0])
71bf215546Sopenharmony_ci        self.d[k] = (r,)
72bf215546Sopenharmony_ci        return r
73bf215546Sopenharmony_ci
74bf215546Sopenharmony_ci    def get_set(self, k):
75bf215546Sopenharmony_ci        k = self.find(k)
76bf215546Sopenharmony_ci        assert isinstance(self.d[k], set)
77bf215546Sopenharmony_ci        return self.d[k]
78bf215546Sopenharmony_ci
79bf215546Sopenharmony_ci    def sets(self):
80bf215546Sopenharmony_ci        for v in self.d.values():
81bf215546Sopenharmony_ci            if isinstance(v, set):
82bf215546Sopenharmony_ci                yield v
83bf215546Sopenharmony_ci
84bf215546Sopenharmony_ci
85bf215546Sopenharmony_ciclass Object(object):
86bf215546Sopenharmony_ci    """
87bf215546Sopenharmony_ci    Convenience helper class that essentially acts as a dictionary for convenient
88bf215546Sopenharmony_ci    conversion from and to JSON while allowing the use of .field notation
89bf215546Sopenharmony_ci    instead of subscript notation for member access.
90bf215546Sopenharmony_ci    """
91bf215546Sopenharmony_ci    def __init__(self, **kwargs):
92bf215546Sopenharmony_ci        for k, v in kwargs.items():
93bf215546Sopenharmony_ci            setattr(self, k, v)
94bf215546Sopenharmony_ci
95bf215546Sopenharmony_ci    def update(self, **kwargs):
96bf215546Sopenharmony_ci        for key, value in kwargs.items():
97bf215546Sopenharmony_ci            setattr(self, key, value)
98bf215546Sopenharmony_ci        return self
99bf215546Sopenharmony_ci
100bf215546Sopenharmony_ci    def __str__(self):
101bf215546Sopenharmony_ci        return 'Object(' + ', '.join(
102bf215546Sopenharmony_ci            '{k}={v}'.format(**locals()) for k, v, in self.__dict__.items()
103bf215546Sopenharmony_ci        ) + ')'
104bf215546Sopenharmony_ci
105bf215546Sopenharmony_ci    @staticmethod
106bf215546Sopenharmony_ci    def from_json(json, keys=None):
107bf215546Sopenharmony_ci        if isinstance(json, list):
108bf215546Sopenharmony_ci            return [Object.from_json(v) for v in json]
109bf215546Sopenharmony_ci        elif isinstance(json, dict):
110bf215546Sopenharmony_ci            obj = Object()
111bf215546Sopenharmony_ci            for k, v in json.items():
112bf215546Sopenharmony_ci                if keys is not None and k in keys:
113bf215546Sopenharmony_ci                    v = keys[k](v)
114bf215546Sopenharmony_ci                else:
115bf215546Sopenharmony_ci                    v = Object.from_json(v)
116bf215546Sopenharmony_ci                setattr(obj, k, v)
117bf215546Sopenharmony_ci            return obj
118bf215546Sopenharmony_ci        else:
119bf215546Sopenharmony_ci            return json
120bf215546Sopenharmony_ci
121bf215546Sopenharmony_ci    @staticmethod
122bf215546Sopenharmony_ci    def to_json(obj):
123bf215546Sopenharmony_ci        if isinstance(obj, Object):
124bf215546Sopenharmony_ci            return dict((k, Object.to_json(v)) for k, v in obj.__dict__.items())
125bf215546Sopenharmony_ci        elif isinstance(obj, dict):
126bf215546Sopenharmony_ci            return dict((k, Object.to_json(v)) for k, v in obj.items())
127bf215546Sopenharmony_ci        elif isinstance(obj, list):
128bf215546Sopenharmony_ci            return [Object.to_json(v) for v in obj]
129bf215546Sopenharmony_ci        else:
130bf215546Sopenharmony_ci            return obj
131bf215546Sopenharmony_ci
132bf215546Sopenharmony_ciclass MergeError(Exception):
133bf215546Sopenharmony_ci    def __init__(self, msg):
134bf215546Sopenharmony_ci        super(MergeError, self).__init__(msg)
135bf215546Sopenharmony_ci
136bf215546Sopenharmony_ciclass RegisterDatabaseError(Exception):
137bf215546Sopenharmony_ci    def __init__(self, msg):
138bf215546Sopenharmony_ci        super(RegisterDatabaseError, self).__init__(msg)
139bf215546Sopenharmony_ci
140bf215546Sopenharmony_ci@contextmanager
141bf215546Sopenharmony_cidef merge_scope(name):
142bf215546Sopenharmony_ci    """
143bf215546Sopenharmony_ci    Wrap a merge handling function in a "scope" whose name will be added when
144bf215546Sopenharmony_ci    propagating MergeErrors.
145bf215546Sopenharmony_ci    """
146bf215546Sopenharmony_ci    try:
147bf215546Sopenharmony_ci        yield
148bf215546Sopenharmony_ci    except Exception as e:
149bf215546Sopenharmony_ci        raise MergeError('{name}: {e}'.format(**locals()))
150bf215546Sopenharmony_ci
151bf215546Sopenharmony_cidef merge_dicts(dicts, keys=None, values=None):
152bf215546Sopenharmony_ci    """
153bf215546Sopenharmony_ci    Generic dictionary merging function.
154bf215546Sopenharmony_ci
155bf215546Sopenharmony_ci    dicts -- list of (origin, dictionary) pairs to merge
156bf215546Sopenharmony_ci    keys -- optional dictionary to provide a merge-strategy per key;
157bf215546Sopenharmony_ci            the merge strategy is a callable which will receive a list of
158bf215546Sopenharmony_ci            (origin, value) pairs
159bf215546Sopenharmony_ci    value -- optional function which provides a merge-strategy for values;
160bf215546Sopenharmony_ci             the merge strategy is a callable which will receive the name of
161bf215546Sopenharmony_ci             the key and a list of (origin, value) pairs
162bf215546Sopenharmony_ci
163bf215546Sopenharmony_ci    The default strategy is to allow merging keys if all origin dictionaries
164bf215546Sopenharmony_ci    that contain the key have the same value for it.
165bf215546Sopenharmony_ci    """
166bf215546Sopenharmony_ci    ks = set()
167bf215546Sopenharmony_ci    for _, d in dicts:
168bf215546Sopenharmony_ci        ks.update(d.keys())
169bf215546Sopenharmony_ci
170bf215546Sopenharmony_ci    result = {}
171bf215546Sopenharmony_ci    for k in ks:
172bf215546Sopenharmony_ci        vs = [(o, d[k]) for o, d in dicts if k in d]
173bf215546Sopenharmony_ci        with merge_scope('Key {k}'.format(**locals())):
174bf215546Sopenharmony_ci            if keys is not None and k in keys:
175bf215546Sopenharmony_ci                result[k] = keys[k](vs)
176bf215546Sopenharmony_ci            elif values is not None:
177bf215546Sopenharmony_ci                result[k] = values(k, vs)
178bf215546Sopenharmony_ci            else:
179bf215546Sopenharmony_ci                base_origin, base = vs[0]
180bf215546Sopenharmony_ci                for other_origin, other in vs[1:]:
181bf215546Sopenharmony_ci                    if base != other:
182bf215546Sopenharmony_ci                        raise MergeError('{base} (from {base_origin}) != {other} (from {other_origin})'.format(**locals()))
183bf215546Sopenharmony_ci                result[k] = base
184bf215546Sopenharmony_ci    return result
185bf215546Sopenharmony_ci
186bf215546Sopenharmony_cidef merge_objects(objects, keys=None):
187bf215546Sopenharmony_ci    """
188bf215546Sopenharmony_ci    Like merge_dicts, but applied to instances of Object.
189bf215546Sopenharmony_ci    """
190bf215546Sopenharmony_ci    return Object(**merge_dicts([(origin, obj.__dict__) for origin, obj in objects], keys=keys))
191bf215546Sopenharmony_ci
192bf215546Sopenharmony_ciclass RegisterDatabase(object):
193bf215546Sopenharmony_ci    """
194bf215546Sopenharmony_ci    A register database containing:
195bf215546Sopenharmony_ci
196bf215546Sopenharmony_ci    - enums: these are lists of named values that can occur in a register field
197bf215546Sopenharmony_ci    - register types: description of a register type or template as a list of
198bf215546Sopenharmony_ci                      fields
199bf215546Sopenharmony_ci    - register mappings: named and typed registers mapped at locations in an
200bf215546Sopenharmony_ci                         address space
201bf215546Sopenharmony_ci    """
202bf215546Sopenharmony_ci    def __init__(self):
203bf215546Sopenharmony_ci        self.__enums = {}
204bf215546Sopenharmony_ci        self.__register_types = {}
205bf215546Sopenharmony_ci        self.__register_mappings = []
206bf215546Sopenharmony_ci        self.__regmap_by_addr = None
207bf215546Sopenharmony_ci        self.__chips = None
208bf215546Sopenharmony_ci
209bf215546Sopenharmony_ci    def __post_init(self):
210bf215546Sopenharmony_ci        """
211bf215546Sopenharmony_ci        Perform some basic canonicalization:
212bf215546Sopenharmony_ci        - enum entries are sorted by value
213bf215546Sopenharmony_ci        - register type fields are sorted by starting bit
214bf215546Sopenharmony_ci        - __register_mappings is sorted by offset
215bf215546Sopenharmony_ci        - the chips field of register mappings is sorted
216bf215546Sopenharmony_ci
217bf215546Sopenharmony_ci        Lazily computes the set of all chips mentioned by register mappings.
218bf215546Sopenharmony_ci        """
219bf215546Sopenharmony_ci        if self.__regmap_by_addr is not None:
220bf215546Sopenharmony_ci            return
221bf215546Sopenharmony_ci
222bf215546Sopenharmony_ci        for enum in self.__enums.values():
223bf215546Sopenharmony_ci            enum.entries.sort(key=lambda entry: entry.value)
224bf215546Sopenharmony_ci
225bf215546Sopenharmony_ci        for regtype in self.__register_types.values():
226bf215546Sopenharmony_ci            regtype.fields.sort(key=lambda field: field.bits[0])
227bf215546Sopenharmony_ci
228bf215546Sopenharmony_ci        self.__regmap_by_addr = defaultdict(list)
229bf215546Sopenharmony_ci        self.__chips = set()
230bf215546Sopenharmony_ci
231bf215546Sopenharmony_ci        # Merge register mappings using sort order and garbage collect enums
232bf215546Sopenharmony_ci        # and register types.
233bf215546Sopenharmony_ci        old_register_mappings = self.__register_mappings
234bf215546Sopenharmony_ci        old_register_mappings.sort(key=lambda regmap: regmap.map.at)
235bf215546Sopenharmony_ci
236bf215546Sopenharmony_ci        self.__register_mappings = []
237bf215546Sopenharmony_ci        for regmap in old_register_mappings:
238bf215546Sopenharmony_ci            addr = (regmap.map.to, regmap.map.at)
239bf215546Sopenharmony_ci            chips = set(getattr(regmap, 'chips', ['undef']))
240bf215546Sopenharmony_ci            type_ref = getattr(regmap, 'type_ref', None)
241bf215546Sopenharmony_ci
242bf215546Sopenharmony_ci            self.__chips.update(chips)
243bf215546Sopenharmony_ci
244bf215546Sopenharmony_ci            merged = False
245bf215546Sopenharmony_ci            for other in reversed(self.__register_mappings):
246bf215546Sopenharmony_ci                if other.name != regmap.name:
247bf215546Sopenharmony_ci                    break
248bf215546Sopenharmony_ci
249bf215546Sopenharmony_ci                other_addr = (other.map.to, other.map.at)
250bf215546Sopenharmony_ci                other_chips = getattr(other, 'chips', ['undef'])
251bf215546Sopenharmony_ci                other_type_ref = getattr(other, 'type_ref', None)
252bf215546Sopenharmony_ci
253bf215546Sopenharmony_ci                if addr == other_addr and\
254bf215546Sopenharmony_ci                   (type_ref is None or other_type_ref is None or type_ref == other_type_ref):
255bf215546Sopenharmony_ci                    other.chips = sorted(list(chips.union(other_chips)))
256bf215546Sopenharmony_ci                    if type_ref is not None:
257bf215546Sopenharmony_ci                        other.type_ref = type_ref
258bf215546Sopenharmony_ci                    merged = True
259bf215546Sopenharmony_ci                    break
260bf215546Sopenharmony_ci
261bf215546Sopenharmony_ci            if merged:
262bf215546Sopenharmony_ci                continue
263bf215546Sopenharmony_ci
264bf215546Sopenharmony_ci            addrmappings = self.__regmap_by_addr[addr]
265bf215546Sopenharmony_ci
266bf215546Sopenharmony_ci            for other in addrmappings:
267bf215546Sopenharmony_ci                other_type_ref = getattr(other, 'type_ref', None)
268bf215546Sopenharmony_ci                other_chips = getattr(other, 'chips', ['undef'])
269bf215546Sopenharmony_ci                if type_ref is not None and other_type_ref is not None and \
270bf215546Sopenharmony_ci                   type_ref != other_type_ref and chips.intersection(other_chips):
271bf215546Sopenharmony_ci                    raise RegisterDatabaseError(
272bf215546Sopenharmony_ci                        'Registers {0} and {1} overlap and have conflicting types'.format(
273bf215546Sopenharmony_ci                            other.name, regmap.name))
274bf215546Sopenharmony_ci
275bf215546Sopenharmony_ci            addrmappings.append(regmap)
276bf215546Sopenharmony_ci            self.__register_mappings.append(regmap)
277bf215546Sopenharmony_ci
278bf215546Sopenharmony_ci    def garbage_collect(self):
279bf215546Sopenharmony_ci        """
280bf215546Sopenharmony_ci        Remove unreferenced enums and register types.
281bf215546Sopenharmony_ci        """
282bf215546Sopenharmony_ci        old_enums = self.__enums
283bf215546Sopenharmony_ci        old_register_types = self.__register_types
284bf215546Sopenharmony_ci
285bf215546Sopenharmony_ci        self.__enums = {}
286bf215546Sopenharmony_ci        self.__register_types = {}
287bf215546Sopenharmony_ci        for regmap in self.__register_mappings:
288bf215546Sopenharmony_ci            if hasattr(regmap, 'type_ref') and regmap.type_ref not in self.__register_types:
289bf215546Sopenharmony_ci                regtype = old_register_types[regmap.type_ref]
290bf215546Sopenharmony_ci                self.__register_types[regmap.type_ref] = regtype
291bf215546Sopenharmony_ci                for field in regtype.fields:
292bf215546Sopenharmony_ci                    if hasattr(field, 'enum_ref') and field.enum_ref not in self.__enums:
293bf215546Sopenharmony_ci                        self.__enums[field.enum_ref] = old_enums[field.enum_ref]
294bf215546Sopenharmony_ci
295bf215546Sopenharmony_ci    def __validate_register_type(self, regtype):
296bf215546Sopenharmony_ci        for field in regtype.fields:
297bf215546Sopenharmony_ci            if hasattr(field, 'enum_ref') and field.enum_ref not in self.__enums:
298bf215546Sopenharmony_ci                raise RegisterDatabaseError(
299bf215546Sopenharmony_ci                    'Register type field {0} has unknown enum_ref {1}'.format(
300bf215546Sopenharmony_ci                        field.name, field.enum_ref))
301bf215546Sopenharmony_ci
302bf215546Sopenharmony_ci    def __validate_register_mapping(self, regmap):
303bf215546Sopenharmony_ci        if hasattr(regmap, 'type_ref') and regmap.type_ref not in self.__register_types:
304bf215546Sopenharmony_ci            raise RegisterDatabaseError(
305bf215546Sopenharmony_ci                'Register mapping {0} has unknown type_ref {1}'.format(
306bf215546Sopenharmony_ci                    regmap.name, regmap.type_ref))
307bf215546Sopenharmony_ci
308bf215546Sopenharmony_ci    def __validate(self):
309bf215546Sopenharmony_ci        for regtype in self.__register_types.values():
310bf215546Sopenharmony_ci            self.__validate_register_type(regtype)
311bf215546Sopenharmony_ci        for regmap in self.__register_mappings:
312bf215546Sopenharmony_ci            self.__validate_register_mapping(regmap)
313bf215546Sopenharmony_ci
314bf215546Sopenharmony_ci    @staticmethod
315bf215546Sopenharmony_ci    def enum_key(enum):
316bf215546Sopenharmony_ci        """
317bf215546Sopenharmony_ci        Return a key that uniquely describes the signature of the given
318bf215546Sopenharmony_ci        enum (assuming that it has been canonicalized). Two enums with the
319bf215546Sopenharmony_ci        same key can be merged.
320bf215546Sopenharmony_ci        """
321bf215546Sopenharmony_ci        return ''.join(
322bf215546Sopenharmony_ci            ':{0}:{1}'.format(entry.name, entry.value)
323bf215546Sopenharmony_ci            for entry in enum.entries
324bf215546Sopenharmony_ci        )
325bf215546Sopenharmony_ci
326bf215546Sopenharmony_ci    def add_enum(self, name, enum):
327bf215546Sopenharmony_ci        if name in self.__enums:
328bf215546Sopenharmony_ci            raise RegisterDatabaseError('Duplicate enum ' + name)
329bf215546Sopenharmony_ci        self.__enums[name] = enum
330bf215546Sopenharmony_ci
331bf215546Sopenharmony_ci    @staticmethod
332bf215546Sopenharmony_ci    def __merge_enums(enums, union=False):
333bf215546Sopenharmony_ci        def merge_entries(entries_lists):
334bf215546Sopenharmony_ci            values = defaultdict(list)
335bf215546Sopenharmony_ci            for origin, enum in entries_lists:
336bf215546Sopenharmony_ci                for entry in enum:
337bf215546Sopenharmony_ci                    values[entry.value].append((origin, entry))
338bf215546Sopenharmony_ci
339bf215546Sopenharmony_ci            if not union:
340bf215546Sopenharmony_ci                if any(len(entries) != len(enums) for entries in values.values()):
341bf215546Sopenharmony_ci                    raise RegisterDatabaseError(
342bf215546Sopenharmony_ci                        'Attempting to merge enums with different values')
343bf215546Sopenharmony_ci
344bf215546Sopenharmony_ci            return [
345bf215546Sopenharmony_ci                merge_objects(entries)
346bf215546Sopenharmony_ci                for entries in values.values()
347bf215546Sopenharmony_ci            ]
348bf215546Sopenharmony_ci
349bf215546Sopenharmony_ci        return merge_objects(
350bf215546Sopenharmony_ci            enums,
351bf215546Sopenharmony_ci            keys={
352bf215546Sopenharmony_ci                'entries': merge_entries,
353bf215546Sopenharmony_ci            }
354bf215546Sopenharmony_ci        )
355bf215546Sopenharmony_ci
356bf215546Sopenharmony_ci    def merge_enums(self, names, newname, union=False):
357bf215546Sopenharmony_ci        """
358bf215546Sopenharmony_ci        Given a list of enum names, merge them all into one with a new name and
359bf215546Sopenharmony_ci        update all references.
360bf215546Sopenharmony_ci        """
361bf215546Sopenharmony_ci        if newname not in names and newname in self.__enums:
362bf215546Sopenharmony_ci            raise RegisterDatabaseError('Enum {0} already exists'.format(newname))
363bf215546Sopenharmony_ci
364bf215546Sopenharmony_ci        newenum = self.__merge_enums(
365bf215546Sopenharmony_ci            [(name, self.__enums[name]) for name in names],
366bf215546Sopenharmony_ci            union=union
367bf215546Sopenharmony_ci        )
368bf215546Sopenharmony_ci
369bf215546Sopenharmony_ci        for name in names:
370bf215546Sopenharmony_ci            del self.__enums[name]
371bf215546Sopenharmony_ci        self.__enums[newname] = newenum
372bf215546Sopenharmony_ci
373bf215546Sopenharmony_ci        for regtype in self.__register_types.values():
374bf215546Sopenharmony_ci            for field in regtype.fields:
375bf215546Sopenharmony_ci                if getattr(field, 'enum_ref', None) in names:
376bf215546Sopenharmony_ci                    field.enum_ref = newname
377bf215546Sopenharmony_ci
378bf215546Sopenharmony_ci        self.__regmap_by_addr = None
379bf215546Sopenharmony_ci
380bf215546Sopenharmony_ci    def add_register_type(self, name, regtype):
381bf215546Sopenharmony_ci        if regtype in self.__register_types:
382bf215546Sopenharmony_ci            raise RegisterDatabaseError('Duplicate register type ' + name)
383bf215546Sopenharmony_ci        self.__register_types[name] = regtype
384bf215546Sopenharmony_ci        self.__validate_register_type(regtype)
385bf215546Sopenharmony_ci
386bf215546Sopenharmony_ci    def register_type(self, name):
387bf215546Sopenharmony_ci        self.__post_init()
388bf215546Sopenharmony_ci        return self.__register_types[name]
389bf215546Sopenharmony_ci
390bf215546Sopenharmony_ci    @staticmethod
391bf215546Sopenharmony_ci    def __merge_register_types(regtypes, union=False, field_keys={}):
392bf215546Sopenharmony_ci        def merge_fields(fields_lists):
393bf215546Sopenharmony_ci            fields = defaultdict(list)
394bf215546Sopenharmony_ci            for origin, fields_list in fields_lists:
395bf215546Sopenharmony_ci                for field in fields_list:
396bf215546Sopenharmony_ci                    fields[field.bits[0]].append((origin, field))
397bf215546Sopenharmony_ci
398bf215546Sopenharmony_ci            if not union:
399bf215546Sopenharmony_ci                if any(len(entries) != len(regtypes) for entries in fields.values()):
400bf215546Sopenharmony_ci                    raise RegisterDatabaseError(
401bf215546Sopenharmony_ci                        'Attempting to merge register types with different fields')
402bf215546Sopenharmony_ci
403bf215546Sopenharmony_ci            return [
404bf215546Sopenharmony_ci                merge_objects(field, keys=field_keys)
405bf215546Sopenharmony_ci                for field in fields.values()
406bf215546Sopenharmony_ci            ]
407bf215546Sopenharmony_ci
408bf215546Sopenharmony_ci        with merge_scope('Register types {0}'.format(', '.join(name for name, _ in regtypes))):
409bf215546Sopenharmony_ci            return merge_objects(
410bf215546Sopenharmony_ci                regtypes,
411bf215546Sopenharmony_ci                keys={
412bf215546Sopenharmony_ci                    'fields': merge_fields,
413bf215546Sopenharmony_ci                }
414bf215546Sopenharmony_ci            )
415bf215546Sopenharmony_ci
416bf215546Sopenharmony_ci    def merge_register_types(self, names, newname, union=False):
417bf215546Sopenharmony_ci        """
418bf215546Sopenharmony_ci        Given a list of register type names, merge them all into one with a
419bf215546Sopenharmony_ci        new name and update all references.
420bf215546Sopenharmony_ci        """
421bf215546Sopenharmony_ci        if newname not in names and newname in self.__register_types:
422bf215546Sopenharmony_ci            raise RegisterDatabaseError('Register type {0} already exists'.format(newname))
423bf215546Sopenharmony_ci
424bf215546Sopenharmony_ci        newregtype = self.__merge_register_types(
425bf215546Sopenharmony_ci            [(name, self.__register_types[name]) for name in names],
426bf215546Sopenharmony_ci            union=union
427bf215546Sopenharmony_ci        )
428bf215546Sopenharmony_ci
429bf215546Sopenharmony_ci        for name in names:
430bf215546Sopenharmony_ci            del self.__register_types[name]
431bf215546Sopenharmony_ci        self.__register_types[newname] = newregtype
432bf215546Sopenharmony_ci
433bf215546Sopenharmony_ci        for regmap in self.__register_mappings:
434bf215546Sopenharmony_ci            if getattr(regmap, 'type_ref', None) in names:
435bf215546Sopenharmony_ci                regmap.type_ref = newname
436bf215546Sopenharmony_ci
437bf215546Sopenharmony_ci        self.__regmap_by_addr = None
438bf215546Sopenharmony_ci
439bf215546Sopenharmony_ci    def add_register_mapping(self, regmap):
440bf215546Sopenharmony_ci        self.__regmap_by_addr = None
441bf215546Sopenharmony_ci        self.__register_mappings.append(regmap)
442bf215546Sopenharmony_ci        self.__validate_register_mapping(regmap)
443bf215546Sopenharmony_ci
444bf215546Sopenharmony_ci    def remove_register_mappings(self, regmaps_to_remove):
445bf215546Sopenharmony_ci        self.__post_init()
446bf215546Sopenharmony_ci
447bf215546Sopenharmony_ci        regmaps_to_remove = set(regmaps_to_remove)
448bf215546Sopenharmony_ci
449bf215546Sopenharmony_ci        regmaps = self.__register_mappings
450bf215546Sopenharmony_ci        self.__register_mappings = []
451bf215546Sopenharmony_ci        for regmap in regmaps:
452bf215546Sopenharmony_ci            if regmap not in regmaps_to_remove:
453bf215546Sopenharmony_ci                self.__register_mappings.append(regmap)
454bf215546Sopenharmony_ci
455bf215546Sopenharmony_ci        self.__regmap_by_addr = None
456bf215546Sopenharmony_ci
457bf215546Sopenharmony_ci    def enum(self, name):
458bf215546Sopenharmony_ci        """
459bf215546Sopenharmony_ci        Return the enum of the given name, if any.
460bf215546Sopenharmony_ci        """
461bf215546Sopenharmony_ci        self.__post_init()
462bf215546Sopenharmony_ci        return self.__enums.get(name, None)
463bf215546Sopenharmony_ci
464bf215546Sopenharmony_ci    def enums(self):
465bf215546Sopenharmony_ci        """
466bf215546Sopenharmony_ci        Yields all (name, enum) pairs.
467bf215546Sopenharmony_ci        """
468bf215546Sopenharmony_ci        self.__post_init()
469bf215546Sopenharmony_ci        for name, enum in self.__enums.items():
470bf215546Sopenharmony_ci            yield (name, enum)
471bf215546Sopenharmony_ci
472bf215546Sopenharmony_ci    def fields(self):
473bf215546Sopenharmony_ci        """
474bf215546Sopenharmony_ci        Yields all (register_type, fields) pairs.
475bf215546Sopenharmony_ci        """
476bf215546Sopenharmony_ci        self.__post_init()
477bf215546Sopenharmony_ci        for regtype in self.__register_types.values():
478bf215546Sopenharmony_ci            for field in regtype.fields:
479bf215546Sopenharmony_ci                yield (regtype, field)
480bf215546Sopenharmony_ci
481bf215546Sopenharmony_ci    def register_types(self):
482bf215546Sopenharmony_ci        """
483bf215546Sopenharmony_ci        Yields all (name, register_type) pairs.
484bf215546Sopenharmony_ci        """
485bf215546Sopenharmony_ci        self.__post_init()
486bf215546Sopenharmony_ci        for name, regtype in self.__register_types.items():
487bf215546Sopenharmony_ci            yield (name, regtype)
488bf215546Sopenharmony_ci
489bf215546Sopenharmony_ci    def register_mappings_by_name(self, name):
490bf215546Sopenharmony_ci        """
491bf215546Sopenharmony_ci        Return a list of register mappings with the given name.
492bf215546Sopenharmony_ci        """
493bf215546Sopenharmony_ci        self.__post_init()
494bf215546Sopenharmony_ci
495bf215546Sopenharmony_ci        begin = 0
496bf215546Sopenharmony_ci        end = len(self.__register_mappings)
497bf215546Sopenharmony_ci        while begin < end:
498bf215546Sopenharmony_ci            middle = (begin + end) // 2
499bf215546Sopenharmony_ci            if self.__register_mappings[middle].name < name:
500bf215546Sopenharmony_ci                begin = middle + 1
501bf215546Sopenharmony_ci            elif name < self.__register_mappings[middle].name:
502bf215546Sopenharmony_ci                end = middle
503bf215546Sopenharmony_ci            else:
504bf215546Sopenharmony_ci                break
505bf215546Sopenharmony_ci
506bf215546Sopenharmony_ci        if begin >= end:
507bf215546Sopenharmony_ci            return []
508bf215546Sopenharmony_ci
509bf215546Sopenharmony_ci        # We now have begin <= mid < end with begin.name <= name, mid.name == name, name < end.name
510bf215546Sopenharmony_ci        # Narrow down begin and end
511bf215546Sopenharmony_ci        hi = middle
512bf215546Sopenharmony_ci        while begin < hi:
513bf215546Sopenharmony_ci            mid = (begin + hi) // 2
514bf215546Sopenharmony_ci            if self.__register_mappings[mid].name < name:
515bf215546Sopenharmony_ci                begin = mid + 1
516bf215546Sopenharmony_ci            else:
517bf215546Sopenharmony_ci                hi = mid
518bf215546Sopenharmony_ci
519bf215546Sopenharmony_ci        lo = middle + 1
520bf215546Sopenharmony_ci        while lo < end:
521bf215546Sopenharmony_ci            mid = (lo + end) // 2
522bf215546Sopenharmony_ci            if self.__register_mappings[mid].name == name:
523bf215546Sopenharmony_ci                lo = mid + 1
524bf215546Sopenharmony_ci            else:
525bf215546Sopenharmony_ci                end = mid
526bf215546Sopenharmony_ci
527bf215546Sopenharmony_ci        return self.__register_mappings[begin:end]
528bf215546Sopenharmony_ci
529bf215546Sopenharmony_ci    def register_mappings(self):
530bf215546Sopenharmony_ci        """
531bf215546Sopenharmony_ci        Yields all register mappings.
532bf215546Sopenharmony_ci        """
533bf215546Sopenharmony_ci        self.__post_init()
534bf215546Sopenharmony_ci        for regmap in self.__register_mappings:
535bf215546Sopenharmony_ci            yield regmap
536bf215546Sopenharmony_ci
537bf215546Sopenharmony_ci    def chips(self):
538bf215546Sopenharmony_ci        """
539bf215546Sopenharmony_ci        Yields all chips.
540bf215546Sopenharmony_ci        """
541bf215546Sopenharmony_ci        self.__post_init()
542bf215546Sopenharmony_ci        return iter(self.__chips)
543bf215546Sopenharmony_ci
544bf215546Sopenharmony_ci    def merge_chips(self, chips, newchip):
545bf215546Sopenharmony_ci        """
546bf215546Sopenharmony_ci        Merge register mappings of the given chips into a single chip of the
547bf215546Sopenharmony_ci        given name. Recursively merges register types and enums when appropriate.
548bf215546Sopenharmony_ci        """
549bf215546Sopenharmony_ci        self.__post_init()
550bf215546Sopenharmony_ci
551bf215546Sopenharmony_ci        chips = set(chips)
552bf215546Sopenharmony_ci
553bf215546Sopenharmony_ci        regtypes_merge = UnionFind()
554bf215546Sopenharmony_ci        enums_merge = UnionFind()
555bf215546Sopenharmony_ci
556bf215546Sopenharmony_ci        # Walk register mappings to find register types that should be merged.
557bf215546Sopenharmony_ci        for idx, regmap in itertools.islice(enumerate(self.__register_mappings), 1, None):
558bf215546Sopenharmony_ci            if not hasattr(regmap, 'type_ref'):
559bf215546Sopenharmony_ci                continue
560bf215546Sopenharmony_ci            if chips.isdisjoint(regmap.chips):
561bf215546Sopenharmony_ci                continue
562bf215546Sopenharmony_ci
563bf215546Sopenharmony_ci            for other in self.__register_mappings[idx-1::-1]:
564bf215546Sopenharmony_ci                if regmap.name != other.name:
565bf215546Sopenharmony_ci                    break
566bf215546Sopenharmony_ci                if chips.isdisjoint(other.chips):
567bf215546Sopenharmony_ci                    continue
568bf215546Sopenharmony_ci                if regmap.map.to != other.map.to or regmap.map.at != other.map.at:
569bf215546Sopenharmony_ci                    raise RegisterDatabaseError(
570bf215546Sopenharmony_ci                        'Attempting to merge chips with incompatible addresses of {0}'.format(regmap.name))
571bf215546Sopenharmony_ci                if not hasattr(regmap, 'type_ref'):
572bf215546Sopenharmony_ci                    continue
573bf215546Sopenharmony_ci
574bf215546Sopenharmony_ci                if regmap.type_ref != other.type_ref:
575bf215546Sopenharmony_ci                    regtypes_merge.add(regmap.type_ref)
576bf215546Sopenharmony_ci                    regtypes_merge.add(other.type_ref)
577bf215546Sopenharmony_ci                    regtypes_merge.union(regmap.type_ref, other.type_ref)
578bf215546Sopenharmony_ci
579bf215546Sopenharmony_ci        # Walk over regtype sets that are to be merged and find enums that
580bf215546Sopenharmony_ci        # should be merged.
581bf215546Sopenharmony_ci        for type_refs in regtypes_merge.sets():
582bf215546Sopenharmony_ci            fields_merge = defaultdict(set)
583bf215546Sopenharmony_ci            for type_ref in type_refs:
584bf215546Sopenharmony_ci                regtype = self.__register_types[type_ref]
585bf215546Sopenharmony_ci                for field in regtype.fields:
586bf215546Sopenharmony_ci                    if hasattr(field, 'enum_ref'):
587bf215546Sopenharmony_ci                        fields_merge[field.name].add(field.enum_ref)
588bf215546Sopenharmony_ci
589bf215546Sopenharmony_ci            for enum_refs in fields_merge.values():
590bf215546Sopenharmony_ci                if len(enum_refs) > 1:
591bf215546Sopenharmony_ci                    enum_refs = list(enum_refs)
592bf215546Sopenharmony_ci                    enums_merge.add(enum_refs[0])
593bf215546Sopenharmony_ci                    for enum_ref in enum_refs[1:]:
594bf215546Sopenharmony_ci                        enums_merge.add(enum_ref)
595bf215546Sopenharmony_ci                        enums_merge.union(enum_ref, enum_refs[0])
596bf215546Sopenharmony_ci
597bf215546Sopenharmony_ci        # Merge all mergeable enum sets
598bf215546Sopenharmony_ci        remap_enum_refs = {}
599bf215546Sopenharmony_ci        for enum_refs in enums_merge.sets():
600bf215546Sopenharmony_ci            enum_refs = sorted(enum_refs)
601bf215546Sopenharmony_ci            newname = enum_refs[0] + '_' + newchip
602bf215546Sopenharmony_ci            i = 0
603bf215546Sopenharmony_ci            while newname in self.__enums:
604bf215546Sopenharmony_ci                newname = enum_refs[0] + '_' + newchip + str(i)
605bf215546Sopenharmony_ci                i += 1
606bf215546Sopenharmony_ci
607bf215546Sopenharmony_ci            for enum_ref in enum_refs:
608bf215546Sopenharmony_ci                remap_enum_refs[enum_ref] = newname
609bf215546Sopenharmony_ci
610bf215546Sopenharmony_ci            # Don't use self.merge_enums, because we don't want to automatically
611bf215546Sopenharmony_ci            # update _all_ references to the merged enums (some may be from
612bf215546Sopenharmony_ci            # register types that aren't going to be merged).
613bf215546Sopenharmony_ci            self.add_enum(newname, self.__merge_enums(
614bf215546Sopenharmony_ci                [(enum_ref, self.__enums[enum_ref]) for enum_ref in enum_refs],
615bf215546Sopenharmony_ci                union=True
616bf215546Sopenharmony_ci            ))
617bf215546Sopenharmony_ci
618bf215546Sopenharmony_ci        # Merge all mergeable type refs
619bf215546Sopenharmony_ci        remap_type_refs = {}
620bf215546Sopenharmony_ci        for type_refs in regtypes_merge.sets():
621bf215546Sopenharmony_ci            type_refs = sorted(type_refs)
622bf215546Sopenharmony_ci            newname = type_refs[0] + '_' + newchip
623bf215546Sopenharmony_ci            i = 0
624bf215546Sopenharmony_ci            while newname in self.__enums:
625bf215546Sopenharmony_ci                newname = type_refs[0] + '_' + newchip + str(i)
626bf215546Sopenharmony_ci                i += 1
627bf215546Sopenharmony_ci
628bf215546Sopenharmony_ci            updated_regtypes = []
629bf215546Sopenharmony_ci            for type_ref in type_refs:
630bf215546Sopenharmony_ci                remap_type_refs[type_ref] = newname
631bf215546Sopenharmony_ci
632bf215546Sopenharmony_ci                regtype = Object.from_json(Object.to_json(self.__register_types[type_ref]))
633bf215546Sopenharmony_ci                for field in regtype.fields:
634bf215546Sopenharmony_ci                    if hasattr(field, 'enum_ref'):
635bf215546Sopenharmony_ci                        field.enum_ref = remap_enum_refs.get(enum_ref, enum_ref)
636bf215546Sopenharmony_ci
637bf215546Sopenharmony_ci                updated_regtypes.append(regtype)
638bf215546Sopenharmony_ci
639bf215546Sopenharmony_ci            def merge_enum_refs(enum_refs):
640bf215546Sopenharmony_ci                enum_refs = set(
641bf215546Sopenharmony_ci                    remap_enum_refs.get(enum_ref, enum_ref)
642bf215546Sopenharmony_ci                    for origin, enum_ref in enum_refs
643bf215546Sopenharmony_ci                )
644bf215546Sopenharmony_ci                assert len(enum_refs) == 1 # should be ensured by how we determine the enums to be merged
645bf215546Sopenharmony_ci                return enum_refs.pop()
646bf215546Sopenharmony_ci
647bf215546Sopenharmony_ci            self.add_register_type(newname, self.__merge_register_types(
648bf215546Sopenharmony_ci                [(type_ref, self.__register_types[type_ref]) for type_ref in type_refs],
649bf215546Sopenharmony_ci                field_keys={
650bf215546Sopenharmony_ci                    'enum_ref': merge_enum_refs,
651bf215546Sopenharmony_ci                },
652bf215546Sopenharmony_ci                union=True
653bf215546Sopenharmony_ci            ))
654bf215546Sopenharmony_ci
655bf215546Sopenharmony_ci        # Merge register mappings
656bf215546Sopenharmony_ci        register_mappings = self.__register_mappings
657bf215546Sopenharmony_ci        self.__register_mappings = []
658bf215546Sopenharmony_ci
659bf215546Sopenharmony_ci        regmap_accum = None
660bf215546Sopenharmony_ci        for regmap in register_mappings:
661bf215546Sopenharmony_ci            if regmap_accum and regmap.name != regmap_accum.name:
662bf215546Sopenharmony_ci                regmap_accum.chips = [newchip]
663bf215546Sopenharmony_ci                self.__register_mappings.append(regmap_accum)
664bf215546Sopenharmony_ci                regmap_accum = None
665bf215546Sopenharmony_ci
666bf215546Sopenharmony_ci            joining_chips = chips.intersection(regmap.chips)
667bf215546Sopenharmony_ci            if not joining_chips:
668bf215546Sopenharmony_ci                self.__register_mappings.append(regmap)
669bf215546Sopenharmony_ci                continue
670bf215546Sopenharmony_ci            remaining_chips = set(regmap.chips).difference(chips)
671bf215546Sopenharmony_ci
672bf215546Sopenharmony_ci            type_ref = getattr(regmap, 'type_ref', None)
673bf215546Sopenharmony_ci            if type_ref is None:
674bf215546Sopenharmony_ci                regmap.chips = sorted(remaining_chips.union([newchip]))
675bf215546Sopenharmony_ci                self.__register_mappings.append(regmap)
676bf215546Sopenharmony_ci                continue
677bf215546Sopenharmony_ci
678bf215546Sopenharmony_ci            type_ref = remap_type_refs.get(type_ref, type_ref)
679bf215546Sopenharmony_ci            if remaining_chips:
680bf215546Sopenharmony_ci                regmap.chips = sorted(remaining_chips)
681bf215546Sopenharmony_ci                self.__register_mappings.append(regmap)
682bf215546Sopenharmony_ci                if not regmap_accum:
683bf215546Sopenharmony_ci                    regmap = Object.from_json(Object.to_json(regmap))
684bf215546Sopenharmony_ci                    if type_ref is not None:
685bf215546Sopenharmony_ci                        regmap.type_ref = type_ref
686bf215546Sopenharmony_ci
687bf215546Sopenharmony_ci            if not regmap_accum:
688bf215546Sopenharmony_ci                regmap_accum = regmap
689bf215546Sopenharmony_ci            else:
690bf215546Sopenharmony_ci                if not hasattr(regmap_accum.type_ref, 'type_ref'):
691bf215546Sopenharmony_ci                    if type_ref is not None:
692bf215546Sopenharmony_ci                        regmap_accum.type_ref = type_ref
693bf215546Sopenharmony_ci                else:
694bf215546Sopenharmony_ci                    assert type_ref is None or type_ref == regmap_accum.type_ref
695bf215546Sopenharmony_ci        if regmap_accum:
696bf215546Sopenharmony_ci            self.__register_mappings.append(regmap_accum)
697bf215546Sopenharmony_ci
698bf215546Sopenharmony_ci    def update(self, other):
699bf215546Sopenharmony_ci        """
700bf215546Sopenharmony_ci        Add the contents of the other database to self.
701bf215546Sopenharmony_ci
702bf215546Sopenharmony_ci        Doesn't de-duplicate entries.
703bf215546Sopenharmony_ci        """
704bf215546Sopenharmony_ci        self.__post_init()
705bf215546Sopenharmony_ci        other.__post_init()
706bf215546Sopenharmony_ci
707bf215546Sopenharmony_ci        enum_remap = {}
708bf215546Sopenharmony_ci        regtype_remap = {}
709bf215546Sopenharmony_ci
710bf215546Sopenharmony_ci        for regmap in other.__register_mappings:
711bf215546Sopenharmony_ci            regmap = Object.from_json(Object.to_json(regmap))
712bf215546Sopenharmony_ci
713bf215546Sopenharmony_ci            type_ref = getattr(regmap, 'type_ref', None)
714bf215546Sopenharmony_ci            if type_ref is not None and type_ref not in regtype_remap:
715bf215546Sopenharmony_ci                regtype = Object.from_json(Object.to_json(other.__register_types[type_ref]))
716bf215546Sopenharmony_ci
717bf215546Sopenharmony_ci                chips = getattr(regmap, 'chips', [])
718bf215546Sopenharmony_ci                suffix = '_' + chips[0] if chips else ''
719bf215546Sopenharmony_ci
720bf215546Sopenharmony_ci                for field in regtype.fields:
721bf215546Sopenharmony_ci                    enum_ref = getattr(field, 'enum_ref', None)
722bf215546Sopenharmony_ci                    if enum_ref is not None and enum_ref not in enum_remap:
723bf215546Sopenharmony_ci                        enum = Object.from_json(Object.to_json(other.__enums[enum_ref]))
724bf215546Sopenharmony_ci
725bf215546Sopenharmony_ci                        remapped = enum_ref + suffix if enum_ref in self.__enums else enum_ref
726bf215546Sopenharmony_ci                        i = 0
727bf215546Sopenharmony_ci                        while remapped in self.__enums:
728bf215546Sopenharmony_ci                            remapped = enum_ref + suffix + str(i)
729bf215546Sopenharmony_ci                            i += 1
730bf215546Sopenharmony_ci                        self.add_enum(remapped, enum)
731bf215546Sopenharmony_ci                        enum_remap[enum_ref] = remapped
732bf215546Sopenharmony_ci
733bf215546Sopenharmony_ci                    if enum_ref is not None:
734bf215546Sopenharmony_ci                        field.enum_ref = enum_remap[enum_ref]
735bf215546Sopenharmony_ci
736bf215546Sopenharmony_ci                remapped = type_ref + suffix if type_ref in self.__register_types else type_ref
737bf215546Sopenharmony_ci                i = 0
738bf215546Sopenharmony_ci                while remapped in self.__register_types:
739bf215546Sopenharmony_ci                    remapped = type_ref + suffix + str(i)
740bf215546Sopenharmony_ci                    i += 1
741bf215546Sopenharmony_ci                self.add_register_type(remapped, regtype)
742bf215546Sopenharmony_ci                regtype_remap[type_ref] = remapped
743bf215546Sopenharmony_ci
744bf215546Sopenharmony_ci            if type_ref is not None:
745bf215546Sopenharmony_ci                regmap.type_ref = regtype_remap[type_ref]
746bf215546Sopenharmony_ci
747bf215546Sopenharmony_ci            self.add_register_mapping(regmap)
748bf215546Sopenharmony_ci
749bf215546Sopenharmony_ci    def to_json(self):
750bf215546Sopenharmony_ci        self.__post_init()
751bf215546Sopenharmony_ci        return {
752bf215546Sopenharmony_ci            'enums': Object.to_json(self.__enums),
753bf215546Sopenharmony_ci            'register_types': Object.to_json(self.__register_types),
754bf215546Sopenharmony_ci            'register_mappings': Object.to_json(self.__register_mappings),
755bf215546Sopenharmony_ci        }
756bf215546Sopenharmony_ci
757bf215546Sopenharmony_ci    def encode_json_pretty(self):
758bf215546Sopenharmony_ci        """
759bf215546Sopenharmony_ci        Use a custom JSON encoder which pretty prints, but keeps inner structures compact
760bf215546Sopenharmony_ci        """
761bf215546Sopenharmony_ci        # Since the JSON module isn't very extensible, this ends up being
762bf215546Sopenharmony_ci        # really hacky.
763bf215546Sopenharmony_ci        obj = self.to_json()
764bf215546Sopenharmony_ci
765bf215546Sopenharmony_ci        replacements = []
766bf215546Sopenharmony_ci        def placeholder(s):
767bf215546Sopenharmony_ci            placeholder = "JSON-{key}-NOSJ".format(key=len(replacements))
768bf215546Sopenharmony_ci            replacements.append(json.dumps(s, sort_keys=True))
769bf215546Sopenharmony_ci            return placeholder
770bf215546Sopenharmony_ci
771bf215546Sopenharmony_ci        # Pre-create non-indented encodings for inner objects
772bf215546Sopenharmony_ci        for enum in obj['enums'].values():
773bf215546Sopenharmony_ci            enum['entries'] = [
774bf215546Sopenharmony_ci                placeholder(entry)
775bf215546Sopenharmony_ci                for entry in enum['entries']
776bf215546Sopenharmony_ci            ]
777bf215546Sopenharmony_ci
778bf215546Sopenharmony_ci        for regtype in obj['register_types'].values():
779bf215546Sopenharmony_ci            regtype['fields'] = [
780bf215546Sopenharmony_ci                placeholder(field)
781bf215546Sopenharmony_ci                for field in regtype['fields']
782bf215546Sopenharmony_ci            ]
783bf215546Sopenharmony_ci
784bf215546Sopenharmony_ci        for regmap in obj['register_mappings']:
785bf215546Sopenharmony_ci            regmap['map'] = placeholder(regmap['map'])
786bf215546Sopenharmony_ci            if 'chips' in regmap:
787bf215546Sopenharmony_ci                regmap['chips'] = placeholder(regmap['chips'])
788bf215546Sopenharmony_ci
789bf215546Sopenharmony_ci        # Now create the 'outer' encoding with indentation and search-and-replace
790bf215546Sopenharmony_ci        # placeholders
791bf215546Sopenharmony_ci        result = json.dumps(obj, indent=1, sort_keys=True)
792bf215546Sopenharmony_ci
793bf215546Sopenharmony_ci        result = re.sub(
794bf215546Sopenharmony_ci            '"JSON-([0-9]+)-NOSJ"',
795bf215546Sopenharmony_ci            lambda m: replacements[int(m.group(1))],
796bf215546Sopenharmony_ci            result
797bf215546Sopenharmony_ci        )
798bf215546Sopenharmony_ci
799bf215546Sopenharmony_ci        return result
800bf215546Sopenharmony_ci
801bf215546Sopenharmony_ci    @staticmethod
802bf215546Sopenharmony_ci    def from_json(json):
803bf215546Sopenharmony_ci        db = RegisterDatabase()
804bf215546Sopenharmony_ci
805bf215546Sopenharmony_ci        db.__enums = dict((k, Object.from_json(v)) for k, v in json['enums'].items())
806bf215546Sopenharmony_ci        if 'register_types' in json:
807bf215546Sopenharmony_ci            db.__register_types = dict(
808bf215546Sopenharmony_ci                (k, Object.from_json(v))
809bf215546Sopenharmony_ci                for k, v in json['register_types'].items()
810bf215546Sopenharmony_ci            )
811bf215546Sopenharmony_ci        if 'register_mappings' in json:
812bf215546Sopenharmony_ci            db.__register_mappings = Object.from_json(json['register_mappings'])
813bf215546Sopenharmony_ci
814bf215546Sopenharmony_ci        # Old format
815bf215546Sopenharmony_ci        if 'registers' in json:
816bf215546Sopenharmony_ci            for reg in json['registers']:
817bf215546Sopenharmony_ci                type_ref = None
818bf215546Sopenharmony_ci                if 'fields' in reg and reg['fields']:
819bf215546Sopenharmony_ci                    type_ref = reg['names'][0]
820bf215546Sopenharmony_ci                    db.add_register_type(type_ref, Object(
821bf215546Sopenharmony_ci                        fields=Object.from_json(reg['fields'])
822bf215546Sopenharmony_ci                    ))
823bf215546Sopenharmony_ci
824bf215546Sopenharmony_ci                for name in reg['names']:
825bf215546Sopenharmony_ci                    regmap = Object(
826bf215546Sopenharmony_ci                        name=name,
827bf215546Sopenharmony_ci                        map=Object.from_json(reg['map'])
828bf215546Sopenharmony_ci                    )
829bf215546Sopenharmony_ci                    if type_ref is not None:
830bf215546Sopenharmony_ci                        regmap.type_ref = type_ref
831bf215546Sopenharmony_ci                    db.add_register_mapping(regmap)
832bf215546Sopenharmony_ci
833bf215546Sopenharmony_ci        db.__post_init()
834bf215546Sopenharmony_ci        return db
835bf215546Sopenharmony_ci
836bf215546Sopenharmony_cidef deduplicate_enums(regdb):
837bf215546Sopenharmony_ci    """
838bf215546Sopenharmony_ci    Find enums that have the exact same entries and merge them.
839bf215546Sopenharmony_ci    """
840bf215546Sopenharmony_ci    buckets = defaultdict(list)
841bf215546Sopenharmony_ci    for name, enum in regdb.enums():
842bf215546Sopenharmony_ci        buckets[RegisterDatabase.enum_key(enum)].append(name)
843bf215546Sopenharmony_ci
844bf215546Sopenharmony_ci    for bucket in buckets.values():
845bf215546Sopenharmony_ci        if len(bucket) > 1:
846bf215546Sopenharmony_ci            regdb.merge_enums(bucket, bucket[0])
847bf215546Sopenharmony_ci
848bf215546Sopenharmony_cidef deduplicate_register_types(regdb):
849bf215546Sopenharmony_ci    """
850bf215546Sopenharmony_ci    Find register types with the exact same fields (identified by name and
851bf215546Sopenharmony_ci    bit range) and merge them.
852bf215546Sopenharmony_ci
853bf215546Sopenharmony_ci    However, register types *aren't* merged if they have different enums for
854bf215546Sopenharmony_ci    the same field (as an exception, if one of them has an enum and the other
855bf215546Sopenharmony_ci    one doesn't, we assume that one is simply missing a bit of information and
856bf215546Sopenharmony_ci    merge the register types).
857bf215546Sopenharmony_ci    """
858bf215546Sopenharmony_ci    buckets = defaultdict(list)
859bf215546Sopenharmony_ci    for name, regtype in regdb.register_types():
860bf215546Sopenharmony_ci        key = ''.join(
861bf215546Sopenharmony_ci            ':{0}:{1}:{2}:'.format(
862bf215546Sopenharmony_ci                field.name, field.bits[0], field.bits[1],
863bf215546Sopenharmony_ci            )
864bf215546Sopenharmony_ci            for field in regtype.fields
865bf215546Sopenharmony_ci        )
866bf215546Sopenharmony_ci        buckets[key].append((name, regtype.fields))
867bf215546Sopenharmony_ci
868bf215546Sopenharmony_ci    for bucket in buckets.values():
869bf215546Sopenharmony_ci        # Register types in the same bucket have the same fields in the same
870bf215546Sopenharmony_ci        # places, but they may have different enum_refs. Allow merging when
871bf215546Sopenharmony_ci        # one has an enum_ref and another doesn't, but don't merge if they
872bf215546Sopenharmony_ci        # have enum_refs that differ.
873bf215546Sopenharmony_ci        bucket_enum_refs = [
874bf215546Sopenharmony_ci            [getattr(field, 'enum_ref', None) for field in fields]
875bf215546Sopenharmony_ci            for name, fields in bucket
876bf215546Sopenharmony_ci        ]
877bf215546Sopenharmony_ci        while bucket:
878bf215546Sopenharmony_ci            regtypes = [bucket[0][0]]
879bf215546Sopenharmony_ci            enum_refs = bucket_enum_refs[0]
880bf215546Sopenharmony_ci            del bucket[0]
881bf215546Sopenharmony_ci            del bucket_enum_refs[0]
882bf215546Sopenharmony_ci
883bf215546Sopenharmony_ci            idx = 0
884bf215546Sopenharmony_ci            while idx < len(bucket):
885bf215546Sopenharmony_ci                if all([
886bf215546Sopenharmony_ci                    not lhs or not rhs or lhs == rhs
887bf215546Sopenharmony_ci                    for lhs, rhs in zip(enum_refs, bucket_enum_refs[idx])
888bf215546Sopenharmony_ci                ]):
889bf215546Sopenharmony_ci                    regtypes.append(bucket[idx][0])
890bf215546Sopenharmony_ci                    enum_refs = [lhs or rhs for lhs, rhs in zip(enum_refs, bucket_enum_refs[idx])]
891bf215546Sopenharmony_ci                    del bucket[idx]
892bf215546Sopenharmony_ci                    del bucket_enum_refs[idx]
893bf215546Sopenharmony_ci                else:
894bf215546Sopenharmony_ci                    idx += 1
895bf215546Sopenharmony_ci
896bf215546Sopenharmony_ci            if len(regtypes) > 1:
897bf215546Sopenharmony_ci                regdb.merge_register_types(regtypes, regtypes[0])
898bf215546Sopenharmony_ci
899bf215546Sopenharmony_ci# kate: space-indent on; indent-width 4; replace-tabs on;
900