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