"Fossies" - the Fresh Open Source Software Archive

Member "mesa-20.1.8/src/amd/registers/regdb.py" (16 Sep 2020, 32263 Bytes) of package /linux/misc/mesa-20.1.8.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "regdb.py" see the Fossies "Dox" file reference documentation.

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