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