1 from __future__
import absolute_import
, division
, print_function
, unicode_literals
5 * Copyright 2015-2019 Advanced Micro Devices, Inc.
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * on the rights to use, copy, modify, merge, publish, distribute, sub
11 * license, and/or sell copies of the Software, and to permit persons to whom
12 * the Software is furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice (including the next
15 * paragraph) shall be included in all copies or substantial portions of the
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24 * USE OR OTHER DEALINGS IN THE SOFTWARE.
29 Create the (combined) register header from register JSON. Use --help for usage.
33 from collections
import defaultdict
39 from regdb
import Object
, RegisterDatabase
, deduplicate_enums
, deduplicate_register_types
42 ######### BEGIN HARDCODED CONFIGURATION
44 # Chips are sorted chronologically
46 Object(name
='gfx6', disambiguation
='GFX6'),
47 Object(name
='gfx7', disambiguation
='GFX7'),
48 Object(name
='gfx8', disambiguation
='GFX8'),
49 Object(name
='fiji', disambiguation
='GFX8'),
50 Object(name
='stoney', disambiguation
='GFX8'),
51 Object(name
='gfx9', disambiguation
='GFX9'),
52 Object(name
='gfx10', disambiguation
='GFX10'),
55 ######### END HARDCODED CONFIGURATION
57 def get_chip_index(chip
):
59 Given a chip name, return its index in the global CHIPS list.
61 return next(idx
for idx
, obj
in enumerate(CHIPS
) if obj
.name
== chip
)
63 def get_disambiguation_suffix(chips
):
65 Disambiguation suffix to be used for an enum entry or field name that
66 is supported in the given set of chips.
68 oldest_chip_index
= min([get_chip_index(chip
) for chip
in chips
])
69 return CHIPS
[oldest_chip_index
].disambiguation
71 def get_chips_comment(chips
, parent
=None):
73 Generate a user-friendly comment describing the given set of chips.
75 The return value may be None, if such a comment is deemed unnecessary.
77 parent is an optional set of chips supporting a parent structure, e.g.
78 where chips may be the set of chips supporting a specific enum value,
79 parent would be the set of chips supporting the field containing the enum,
80 the idea being that no comment is necessary if all chips that support the
81 parent also support the child.
83 chipflags
= [chip
.name
in chips
for chip
in CHIPS
]
87 if parent
is not None:
88 parentflags
= [chip
.name
in parent
for chip
in CHIPS
]
89 if all(childflag
or not parentflag
for childflag
, parentflag
in zip(chipflags
, parentflags
)):
93 for idx
, chip
, flag
in zip(itertools
.count(), CHIPS
, chipflags
):
99 for idx
, chip
, flag
in zip(itertools
.count(), reversed(CHIPS
), reversed(chipflags
)):
102 suffix
= len(CHIPS
) - idx
- 1
106 comment
.append('<= {0}'.format(CHIPS
[prefix
- 1].name
))
107 for chip
, flag
in zip(CHIPS
[prefix
:suffix
], chipflags
[prefix
:suffix
]):
109 comment
.append(chip
.name
)
110 if suffix
< len(CHIPS
):
111 comment
.append('>= {0}'.format(CHIPS
[suffix
].name
))
113 return ', '.join(comment
)
116 class HeaderWriter(object):
117 def __init__(self
, regdb
, guard
=None):
120 # The following contain: Object(address, chips, name, regmap/field/enumentry)
121 self
.register_lines
= []
122 self
.field_lines
= []
123 self
.value_lines
= []
125 regtype_emit
= defaultdict(set)
126 enum_emit
= defaultdict(set)
128 for regmap
in regdb
.register_mappings():
129 type_ref
= getattr(regmap
, 'type_ref', None)
130 self
.register_lines
.append(Object(
131 address
=regmap
.map.at
,
132 chips
=set(regmap
.chips
),
135 type_refs
=set([type_ref
]) if type_ref
else set(),
138 basename
= re
.sub(r
'[0-9]+', '', regmap
.name
)
139 key
= '{type_ref}::{basename}'.format(**locals())
140 if type_ref
is not None and regtype_emit
[key
].isdisjoint(regmap
.chips
):
141 regtype_emit
[key
].update(regmap
.chips
)
143 regtype
= regdb
.register_type(type_ref
)
144 for field
in regtype
.fields
:
145 if field
.name
== 'RESERVED':
148 enum_ref
= getattr(field
, 'enum_ref', None)
149 self
.field_lines
.append(Object(
150 address
=regmap
.map.at
,
151 chips
=set(regmap
.chips
),
155 type_refs
=set([type_ref
]) if type_ref
else set(),
156 enum_refs
=set([enum_ref
]) if enum_ref
else set(),
159 key
= '{type_ref}::{basename}::{enum_ref}'.format(**locals())
160 if enum_ref
is not None and enum_emit
[key
].isdisjoint(regmap
.chips
):
161 enum_emit
[key
].update(regmap
.chips
)
163 enum
= regdb
.enum(enum_ref
)
164 for entry
in enum
.entries
:
165 self
.value_lines
.append(Object(
166 address
=regmap
.map.at
,
167 chips
=set(regmap
.chips
),
170 enum_refs
=set([enum_ref
]) if enum_ref
else set(),
173 # Merge register lines
174 lines
= self
.register_lines
175 lines
.sort(key
=lambda line
: (line
.address
, line
.name
))
177 self
.register_lines
= []
179 prev
= self
.register_lines
[-1] if self
.register_lines
else None
180 if prev
and prev
.address
== line
.address
and prev
.name
== line
.name
:
181 prev
.chips
.update(line
.chips
)
182 prev
.type_refs
.update(line
.type_refs
)
184 self
.register_lines
.append(line
)
187 lines
= self
.field_lines
188 lines
.sort(key
=lambda line
: (line
.address
, line
.name
))
190 self
.field_lines
= []
193 for prev
in reversed(self
.field_lines
):
194 if prev
.address
!= line
.address
or prev
.name
!= line
.name
:
197 # Can merge fields if they have the same starting bit and the
198 # range of the field as intended by the current line does not
199 # conflict with any of the regtypes covered by prev.
200 if prev
.bits
[0] != line
.bits
[0]:
203 if prev
.bits
[1] < line
.bits
[1]:
204 # Current line's field extends beyond the range of prev.
205 # Need to check for conflicts
207 for type_ref
in prev
.type_refs
:
208 for field
in regdb
.register_type(type_ref
).fields
:
209 # The only possible conflict is for a prev field
210 # that starts at a higher bit.
211 if (field
.bits
[0] > line
.bits
[0] and
212 field
.bits
[0] <= line
.bits
[1]):
220 prev
.bits
[1] = max(prev
.bits
[1], line
.bits
[1])
221 prev
.chips
.update(line
.chips
)
222 prev
.type_refs
.update(line
.type_refs
)
223 prev
.enum_refs
.update(line
.enum_refs
)
227 self
.field_lines
.append(line
)
230 lines
= self
.value_lines
231 lines
.sort(key
=lambda line
: (line
.address
, line
.name
))
233 self
.value_lines
= []
235 for prev
in reversed(self
.value_lines
):
236 if prev
.address
== line
.address
and prev
.name
== line
.name
and\
237 prev
.enumentry
.value
== line
.enumentry
.value
:
238 prev
.chips
.update(line
.chips
)
239 prev
.enum_refs
.update(line
.enum_refs
)
242 self
.value_lines
.append(line
)
244 # Disambiguate field and value lines
245 for idx
, line
in enumerate(self
.field_lines
):
246 prev
= self
.field_lines
[idx
- 1] if idx
> 0 else None
247 next
= self
.field_lines
[idx
+ 1] if idx
+ 1 < len(self
.field_lines
) else None
248 if (prev
and prev
.address
== line
.address
and prev
.field
.name
== line
.field
.name
) or\
249 (next
and next
.address
== line
.address
and next
.field
.name
== line
.field
.name
):
250 line
.name
+= '_' + get_disambiguation_suffix(line
.chips
)
252 for idx
, line
in enumerate(self
.value_lines
):
253 prev
= self
.value_lines
[idx
- 1] if idx
> 0 else None
254 next
= self
.value_lines
[idx
+ 1] if idx
+ 1 < len(self
.value_lines
) else None
255 if (prev
and prev
.address
== line
.address
and prev
.enumentry
.name
== line
.enumentry
.name
) or\
256 (next
and next
.address
== line
.address
and next
.enumentry
.name
== line
.enumentry
.name
):
257 line
.name
+= '_' + get_disambiguation_suffix(line
.chips
)
259 def print(self
, filp
, sort
='address'):
261 Print out the entire register header.
263 if sort
== 'address':
264 self
.register_lines
.sort(key
=lambda line
: (line
.address
, line
.name
))
266 assert sort
== 'name'
267 self
.register_lines
.sort(key
=lambda line
: (line
.name
, line
.address
))
269 # Collect and sort field lines by address
270 field_lines_by_address
= defaultdict(list)
271 for line
in self
.field_lines
:
272 field_lines_by_address
[line
.address
].append(line
)
273 for field_lines
in field_lines_by_address
.values():
274 if sort
== 'address':
275 field_lines
.sort(key
=lambda line
: (line
.bits
[0], line
.name
))
277 field_lines
.sort(key
=lambda line
: (line
.name
, line
.bits
[0]))
279 # Collect and sort value lines by address
280 value_lines_by_address
= defaultdict(list)
281 for line
in self
.value_lines
:
282 value_lines_by_address
[line
.address
].append(line
)
283 for value_lines
in value_lines_by_address
.values():
284 if sort
== 'address':
285 value_lines
.sort(key
=lambda line
: (line
.enumentry
.value
, line
.name
))
287 value_lines
.sort(key
=lambda line
: (line
.name
, line
.enumentry
.value
))
289 print('/* Automatically generated by amd/registers/makeregheader.py */\n', file=filp
)
291 print(COPYRIGHT
.strip(), file=filp
)
295 print('#ifndef {self.guard}'.format(**locals()), file=filp
)
296 print('#define {self.guard}\n'.format(**locals()), file=filp
)
298 for register_line
in self
.register_lines
:
299 comment
= get_chips_comment(register_line
.chips
)
301 address
= '{0:X}'.format(register_line
.address
)
302 address
= address
.rjust(3 if register_line
.regmap
.map.to
== 'pkt3' else 6, '0')
304 define_name
= 'R_{address}_{register_line.name}'.format(**locals()).ljust(63)
305 comment
= ' /* {0} */'.format(comment
) if comment
else ''
306 print('#define {define_name} 0x{address}{comment}'.format(**locals()), file=filp
)
308 field_lines
= field_lines_by_address
[register_line
.address
]
310 while field_idx
< len(field_lines
):
311 field_line
= field_lines
[field_idx
]
313 if field_line
.type_refs
.isdisjoint(register_line
.type_refs
):
316 del field_lines
[field_idx
]
318 comment
= get_chips_comment(field_line
.chips
, register_line
.chips
)
320 mask
= (1 << (field_line
.bits
[1] - field_line
.bits
[0] + 1)) - 1
321 define_name
= '_{address}_{field_line.name}(x)'.format(**locals()).ljust(58)
322 comment
= ' /* {0} */'.format(comment
) if comment
else ''
324 '#define S{define_name} (((unsigned)(x) & 0x{mask:X}) << {field_line.bits[0]}){comment}'
325 .format(**locals()), file=filp
)
326 print('#define G{define_name} (((x) >> {field_line.bits[0]}) & 0x{mask:X})'
327 .format(**locals()), file=filp
)
329 complement
= ((1 << 32) - 1) ^
(mask
<< field_line
.bits
[0])
330 define_name
= '_{address}_{field_line.name}'.format(**locals()).ljust(58)
331 print('#define C{define_name} 0x{complement:08X}'
332 .format(**locals()), file=filp
)
334 value_lines
= value_lines_by_address
[register_line
.address
]
336 while value_idx
< len(value_lines
):
337 value_line
= value_lines
[value_idx
]
339 if value_line
.enum_refs
.isdisjoint(field_line
.enum_refs
):
342 del value_lines
[value_idx
]
344 comment
= get_chips_comment(value_line
.chips
, field_line
.chips
)
346 define_name
= 'V_{address}_{value_line.name}'.format(**locals()).ljust(55)
347 comment
= ' /* {0} */'.format(comment
) if comment
else ''
348 print('#define {define_name} {value_line.enumentry.value}{comment}'
349 .format(**locals()), file=filp
)
352 print('\n#endif // {self.guard}'.format(**locals()), file=filp
)
356 parser
= argparse
.ArgumentParser()
357 parser
.add_argument('--chip', dest
='chips', type=str, nargs
='*',
358 help='Chip for which to generate the header (all chips if unspecified)')
359 parser
.add_argument('--sort', choices
=['name', 'address'], default
='address',
360 help='Sort key for registers, fields, and enum values')
361 parser
.add_argument('--guard', type=str, help='Name of the #include guard')
362 parser
.add_argument('files', metavar
='FILE', type=str, nargs
='+',
363 help='Register database file')
364 args
= parser
.parse_args()
367 for filename
in args
.files
:
368 with
open(filename
, 'r') as filp
:
369 db
= RegisterDatabase
.from_json(json
.load(filp
))
375 deduplicate_enums(regdb
)
376 deduplicate_register_types(regdb
)
378 w
= HeaderWriter(regdb
, guard
=args
.guard
)
379 w
.print(sys
.stdout
, sort
=args
.sort
)
382 if __name__
== '__main__':
385 # kate: space-indent on; indent-width 4; replace-tabs on;