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] 52 53######### END HARDCODED CONFIGURATION 54 55def get_chip_index(chip): 56 """ 57 Given a chip name, return its index in the global CHIPS list. 58 """ 59 return next(idx for idx, obj in enumerate(CHIPS) if obj.name == chip) 60 61def get_disambiguation_suffix(chips): 62 """ 63 Disambiguation suffix to be used for an enum entry or field name that 64 is supported in the given set of chips. 65 """ 66 oldest_chip_index = min([get_chip_index(chip) for chip in chips]) 67 return CHIPS[oldest_chip_index].disambiguation 68 69def get_chips_comment(chips, parent=None): 70 """ 71 Generate a user-friendly comment describing the given set of chips. 72 73 The return value may be None, if such a comment is deemed unnecessary. 74 75 parent is an optional set of chips supporting a parent structure, e.g. 76 where chips may be the set of chips supporting a specific enum value, 77 parent would be the set of chips supporting the field containing the enum, 78 the idea being that no comment is necessary if all chips that support the 79 parent also support the child. 80 """ 81 chipflags = [chip.name in chips for chip in CHIPS] 82 if all(chipflags): 83 return None 84 85 if parent is not None: 86 parentflags = [chip.name in parent for chip in CHIPS] 87 if all(childflag or not parentflag for childflag, parentflag in zip(chipflags, parentflags)): 88 return None 89 90 prefix = 0 91 for idx, chip, flag in zip(itertools.count(), CHIPS, chipflags): 92 if not flag: 93 break 94 prefix = idx + 1 95 96 suffix = len(CHIPS) 97 for idx, chip, flag in zip(itertools.count(), reversed(CHIPS), reversed(chipflags)): 98 if not flag: 99 break 100 suffix = len(CHIPS) - idx - 1 101 102 comment = [] 103 if prefix > 0: 104 comment.append('<= {0}'.format(CHIPS[prefix - 1].name)) 105 for chip, flag in zip(CHIPS[prefix:suffix], chipflags[prefix:suffix]): 106 if flag: 107 comment.append(chip.name) 108 if suffix < len(CHIPS): 109 comment.append('>= {0}'.format(CHIPS[suffix].name)) 110 111 return ', '.join(comment) 112 113def detect_conflict(regdb, field_in_type1, field_in_type2): 114 """ 115 Returns False if field_in_type1 and field_in_type2 can be merged 116 into a single field = if writing to field_in_type1 bits won't 117 overwrite adjacent fields in type2, and the other way around. 118 """ 119 for idx, type_refs in enumerate([field_in_type1.type_refs, field_in_type2.type_refs]): 120 ref = field_in_type2 if idx == 0 else field_in_type1 121 for type_ref in type_refs: 122 for field in regdb.register_type(type_ref).fields: 123 # If a different field in the other type starts in 124 # the tested field's bits[0, 1] interval 125 if (field.bits[0] > ref.bits[0] and 126 field.bits[0] <= ref.bits[1]): 127 return True 128 129 return False 130 131class HeaderWriter(object): 132 def __init__(self, regdb, guard=None): 133 self.guard = guard 134 135 # The following contain: Object(address, chips, name, regmap/field/enumentry) 136 self.register_lines = [] 137 self.field_lines = [] 138 self.value_lines = [] 139 140 regtype_emit = defaultdict(set) 141 enum_emit = defaultdict(set) 142 143 for regmap in regdb.register_mappings(): 144 type_ref = getattr(regmap, 'type_ref', None) 145 self.register_lines.append(Object( 146 address=regmap.map.at, 147 chips=set(regmap.chips), 148 name=regmap.name, 149 regmap=regmap, 150 type_refs=set([type_ref]) if type_ref else set(), 151 )) 152 153 basename = re.sub(r'[0-9]+', '', regmap.name) 154 key = '{type_ref}::{basename}'.format(**locals()) 155 if type_ref is not None and regtype_emit[key].isdisjoint(regmap.chips): 156 regtype_emit[key].update(regmap.chips) 157 158 regtype = regdb.register_type(type_ref) 159 for field in regtype.fields: 160 if field.name == 'RESERVED': 161 continue 162 163 enum_ref = getattr(field, 'enum_ref', None) 164 self.field_lines.append(Object( 165 address=regmap.map.at, 166 chips=set(regmap.chips), 167 name=field.name, 168 field=field, 169 bits=field.bits[:], 170 type_refs=set([type_ref]) if type_ref else set(), 171 enum_refs=set([enum_ref]) if enum_ref else set(), 172 )) 173 174 key = '{type_ref}::{basename}::{enum_ref}'.format(**locals()) 175 if enum_ref is not None and enum_emit[key].isdisjoint(regmap.chips): 176 enum_emit[key].update(regmap.chips) 177 178 enum = regdb.enum(enum_ref) 179 for entry in enum.entries: 180 self.value_lines.append(Object( 181 address=regmap.map.at, 182 chips=set(regmap.chips), 183 name=entry.name, 184 enumentry=entry, 185 enum_refs=set([enum_ref]) if enum_ref else set(), 186 )) 187 188 # Merge register lines 189 lines = self.register_lines 190 lines.sort(key=lambda line: (line.address, line.name)) 191 192 self.register_lines = [] 193 for line in lines: 194 prev = self.register_lines[-1] if self.register_lines else None 195 if prev and prev.address == line.address and prev.name == line.name: 196 prev.chips.update(line.chips) 197 prev.type_refs.update(line.type_refs) 198 continue 199 self.register_lines.append(line) 200 201 # Merge field lines 202 lines = self.field_lines 203 lines.sort(key=lambda line: (line.address, line.name)) 204 205 self.field_lines = [] 206 for line in lines: 207 merged = False 208 for prev in reversed(self.field_lines): 209 if prev.address != line.address or prev.name != line.name: 210 break 211 212 # Can merge fields if they have the same starting bit and the 213 # range of the field as intended by the current line does not 214 # conflict with any of the regtypes covered by prev. 215 if prev.bits[0] != line.bits[0]: 216 continue 217 218 if prev.bits[1] != line.bits[1]: 219 # Current line's field extends beyond the range of prev. 220 # Need to check for conflicts 221 if detect_conflict(regdb, prev, line): 222 continue 223 224 prev.bits[1] = max(prev.bits[1], line.bits[1]) 225 prev.chips.update(line.chips) 226 prev.type_refs.update(line.type_refs) 227 prev.enum_refs.update(line.enum_refs) 228 merged = True 229 break 230 if not merged: 231 self.field_lines.append(line) 232 233 # Merge value lines 234 lines = self.value_lines 235 lines.sort(key=lambda line: (line.address, line.name)) 236 237 self.value_lines = [] 238 for line in lines: 239 for prev in reversed(self.value_lines): 240 if prev.address == line.address and prev.name == line.name and\ 241 prev.enumentry.value == line.enumentry.value: 242 prev.chips.update(line.chips) 243 prev.enum_refs.update(line.enum_refs) 244 break 245 else: 246 self.value_lines.append(line) 247 248 # Disambiguate field and value lines 249 for idx, line in enumerate(self.field_lines): 250 prev = self.field_lines[idx - 1] if idx > 0 else None 251 next = self.field_lines[idx + 1] if idx + 1 < len(self.field_lines) else None 252 if (prev and prev.address == line.address and prev.field.name == line.field.name) or\ 253 (next and next.address == line.address and next.field.name == line.field.name): 254 line.name += '_' + get_disambiguation_suffix(line.chips) 255 256 for idx, line in enumerate(self.value_lines): 257 prev = self.value_lines[idx - 1] if idx > 0 else None 258 next = self.value_lines[idx + 1] if idx + 1 < len(self.value_lines) else None 259 if (prev and prev.address == line.address and prev.enumentry.name == line.enumentry.name) or\ 260 (next and next.address == line.address and next.enumentry.name == line.enumentry.name): 261 line.name += '_' + get_disambiguation_suffix(line.chips) 262 263 def print(self, filp, sort='address'): 264 """ 265 Print out the entire register header. 266 """ 267 if sort == 'address': 268 self.register_lines.sort(key=lambda line: (line.address, line.name)) 269 else: 270 assert sort == 'name' 271 self.register_lines.sort(key=lambda line: (line.name, line.address)) 272 273 # Collect and sort field lines by address 274 field_lines_by_address = defaultdict(list) 275 for line in self.field_lines: 276 field_lines_by_address[line.address].append(line) 277 for field_lines in field_lines_by_address.values(): 278 if sort == 'address': 279 field_lines.sort(key=lambda line: (line.bits[0], line.name)) 280 else: 281 field_lines.sort(key=lambda line: (line.name, line.bits[0])) 282 283 # Collect and sort value lines by address 284 value_lines_by_address = defaultdict(list) 285 for line in self.value_lines: 286 value_lines_by_address[line.address].append(line) 287 for value_lines in value_lines_by_address.values(): 288 if sort == 'address': 289 value_lines.sort(key=lambda line: (line.enumentry.value, line.name)) 290 else: 291 value_lines.sort(key=lambda line: (line.name, line.enumentry.value)) 292 293 print('/* Automatically generated by amd/registers/makeregheader.py */\n', file=filp) 294 print(file=filp) 295 print(COPYRIGHT.strip(), file=filp) 296 print(file=filp) 297 298 if self.guard: 299 print('#ifndef {self.guard}'.format(**locals()), file=filp) 300 print('#define {self.guard}\n'.format(**locals()), file=filp) 301 302 for register_line in self.register_lines: 303 comment = get_chips_comment(register_line.chips) 304 305 address = '{0:X}'.format(register_line.address) 306 address = address.rjust(3 if register_line.regmap.map.to == 'pkt3' else 6, '0') 307 308 define_name = 'R_{address}_{register_line.name}'.format(**locals()).ljust(63) 309 comment = ' /* {0} */'.format(comment) if comment else '' 310 print('#define {define_name} 0x{address}{comment}'.format(**locals()), file=filp) 311 312 field_lines = field_lines_by_address[register_line.address] 313 field_idx = 0 314 while field_idx < len(field_lines): 315 field_line = field_lines[field_idx] 316 317 if field_line.type_refs.isdisjoint(register_line.type_refs): 318 field_idx += 1 319 continue 320 del field_lines[field_idx] 321 322 comment = get_chips_comment(field_line.chips, register_line.chips) 323 324 mask = (1 << (field_line.bits[1] - field_line.bits[0] + 1)) - 1 325 define_name = '_{address}_{field_line.name}(x)'.format(**locals()).ljust(58) 326 comment = ' /* {0} */'.format(comment) if comment else '' 327 print( 328 '#define S{define_name} (((unsigned)(x) & 0x{mask:X}) << {field_line.bits[0]}){comment}' 329 .format(**locals()), file=filp) 330 print('#define G{define_name} (((x) >> {field_line.bits[0]}) & 0x{mask:X})' 331 .format(**locals()), file=filp) 332 333 complement = ((1 << 32) - 1) ^ (mask << field_line.bits[0]) 334 define_name = '_{address}_{field_line.name}'.format(**locals()).ljust(58) 335 print('#define C{define_name} 0x{complement:08X}' 336 .format(**locals()), file=filp) 337 338 value_lines = value_lines_by_address[register_line.address] 339 value_idx = 0 340 while value_idx < len(value_lines): 341 value_line = value_lines[value_idx] 342 343 if value_line.enum_refs.isdisjoint(field_line.enum_refs): 344 value_idx += 1 345 continue 346 del value_lines[value_idx] 347 348 comment = get_chips_comment(value_line.chips, field_line.chips) 349 350 define_name = 'V_{address}_{value_line.name}'.format(**locals()).ljust(55) 351 comment = ' /* {0} */'.format(comment) if comment else '' 352 print('#define {define_name} {value_line.enumentry.value}{comment}' 353 .format(**locals()), file=filp) 354 355 if self.guard: 356 print('\n#endif // {self.guard}'.format(**locals()), file=filp) 357 358 359def main(): 360 parser = argparse.ArgumentParser() 361 parser.add_argument('--chip', dest='chips', type=str, nargs='*', 362 help='Chip for which to generate the header (all chips if unspecified)') 363 parser.add_argument('--sort', choices=['name', 'address'], default='address', 364 help='Sort key for registers, fields, and enum values') 365 parser.add_argument('--guard', type=str, help='Name of the #include guard') 366 parser.add_argument('files', metavar='FILE', type=str, nargs='+', 367 help='Register database file') 368 args = parser.parse_args() 369 370 regdb = None 371 for filename in args.files: 372 with open(filename, 'r') as filp: 373 db = RegisterDatabase.from_json(json.load(filp)) 374 if regdb is None: 375 regdb = db 376 else: 377 regdb.update(db) 378 379 deduplicate_enums(regdb) 380 deduplicate_register_types(regdb) 381 382 w = HeaderWriter(regdb, guard=args.guard) 383 w.print(sys.stdout, sort=args.sort) 384 385 386if __name__ == '__main__': 387 main() 388 389# kate: space-indent on; indent-width 4; replace-tabs on; 390