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