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'),
54 ######### END HARDCODED CONFIGURATION
56 def get_chip_index(chip
):
58 Given a chip name, return its index in the global CHIPS list.
60 return next(idx
for idx
, obj
in enumerate(CHIPS
) if obj
.name
== chip
)
62 def get_disambiguation_suffix(chips
):
64 Disambiguation suffix to be used for an enum entry or field name that
65 is supported in the given set of chips.
67 oldest_chip_index
= min([get_chip_index(chip
) for chip
in chips
])
68 return CHIPS
[oldest_chip_index
].disambiguation
70 def get_chips_comment(chips
, parent
=None):
72 Generate a user-friendly comment describing the given set of chips.
74 The return value may be None, if such a comment is deemed unnecessary.
76 parent is an optional set of chips supporting a parent structure, e.g.
77 where chips may be the set of chips supporting a specific enum value,
78 parent would be the set of chips supporting the field containing the enum,
79 the idea being that no comment is necessary if all chips that support the
80 parent also support the child.
82 chipflags
= [chip
.name
in chips
for chip
in CHIPS
]
86 if parent
is not None:
87 parentflags
= [chip
.name
in parent
for chip
in CHIPS
]
88 if all(childflag
or not parentflag
for childflag
, parentflag
in zip(chipflags
, parentflags
)):
92 for idx
, chip
, flag
in zip(itertools
.count(), CHIPS
, chipflags
):
98 for idx
, chip
, flag
in zip(itertools
.count(), reversed(CHIPS
), reversed(chipflags
)):
101 suffix
= len(CHIPS
) - idx
- 1
105 comment
.append('<= {0}'.format(CHIPS
[prefix
- 1].name
))
106 for chip
, flag
in zip(CHIPS
[prefix
:suffix
], chipflags
[prefix
:suffix
]):
108 comment
.append(chip
.name
)
109 if suffix
< len(CHIPS
):
110 comment
.append('>= {0}'.format(CHIPS
[suffix
].name
))
112 return ', '.join(comment
)
115 class HeaderWriter(object):
116 def __init__(self
, regdb
, guard
=None):
119 # The following contain: Object(address, chips, name, regmap/field/enumentry)
120 self
.register_lines
= []
121 self
.field_lines
= []
122 self
.value_lines
= []
124 regtype_emit
= defaultdict(set)
125 enum_emit
= defaultdict(set)
127 for regmap
in regdb
.register_mappings():
128 type_ref
= getattr(regmap
, 'type_ref', None)
129 self
.register_lines
.append(Object(
130 address
=regmap
.map.at
,
131 chips
=set(regmap
.chips
),
134 type_refs
=set([type_ref
]) if type_ref
else set(),
137 basename
= re
.sub(r
'[0-9]+', '', regmap
.name
)
138 key
= '{type_ref}::{basename}'.format(**locals())
139 if type_ref
is not None and regtype_emit
[key
].isdisjoint(regmap
.chips
):
140 regtype_emit
[key
].update(regmap
.chips
)
142 regtype
= regdb
.register_type(type_ref
)
143 for field
in regtype
.fields
:
144 if field
.name
== 'RESERVED':
147 enum_ref
= getattr(field
, 'enum_ref', None)
148 self
.field_lines
.append(Object(
149 address
=regmap
.map.at
,
150 chips
=set(regmap
.chips
),
154 type_refs
=set([type_ref
]) if type_ref
else set(),
155 enum_refs
=set([enum_ref
]) if enum_ref
else set(),
158 key
= '{type_ref}::{basename}::{enum_ref}'.format(**locals())
159 if enum_ref
is not None and enum_emit
[key
].isdisjoint(regmap
.chips
):
160 enum_emit
[key
].update(regmap
.chips
)
162 enum
= regdb
.enum(enum_ref
)
163 for entry
in enum
.entries
:
164 self
.value_lines
.append(Object(
165 address
=regmap
.map.at
,
166 chips
=set(regmap
.chips
),
169 enum_refs
=set([enum_ref
]) if enum_ref
else set(),
172 # Merge register lines
173 lines
= self
.register_lines
174 lines
.sort(key
=lambda line
: (line
.address
, line
.name
))
176 self
.register_lines
= []
178 prev
= self
.register_lines
[-1] if self
.register_lines
else None
179 if prev
and prev
.address
== line
.address
and prev
.name
== line
.name
:
180 prev
.chips
.update(line
.chips
)
181 prev
.type_refs
.update(line
.type_refs
)
183 self
.register_lines
.append(line
)
186 lines
= self
.field_lines
187 lines
.sort(key
=lambda line
: (line
.address
, line
.name
))
189 self
.field_lines
= []
192 for prev
in reversed(self
.field_lines
):
193 if prev
.address
!= line
.address
or prev
.name
!= line
.name
:
196 # Can merge fields if they have the same starting bit and the
197 # range of the field as intended by the current line does not
198 # conflict with any of the regtypes covered by prev.
199 if prev
.bits
[0] != line
.bits
[0]:
202 if prev
.bits
[1] < line
.bits
[1]:
203 # Current line's field extends beyond the range of prev.
204 # Need to check for conflicts
206 for type_ref
in prev
.type_refs
:
207 for field
in regdb
.register_type(type_ref
).fields
:
208 # The only possible conflict is for a prev field
209 # that starts at a higher bit.
210 if (field
.bits
[0] > line
.bits
[0] and
211 field
.bits
[0] <= line
.bits
[1]):
219 prev
.bits
[1] = max(prev
.bits
[1], line
.bits
[1])
220 prev
.chips
.update(line
.chips
)
221 prev
.type_refs
.update(line
.type_refs
)
222 prev
.enum_refs
.update(line
.enum_refs
)
226 self
.field_lines
.append(line
)
229 lines
= self
.value_lines
230 lines
.sort(key
=lambda line
: (line
.address
, line
.name
))
232 self
.value_lines
= []
234 for prev
in reversed(self
.value_lines
):
235 if prev
.address
== line
.address
and prev
.name
== line
.name
and\
236 prev
.enumentry
.value
== line
.enumentry
.value
:
237 prev
.chips
.update(line
.chips
)
238 prev
.enum_refs
.update(line
.enum_refs
)
241 self
.value_lines
.append(line
)
243 # Disambiguate field and value lines
244 for idx
, line
in enumerate(self
.field_lines
):
245 prev
= self
.field_lines
[idx
- 1] if idx
> 0 else None
246 next
= self
.field_lines
[idx
+ 1] if idx
+ 1 < len(self
.field_lines
) else None
247 if (prev
and prev
.address
== line
.address
and prev
.field
.name
== line
.field
.name
) or\
248 (next
and next
.address
== line
.address
and next
.field
.name
== line
.field
.name
):
249 line
.name
+= '_' + get_disambiguation_suffix(line
.chips
)
251 for idx
, line
in enumerate(self
.value_lines
):
252 prev
= self
.value_lines
[idx
- 1] if idx
> 0 else None
253 next
= self
.value_lines
[idx
+ 1] if idx
+ 1 < len(self
.value_lines
) else None
254 if (prev
and prev
.address
== line
.address
and prev
.enumentry
.name
== line
.enumentry
.name
) or\
255 (next
and next
.address
== line
.address
and next
.enumentry
.name
== line
.enumentry
.name
):
256 line
.name
+= '_' + get_disambiguation_suffix(line
.chips
)
258 def print(self
, filp
, sort
='address'):
260 Print out the entire register header.
262 if sort
== 'address':
263 self
.register_lines
.sort(key
=lambda line
: (line
.address
, line
.name
))
265 assert sort
== 'name'
266 self
.register_lines
.sort(key
=lambda line
: (line
.name
, line
.address
))
268 # Collect and sort field lines by address
269 field_lines_by_address
= defaultdict(list)
270 for line
in self
.field_lines
:
271 field_lines_by_address
[line
.address
].append(line
)
272 for field_lines
in field_lines_by_address
.values():
273 if sort
== 'address':
274 field_lines
.sort(key
=lambda line
: (line
.bits
[0], line
.name
))
276 field_lines
.sort(key
=lambda line
: (line
.name
, line
.bits
[0]))
278 # Collect and sort value lines by address
279 value_lines_by_address
= defaultdict(list)
280 for line
in self
.value_lines
:
281 value_lines_by_address
[line
.address
].append(line
)
282 for value_lines
in value_lines_by_address
.values():
283 if sort
== 'address':
284 value_lines
.sort(key
=lambda line
: (line
.enumentry
.value
, line
.name
))
286 value_lines
.sort(key
=lambda line
: (line
.name
, line
.enumentry
.value
))
288 print('/* Automatically generated by amd/registers/makeregheader.py */\n', file=filp
)
290 print(COPYRIGHT
.strip(), file=filp
)
294 print('#ifndef {self.guard}'.format(**locals()), file=filp
)
295 print('#define {self.guard}\n'.format(**locals()), file=filp
)
297 for register_line
in self
.register_lines
:
298 comment
= get_chips_comment(register_line
.chips
)
300 address
= '{0:X}'.format(register_line
.address
)
301 address
= address
.rjust(3 if register_line
.regmap
.map.to
== 'pkt3' else 6, '0')
303 define_name
= 'R_{address}_{register_line.name}'.format(**locals()).ljust(63)
304 comment
= ' /* {0} */'.format(comment
) if comment
else ''
305 print('#define {define_name} 0x{address}{comment}'.format(**locals()), file=filp
)
307 field_lines
= field_lines_by_address
[register_line
.address
]
309 while field_idx
< len(field_lines
):
310 field_line
= field_lines
[field_idx
]
312 if field_line
.type_refs
.isdisjoint(register_line
.type_refs
):
315 del field_lines
[field_idx
]
317 comment
= get_chips_comment(field_line
.chips
, register_line
.chips
)
319 mask
= (1 << (field_line
.bits
[1] - field_line
.bits
[0] + 1)) - 1
320 define_name
= '_{address}_{field_line.name}(x)'.format(**locals()).ljust(58)
321 comment
= ' /* {0} */'.format(comment
) if comment
else ''
323 '#define S{define_name} (((unsigned)(x) & 0x{mask:X}) << {field_line.bits[0]}){comment}'
324 .format(**locals()), file=filp
)
325 print('#define G{define_name} (((x) >> {field_line.bits[0]}) & 0x{mask:X})'
326 .format(**locals()), file=filp
)
328 complement
= ((1 << 32) - 1) ^
(mask
<< field_line
.bits
[0])
329 define_name
= '_{address}_{field_line.name}'.format(**locals()).ljust(58)
330 print('#define C{define_name} 0x{complement:08X}'
331 .format(**locals()), file=filp
)
333 value_lines
= value_lines_by_address
[register_line
.address
]
335 while value_idx
< len(value_lines
):
336 value_line
= value_lines
[value_idx
]
338 if value_line
.enum_refs
.isdisjoint(field_line
.enum_refs
):
341 del value_lines
[value_idx
]
343 comment
= get_chips_comment(value_line
.chips
, field_line
.chips
)
345 define_name
= 'V_{address}_{value_line.name}'.format(**locals()).ljust(55)
346 comment
= ' /* {0} */'.format(comment
) if comment
else ''
347 print('#define {define_name} {value_line.enumentry.value}{comment}'
348 .format(**locals()), file=filp
)
351 print('\n#endif // {self.guard}'.format(**locals()), file=filp
)
355 parser
= argparse
.ArgumentParser()
356 parser
.add_argument('--chip', dest
='chips', type=str, nargs
='*',
357 help='Chip for which to generate the header (all chips if unspecified)')
358 parser
.add_argument('--sort', choices
=['name', 'address'], default
='address',
359 help='Sort key for registers, fields, and enum values')
360 parser
.add_argument('--guard', type=str, help='Name of the #include guard')
361 parser
.add_argument('files', metavar
='FILE', type=str, nargs
='+',
362 help='Register database file')
363 args
= parser
.parse_args()
366 for filename
in args
.files
:
367 with
open(filename
, 'r') as filp
:
368 db
= RegisterDatabase
.from_json(json
.load(filp
))
374 deduplicate_enums(regdb
)
375 deduplicate_register_types(regdb
)
377 w
= HeaderWriter(regdb
, guard
=args
.guard
)
378 w
.print(sys
.stdout
, sort
=args
.sort
)
381 if __name__
== '__main__':
384 # kate: space-indent on; indent-width 4; replace-tabs on;