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