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