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] 37 38######### END HARDCODED CONFIGURATION 39 40def get_chip_index(chip): 41 """ 42 Given a chip name, return its index in the global CHIPS list. 43 """ 44 return next(idx for idx, obj in enumerate(CHIPS) if obj.name == chip) 45 46def get_disambiguation_suffix(chips): 47 """ 48 Disambiguation suffix to be used for an enum entry or field name that 49 is supported in the given set of chips. 50 """ 51 oldest_chip_index = min([get_chip_index(chip) for chip in chips]) 52 return CHIPS[oldest_chip_index].disambiguation 53 54def get_chips_comment(chips, parent=None): 55 """ 56 Generate a user-friendly comment describing the given set of chips. 57 58 The return value may be None, if such a comment is deemed unnecessary. 59 60 parent is an optional set of chips supporting a parent structure, e.g. 61 where chips may be the set of chips supporting a specific enum value, 62 parent would be the set of chips supporting the field containing the enum, 63 the idea being that no comment is necessary if all chips that support the 64 parent also support the child. 65 """ 66 chipflags = [chip.name in chips for chip in CHIPS] 67 if all(chipflags): 68 return None 69 70 if parent is not None: 71 parentflags = [chip.name in parent for chip in CHIPS] 72 if all(childflag or not parentflag for childflag, parentflag in zip(chipflags, parentflags)): 73 return None 74 75 prefix = 0 76 for idx, chip, flag in zip(itertools.count(), CHIPS, chipflags): 77 if not flag: 78 break 79 prefix = idx + 1 80 81 suffix = len(CHIPS) 82 for idx, chip, flag in zip(itertools.count(), reversed(CHIPS), reversed(chipflags)): 83 if not flag: 84 break 85 suffix = len(CHIPS) - idx - 1 86 87 comment = [] 88 if prefix > 0: 89 comment.append('<= {0}'.format(CHIPS[prefix - 1].name)) 90 for chip, flag in zip(CHIPS[prefix:suffix], chipflags[prefix:suffix]): 91 if flag: 92 comment.append(chip.name) 93 if suffix < len(CHIPS): 94 comment.append('>= {0}'.format(CHIPS[suffix].name)) 95 96 return ', '.join(comment) 97 98def detect_conflict(regdb, field_in_type1, field_in_type2): 99 """ 100 Returns False if field_in_type1 and field_in_type2 can be merged 101 into a single field = if writing to field_in_type1 bits won't 102 overwrite adjacent fields in type2, and the other way around. 103 """ 104 for idx, type_refs in enumerate([field_in_type1.type_refs, field_in_type2.type_refs]): 105 ref = field_in_type2 if idx == 0 else field_in_type1 106 for type_ref in type_refs: 107 for field in regdb.register_type(type_ref).fields: 108 # If a different field in the other type starts in 109 # the tested field's bits[0, 1] interval 110 if (field.bits[0] > ref.bits[0] and 111 field.bits[0] <= ref.bits[1]): 112 return True 113 114 return False 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 if detect_conflict(regdb, prev, line): 207 continue 208 209 prev.bits[1] = max(prev.bits[1], line.bits[1]) 210 prev.chips.update(line.chips) 211 prev.type_refs.update(line.type_refs) 212 prev.enum_refs.update(line.enum_refs) 213 merged = True 214 break 215 if not merged: 216 self.field_lines.append(line) 217 218 # Merge value lines 219 lines = self.value_lines 220 lines.sort(key=lambda line: (line.address, line.name)) 221 222 self.value_lines = [] 223 for line in lines: 224 for prev in reversed(self.value_lines): 225 if prev.address == line.address and prev.name == line.name and\ 226 prev.enumentry.value == line.enumentry.value: 227 prev.chips.update(line.chips) 228 prev.enum_refs.update(line.enum_refs) 229 break 230 else: 231 self.value_lines.append(line) 232 233 # Disambiguate field and value lines 234 for idx, line in enumerate(self.field_lines): 235 prev = self.field_lines[idx - 1] if idx > 0 else None 236 next = self.field_lines[idx + 1] if idx + 1 < len(self.field_lines) else None 237 if (prev and prev.address == line.address and prev.field.name == line.field.name) or\ 238 (next and next.address == line.address and next.field.name == line.field.name): 239 line.name += '_' + get_disambiguation_suffix(line.chips) 240 241 for idx, line in enumerate(self.value_lines): 242 prev = self.value_lines[idx - 1] if idx > 0 else None 243 next = self.value_lines[idx + 1] if idx + 1 < len(self.value_lines) else None 244 if (prev and prev.address == line.address and prev.enumentry.name == line.enumentry.name) or\ 245 (next and next.address == line.address and next.enumentry.name == line.enumentry.name): 246 line.name += '_' + get_disambiguation_suffix(line.chips) 247 248 def print(self, filp, sort='address'): 249 """ 250 Print out the entire register header. 251 """ 252 if sort == 'address': 253 self.register_lines.sort(key=lambda line: (line.address, line.name)) 254 else: 255 assert sort == 'name' 256 self.register_lines.sort(key=lambda line: (line.name, line.address)) 257 258 # Collect and sort field lines by address 259 field_lines_by_address = defaultdict(list) 260 for line in self.field_lines: 261 field_lines_by_address[line.address].append(line) 262 for field_lines in field_lines_by_address.values(): 263 if sort == 'address': 264 field_lines.sort(key=lambda line: (line.bits[0], line.name)) 265 else: 266 field_lines.sort(key=lambda line: (line.name, line.bits[0])) 267 268 # Collect and sort value lines by address 269 value_lines_by_address = defaultdict(list) 270 for line in self.value_lines: 271 value_lines_by_address[line.address].append(line) 272 for value_lines in value_lines_by_address.values(): 273 if sort == 'address': 274 value_lines.sort(key=lambda line: (line.enumentry.value, line.name)) 275 else: 276 value_lines.sort(key=lambda line: (line.name, line.enumentry.value)) 277 278 print('/* Automatically generated by amd/registers/makeregheader.py */\n', file=filp) 279 print(file=filp) 280 print(COPYRIGHT.strip(), file=filp) 281 print(file=filp) 282 283 if self.guard: 284 print('#ifndef {self.guard}'.format(**locals()), file=filp) 285 print('#define {self.guard}\n'.format(**locals()), file=filp) 286 287 for register_line in self.register_lines: 288 comment = get_chips_comment(register_line.chips) 289 290 address = '{0:X}'.format(register_line.address) 291 address = address.rjust(3 if register_line.regmap.map.to == 'pkt3' else 6, '0') 292 293 define_name = 'R_{address}_{register_line.name}'.format(**locals()).ljust(63) 294 comment = ' /* {0} */'.format(comment) if comment else '' 295 print('#define {define_name} 0x{address}{comment}'.format(**locals()), file=filp) 296 297 field_lines = field_lines_by_address[register_line.address] 298 field_idx = 0 299 while field_idx < len(field_lines): 300 field_line = field_lines[field_idx] 301 302 if field_line.type_refs.isdisjoint(register_line.type_refs): 303 field_idx += 1 304 continue 305 del field_lines[field_idx] 306 307 comment = get_chips_comment(field_line.chips, register_line.chips) 308 309 mask = (1 << (field_line.bits[1] - field_line.bits[0] + 1)) - 1 310 define_name = '_{address}_{field_line.name}(x)'.format(**locals()).ljust(58) 311 comment = ' /* {0} */'.format(comment) if comment else '' 312 print( 313 '#define S{define_name} (((unsigned)(x) & 0x{mask:X}) << {field_line.bits[0]}){comment}' 314 .format(**locals()), file=filp) 315 print('#define G{define_name} (((x) >> {field_line.bits[0]}) & 0x{mask:X})' 316 .format(**locals()), file=filp) 317 318 complement = ((1 << 32) - 1) ^ (mask << field_line.bits[0]) 319 define_name = '_{address}_{field_line.name}'.format(**locals()).ljust(58) 320 print('#define C{define_name} 0x{complement:08X}' 321 .format(**locals()), file=filp) 322 323 value_lines = value_lines_by_address[register_line.address] 324 value_idx = 0 325 while value_idx < len(value_lines): 326 value_line = value_lines[value_idx] 327 328 if value_line.enum_refs.isdisjoint(field_line.enum_refs): 329 value_idx += 1 330 continue 331 del value_lines[value_idx] 332 333 comment = get_chips_comment(value_line.chips, field_line.chips) 334 335 define_name = 'V_{address}_{value_line.name}'.format(**locals()).ljust(55) 336 comment = ' /* {0} */'.format(comment) if comment else '' 337 print('#define {define_name} {value_line.enumentry.value}{comment}' 338 .format(**locals()), file=filp) 339 340 if self.guard: 341 print('\n#endif // {self.guard}'.format(**locals()), file=filp) 342 343 344def main(): 345 parser = argparse.ArgumentParser() 346 parser.add_argument('--chip', dest='chips', type=str, nargs='*', 347 help='Chip for which to generate the header (all chips if unspecified)') 348 parser.add_argument('--sort', choices=['name', 'address'], default='address', 349 help='Sort key for registers, fields, and enum values') 350 parser.add_argument('--guard', type=str, help='Name of the #include guard') 351 parser.add_argument('files', metavar='FILE', type=str, nargs='+', 352 help='Register database file') 353 args = parser.parse_args() 354 355 regdb = None 356 for filename in args.files: 357 with open(filename, 'r') as filp: 358 db = RegisterDatabase.from_json(json.load(filp)) 359 if regdb is None: 360 regdb = db 361 else: 362 regdb.update(db) 363 364 deduplicate_enums(regdb) 365 deduplicate_register_types(regdb) 366 367 w = HeaderWriter(regdb, guard=args.guard) 368 w.print(sys.stdout, sort=args.sort) 369 370 371if __name__ == '__main__': 372 main() 373 374# kate: space-indent on; indent-width 4; replace-tabs on; 375