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