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