1from __future__ import absolute_import, division, print_function, unicode_literals 2 3COPYRIGHT = ''' 4/* 5 * Copyright 2015-2019 Advanced Micro Devices, Inc. 6 * 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: 13 * 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 16 * Software. 17 * 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. 25 * 26 */ 27''' 28""" 29Create the (combined) register header from register JSON. Use --help for usage. 30""" 31 32import argparse 33from collections import defaultdict 34import itertools 35import json 36import re 37import sys 38 39from regdb import Object, RegisterDatabase, deduplicate_enums, deduplicate_register_types 40 41 42######### BEGIN HARDCODED CONFIGURATION 43 44# Chips are sorted chronologically 45CHIPS = [ 46 Object(name='gfx6', disambiguation='GFX6'), 47 Object(name='gfx7', disambiguation='GFX7'), 48 Object(name='gfx8', disambiguation='GFX8'), 49 Object(name='gfx81', disambiguation='GFX81'), 50 Object(name='gfx9', disambiguation='GFX9'), 51 Object(name='gfx10', disambiguation='GFX10'), 52 Object(name='gfx103', disambiguation='GFX103'), 53] 54 55######### END HARDCODED CONFIGURATION 56 57def get_chip_index(chip): 58 """ 59 Given a chip name, return its index in the global CHIPS list. 60 """ 61 return next(idx for idx, obj in enumerate(CHIPS) if obj.name == chip) 62 63def get_disambiguation_suffix(chips): 64 """ 65 Disambiguation suffix to be used for an enum entry or field name that 66 is supported in the given set of chips. 67 """ 68 oldest_chip_index = min([get_chip_index(chip) for chip in chips]) 69 return CHIPS[oldest_chip_index].disambiguation 70 71def get_chips_comment(chips, parent=None): 72 """ 73 Generate a user-friendly comment describing the given set of chips. 74 75 The return value may be None, if such a comment is deemed unnecessary. 76 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. 82 """ 83 chipflags = [chip.name in chips for chip in CHIPS] 84 if all(chipflags): 85 return None 86 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)): 90 return None 91 92 prefix = 0 93 for idx, chip, flag in zip(itertools.count(), CHIPS, chipflags): 94 if not flag: 95 break 96 prefix = idx + 1 97 98 suffix = len(CHIPS) 99 for idx, chip, flag in zip(itertools.count(), reversed(CHIPS), reversed(chipflags)): 100 if not flag: 101 break 102 suffix = len(CHIPS) - idx - 1 103 104 comment = [] 105 if prefix > 0: 106 comment.append('<= {0}'.format(CHIPS[prefix - 1].name)) 107 for chip, flag in zip(CHIPS[prefix:suffix], chipflags[prefix:suffix]): 108 if flag: 109 comment.append(chip.name) 110 if suffix < len(CHIPS): 111 comment.append('>= {0}'.format(CHIPS[suffix].name)) 112 113 return ', '.join(comment) 114 115 116class HeaderWriter(object): 117 def __init__(self, regdb, guard=None): 118 self.guard = guard 119 120 # The following contain: Object(address, chips, name, regmap/field/enumentry) 121 self.register_lines = [] 122 self.field_lines = [] 123 self.value_lines = [] 124 125 regtype_emit = defaultdict(set) 126 enum_emit = defaultdict(set) 127 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), 133 name=regmap.name, 134 regmap=regmap, 135 type_refs=set([type_ref]) if type_ref else set(), 136 )) 137 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) 142 143 regtype = regdb.register_type(type_ref) 144 for field in regtype.fields: 145 if field.name == 'RESERVED': 146 continue 147 148 enum_ref = getattr(field, 'enum_ref', None) 149 self.field_lines.append(Object( 150 address=regmap.map.at, 151 chips=set(regmap.chips), 152 name=field.name, 153 field=field, 154 bits=field.bits[:], 155 type_refs=set([type_ref]) if type_ref else set(), 156 enum_refs=set([enum_ref]) if enum_ref else set(), 157 )) 158 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) 162 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), 168 name=entry.name, 169 enumentry=entry, 170 enum_refs=set([enum_ref]) if enum_ref else set(), 171 )) 172 173 # Merge register lines 174 lines = self.register_lines 175 lines.sort(key=lambda line: (line.address, line.name)) 176 177 self.register_lines = [] 178 for line in 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) 183 continue 184 self.register_lines.append(line) 185 186 # Merge field lines 187 lines = self.field_lines 188 lines.sort(key=lambda line: (line.address, line.name)) 189 190 self.field_lines = [] 191 for line in lines: 192 merged = False 193 for prev in reversed(self.field_lines): 194 if prev.address != line.address or prev.name != line.name: 195 break 196 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]: 201 continue 202 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 206 conflict = False 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]): 213 conflict = True 214 break 215 if conflict: 216 break 217 if conflict: 218 continue 219 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) 224 merged = True 225 break 226 if not merged: 227 self.field_lines.append(line) 228 229 # Merge value lines 230 lines = self.value_lines 231 lines.sort(key=lambda line: (line.address, line.name)) 232 233 self.value_lines = [] 234 for line in 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) 240 break 241 else: 242 self.value_lines.append(line) 243 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) 251 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) 258 259 def print(self, filp, sort='address'): 260 """ 261 Print out the entire register header. 262 """ 263 if sort == 'address': 264 self.register_lines.sort(key=lambda line: (line.address, line.name)) 265 else: 266 assert sort == 'name' 267 self.register_lines.sort(key=lambda line: (line.name, line.address)) 268 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)) 276 else: 277 field_lines.sort(key=lambda line: (line.name, line.bits[0])) 278 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)) 286 else: 287 value_lines.sort(key=lambda line: (line.name, line.enumentry.value)) 288 289 print('/* Automatically generated by amd/registers/makeregheader.py */\n', file=filp) 290 print(file=filp) 291 print(COPYRIGHT.strip(), file=filp) 292 print(file=filp) 293 294 if self.guard: 295 print('#ifndef {self.guard}'.format(**locals()), file=filp) 296 print('#define {self.guard}\n'.format(**locals()), file=filp) 297 298 for register_line in self.register_lines: 299 comment = get_chips_comment(register_line.chips) 300 301 address = '{0:X}'.format(register_line.address) 302 address = address.rjust(3 if register_line.regmap.map.to == 'pkt3' else 6, '0') 303 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) 307 308 field_lines = field_lines_by_address[register_line.address] 309 field_idx = 0 310 while field_idx < len(field_lines): 311 field_line = field_lines[field_idx] 312 313 if field_line.type_refs.isdisjoint(register_line.type_refs): 314 field_idx += 1 315 continue 316 del field_lines[field_idx] 317 318 comment = get_chips_comment(field_line.chips, register_line.chips) 319 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 '' 323 print( 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) 328 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) 333 334 value_lines = value_lines_by_address[register_line.address] 335 value_idx = 0 336 while value_idx < len(value_lines): 337 value_line = value_lines[value_idx] 338 339 if value_line.enum_refs.isdisjoint(field_line.enum_refs): 340 value_idx += 1 341 continue 342 del value_lines[value_idx] 343 344 comment = get_chips_comment(value_line.chips, field_line.chips) 345 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) 350 351 if self.guard: 352 print('\n#endif // {self.guard}'.format(**locals()), file=filp) 353 354 355def main(): 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() 365 366 regdb = None 367 for filename in args.files: 368 with open(filename, 'r') as filp: 369 db = RegisterDatabase.from_json(json.load(filp)) 370 if regdb is None: 371 regdb = db 372 else: 373 regdb.update(db) 374 375 deduplicate_enums(regdb) 376 deduplicate_register_types(regdb) 377 378 w = HeaderWriter(regdb, guard=args.guard) 379 w.print(sys.stdout, sort=args.sort) 380 381 382if __name__ == '__main__': 383 main() 384 385# kate: space-indent on; indent-width 4; replace-tabs on; 386