1COPYRIGHT = ''' 2/* 3 * Copyright 2015-2019 Advanced Micro Devices, Inc. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * on the rights to use, copy, modify, merge, publish, distribute, sub 9 * license, and/or sell copies of the Software, and to permit persons to whom 10 * the Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the next 13 * paragraph) shall be included in all copies or substantial portions of the 14 * Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, 20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 22 * USE OR OTHER DEALINGS IN THE SOFTWARE. 23 * 24 */ 25''' 26""" 27Create the (combined) register header from register JSON. Use --help for usage. 28""" 29 30import argparse 31from collections import defaultdict 32import itertools 33import json 34import re 35import sys 36 37from regdb import Object, RegisterDatabase, deduplicate_enums, deduplicate_register_types 38 39 40######### BEGIN HARDCODED CONFIGURATION 41 42# Chips are sorted chronologically 43CHIPS = [ 44 Object(name='gfx6', disambiguation='GFX6'), 45 Object(name='gfx7', disambiguation='GFX7'), 46 Object(name='gfx8', disambiguation='GFX8'), 47 Object(name='gfx81', disambiguation='GFX81'), 48 Object(name='gfx9', disambiguation='GFX9'), 49 Object(name='gfx10', disambiguation='GFX10'), 50 Object(name='gfx103', disambiguation='GFX103'), 51 Object(name='gfx11', disambiguation='GFX11'), 52] 53 54######### END HARDCODED CONFIGURATION 55 56def get_chip_index(chip): 57 """ 58 Given a chip name, return its index in the global CHIPS list. 59 """ 60 return next(idx for idx, obj in enumerate(CHIPS) if obj.name == chip) 61 62def get_disambiguation_suffix(chips): 63 """ 64 Disambiguation suffix to be used for an enum entry or field name that 65 is supported in the given set of chips. 66 """ 67 oldest_chip_index = min([get_chip_index(chip) for chip in chips]) 68 return CHIPS[oldest_chip_index].disambiguation 69 70def get_chips_comment(chips, parent=None): 71 """ 72 Generate a user-friendly comment describing the given set of chips. 73 74 The return value may be None, if such a comment is deemed unnecessary. 75 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. 81 """ 82 chipflags = [chip.name in chips for chip in CHIPS] 83 if all(chipflags): 84 return None 85 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)): 89 return None 90 91 prefix = 0 92 for idx, chip, flag in zip(itertools.count(), CHIPS, chipflags): 93 if not flag: 94 break 95 prefix = idx + 1 96 97 suffix = len(CHIPS) 98 for idx, chip, flag in zip(itertools.count(), reversed(CHIPS), reversed(chipflags)): 99 if not flag: 100 break 101 suffix = len(CHIPS) - idx - 1 102 103 comment = [] 104 if prefix > 0: 105 comment.append('<= {0}'.format(CHIPS[prefix - 1].name)) 106 for chip, flag in zip(CHIPS[prefix:suffix], chipflags[prefix:suffix]): 107 if flag: 108 comment.append(chip.name) 109 if suffix < len(CHIPS): 110 comment.append('>= {0}'.format(CHIPS[suffix].name)) 111 112 return ', '.join(comment) 113 114def detect_conflict(regdb, field_in_type1, field_in_type2): 115 """ 116 Returns False if field_in_type1 and field_in_type2 can be merged 117 into a single field = if writing to field_in_type1 bits won't 118 overwrite adjacent fields in type2, and the other way around. 119 """ 120 for idx, type_refs in enumerate([field_in_type1.type_refs, field_in_type2.type_refs]): 121 ref = field_in_type2 if idx == 0 else field_in_type1 122 for type_ref in type_refs: 123 for field in regdb.register_type(type_ref).fields: 124 # If a different field in the other type starts in 125 # the tested field's bits[0, 1] interval 126 if (field.bits[0] > ref.bits[0] and 127 field.bits[0] <= ref.bits[1]): 128 return True 129 130 return False 131 132class HeaderWriter(object): 133 def __init__(self, regdb, guard=None): 134 self.guard = guard 135 136 # The following contain: Object(address, chips, name, regmap/field/enumentry) 137 self.register_lines = [] 138 self.field_lines = [] 139 self.value_lines = [] 140 141 regtype_emit = defaultdict(set) 142 enum_emit = defaultdict(set) 143 144 for regmap in regdb.register_mappings(): 145 type_ref = getattr(regmap, 'type_ref', None) 146 self.register_lines.append(Object( 147 address=regmap.map.at, 148 chips=set(regmap.chips), 149 name=regmap.name, 150 regmap=regmap, 151 type_refs=set([type_ref]) if type_ref else set(), 152 )) 153 154 basename = re.sub(r'[0-9]+', '', regmap.name) 155 key = '{type_ref}::{basename}'.format(**locals()) 156 if type_ref is not None and regtype_emit[key].isdisjoint(regmap.chips): 157 regtype_emit[key].update(regmap.chips) 158 159 regtype = regdb.register_type(type_ref) 160 for field in regtype.fields: 161 if field.name == 'RESERVED': 162 continue 163 164 enum_ref = getattr(field, 'enum_ref', None) 165 self.field_lines.append(Object( 166 address=regmap.map.at, 167 chips=set(regmap.chips), 168 name=field.name, 169 field=field, 170 bits=field.bits[:], 171 type_refs=set([type_ref]) if type_ref else set(), 172 enum_refs=set([enum_ref]) if enum_ref else set(), 173 )) 174 175 key = '{type_ref}::{basename}::{enum_ref}'.format(**locals()) 176 if enum_ref is not None and enum_emit[key].isdisjoint(regmap.chips): 177 enum_emit[key].update(regmap.chips) 178 179 enum = regdb.enum(enum_ref) 180 for entry in enum.entries: 181 self.value_lines.append(Object( 182 address=regmap.map.at, 183 chips=set(regmap.chips), 184 name=entry.name, 185 enumentry=entry, 186 enum_refs=set([enum_ref]) if enum_ref else set(), 187 )) 188 189 # Merge register lines 190 lines = self.register_lines 191 lines.sort(key=lambda line: (line.address, line.name)) 192 193 self.register_lines = [] 194 for line in lines: 195 prev = self.register_lines[-1] if self.register_lines else None 196 if prev and prev.address == line.address and prev.name == line.name: 197 prev.chips.update(line.chips) 198 prev.type_refs.update(line.type_refs) 199 continue 200 self.register_lines.append(line) 201 202 # Merge field lines 203 lines = self.field_lines 204 lines.sort(key=lambda line: (line.address, line.name)) 205 206 self.field_lines = [] 207 for line in lines: 208 merged = False 209 for prev in reversed(self.field_lines): 210 if prev.address != line.address or prev.name != line.name: 211 break 212 213 # Can merge fields if they have the same starting bit and the 214 # range of the field as intended by the current line does not 215 # conflict with any of the regtypes covered by prev. 216 if prev.bits[0] != line.bits[0]: 217 continue 218 219 if prev.bits[1] != line.bits[1]: 220 # Current line's field extends beyond the range of prev. 221 # Need to check for conflicts 222 if detect_conflict(regdb, prev, line): 223 continue 224 225 prev.bits[1] = max(prev.bits[1], line.bits[1]) 226 prev.chips.update(line.chips) 227 prev.type_refs.update(line.type_refs) 228 prev.enum_refs.update(line.enum_refs) 229 merged = True 230 break 231 if not merged: 232 self.field_lines.append(line) 233 234 # Merge value lines 235 lines = self.value_lines 236 lines.sort(key=lambda line: (line.address, line.name)) 237 238 self.value_lines = [] 239 for line in lines: 240 for prev in reversed(self.value_lines): 241 if prev.address == line.address and prev.name == line.name and\ 242 prev.enumentry.value == line.enumentry.value: 243 prev.chips.update(line.chips) 244 prev.enum_refs.update(line.enum_refs) 245 break 246 else: 247 self.value_lines.append(line) 248 249 # Disambiguate field and value lines 250 for idx, line in enumerate(self.field_lines): 251 prev = self.field_lines[idx - 1] if idx > 0 else None 252 next = self.field_lines[idx + 1] if idx + 1 < len(self.field_lines) else None 253 if (prev and prev.address == line.address and prev.field.name == line.field.name) or\ 254 (next and next.address == line.address and next.field.name == line.field.name): 255 line.name += '_' + get_disambiguation_suffix(line.chips) 256 257 for idx, line in enumerate(self.value_lines): 258 prev = self.value_lines[idx - 1] if idx > 0 else None 259 next = self.value_lines[idx + 1] if idx + 1 < len(self.value_lines) else None 260 if (prev and prev.address == line.address and prev.enumentry.name == line.enumentry.name) or\ 261 (next and next.address == line.address and next.enumentry.name == line.enumentry.name): 262 line.name += '_' + get_disambiguation_suffix(line.chips) 263 264 def print(self, filp, sort='address'): 265 """ 266 Print out the entire register header. 267 """ 268 if sort == 'address': 269 self.register_lines.sort(key=lambda line: (line.address, line.name)) 270 else: 271 assert sort == 'name' 272 self.register_lines.sort(key=lambda line: (line.name, line.address)) 273 274 # Collect and sort field lines by address 275 field_lines_by_address = defaultdict(list) 276 for line in self.field_lines: 277 field_lines_by_address[line.address].append(line) 278 for field_lines in field_lines_by_address.values(): 279 if sort == 'address': 280 field_lines.sort(key=lambda line: (line.bits[0], line.name)) 281 else: 282 field_lines.sort(key=lambda line: (line.name, line.bits[0])) 283 284 # Collect and sort value lines by address 285 value_lines_by_address = defaultdict(list) 286 for line in self.value_lines: 287 value_lines_by_address[line.address].append(line) 288 for value_lines in value_lines_by_address.values(): 289 if sort == 'address': 290 value_lines.sort(key=lambda line: (line.enumentry.value, line.name)) 291 else: 292 value_lines.sort(key=lambda line: (line.name, line.enumentry.value)) 293 294 print('/* Automatically generated by amd/registers/makeregheader.py */\n', file=filp) 295 print(file=filp) 296 print(COPYRIGHT.strip(), file=filp) 297 print(file=filp) 298 299 if self.guard: 300 print('#ifndef {self.guard}'.format(**locals()), file=filp) 301 print('#define {self.guard}\n'.format(**locals()), file=filp) 302 303 for register_line in self.register_lines: 304 comment = get_chips_comment(register_line.chips) 305 306 address = '{0:X}'.format(register_line.address) 307 address = address.rjust(3 if register_line.regmap.map.to == 'pkt3' else 6, '0') 308 309 define_name = 'R_{address}_{register_line.name}'.format(**locals()).ljust(63) 310 comment = ' /* {0} */'.format(comment) if comment else '' 311 print('#define {define_name} 0x{address}{comment}'.format(**locals()), file=filp) 312 313 field_lines = field_lines_by_address[register_line.address] 314 field_idx = 0 315 while field_idx < len(field_lines): 316 field_line = field_lines[field_idx] 317 318 if field_line.type_refs.isdisjoint(register_line.type_refs): 319 field_idx += 1 320 continue 321 del field_lines[field_idx] 322 323 comment = get_chips_comment(field_line.chips, register_line.chips) 324 325 mask = (1 << (field_line.bits[1] - field_line.bits[0] + 1)) - 1 326 define_name = '_{address}_{field_line.name}(x)'.format(**locals()).ljust(58) 327 comment = ' /* {0} */'.format(comment) if comment else '' 328 print( 329 '#define S{define_name} (((unsigned)(x) & 0x{mask:X}) << {field_line.bits[0]}){comment}' 330 .format(**locals()), file=filp) 331 print('#define G{define_name} (((x) >> {field_line.bits[0]}) & 0x{mask:X})' 332 .format(**locals()), file=filp) 333 334 complement = ((1 << 32) - 1) ^ (mask << field_line.bits[0]) 335 define_name = '_{address}_{field_line.name}'.format(**locals()).ljust(58) 336 print('#define C{define_name} 0x{complement:08X}' 337 .format(**locals()), file=filp) 338 339 value_lines = value_lines_by_address[register_line.address] 340 value_idx = 0 341 while value_idx < len(value_lines): 342 value_line = value_lines[value_idx] 343 344 if value_line.enum_refs.isdisjoint(field_line.enum_refs): 345 value_idx += 1 346 continue 347 del value_lines[value_idx] 348 349 comment = get_chips_comment(value_line.chips, field_line.chips) 350 351 define_name = 'V_{address}_{value_line.name}'.format(**locals()).ljust(55) 352 comment = ' /* {0} */'.format(comment) if comment else '' 353 print('#define {define_name} {value_line.enumentry.value}{comment}' 354 .format(**locals()), file=filp) 355 356 if self.guard: 357 print('\n#endif // {self.guard}'.format(**locals()), file=filp) 358 359 360def main(): 361 parser = argparse.ArgumentParser() 362 parser.add_argument('--chip', dest='chips', type=str, nargs='*', 363 help='Chip for which to generate the header (all chips if unspecified)') 364 parser.add_argument('--sort', choices=['name', 'address'], default='address', 365 help='Sort key for registers, fields, and enum values') 366 parser.add_argument('--guard', type=str, help='Name of the #include guard') 367 parser.add_argument('files', metavar='FILE', type=str, nargs='+', 368 help='Register database file') 369 args = parser.parse_args() 370 371 regdb = None 372 for filename in args.files: 373 with open(filename, 'r') as filp: 374 db = RegisterDatabase.from_json(json.load(filp)) 375 if regdb is None: 376 regdb = db 377 else: 378 regdb.update(db) 379 380 deduplicate_enums(regdb) 381 deduplicate_register_types(regdb) 382 383 w = HeaderWriter(regdb, guard=args.guard) 384 w.print(sys.stdout, sort=args.sort) 385 386 387if __name__ == '__main__': 388 main() 389 390# kate: space-indent on; indent-width 4; replace-tabs on; 391