1CopyRight = ''' 2/* 3 * Copyright 2015-2019 Advanced Micro Devices, Inc. 4 * 5 * SPDX-License-Identifier: MIT 6 */ 7''' 8 9from collections import defaultdict 10import functools 11import itertools 12import json 13import os.path 14import re 15import sys 16 17AMD_REGISTERS = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "../registers")) 18sys.path.append(AMD_REGISTERS) 19 20from regdb import Object, RegisterDatabase 21 22 23def string_to_chars(string): 24 return "'" + "', '".join(string) + "', '\\0'," 25 26 27class StringTable: 28 """ 29 A class for collecting multiple strings in a single larger string that is 30 used by indexing (to avoid relocations in the resulting binary) 31 """ 32 def __init__(self): 33 self.table = [] 34 self.length = 0 35 36 def add(self, string): 37 # We might get lucky with string being a suffix of a previously added string 38 for te in self.table: 39 if te[0].endswith(string): 40 idx = te[1] + len(te[0]) - len(string) 41 te[2].add(idx) 42 return idx 43 44 idx = self.length 45 self.table.append((string, idx, set((idx,)))) 46 self.length += len(string) + 1 47 48 return idx 49 50 def emit(self, filp, name, static=True): 51 """ 52 Write 53 [static] const char name[] = "..."; 54 to filp. 55 """ 56 fragments = [ 57 '%s /* %s (%s) */' % ( 58 string_to_chars(te[0].encode('unicode_escape').decode()), 59 te[0].encode('unicode_escape').decode(), 60 ', '.join(str(idx) for idx in sorted(te[2])) 61 ) 62 for te in self.table 63 ] 64 filp.write('%sconst char %s[] = {\n%s\n};\n' % ( 65 'static ' if static else '', 66 name, 67 '\n'.join('\t' + fragment for fragment in fragments) 68 )) 69 70class IntTable: 71 """ 72 A class for collecting multiple arrays of integers in a single big array 73 that is used by indexing (to avoid relocations in the resulting binary) 74 """ 75 def __init__(self, typename): 76 self.typename = typename 77 self.table = [] 78 self.idxs = set() 79 80 def add(self, array): 81 # We might get lucky and find the array somewhere in the existing data 82 try: 83 idx = 0 84 while True: 85 idx = self.table.index(array[0], idx, len(self.table) - len(array) + 1) 86 87 for i in range(1, len(array)): 88 if array[i] != self.table[idx + i]: 89 break 90 else: 91 self.idxs.add(idx) 92 return idx 93 94 idx += 1 95 except ValueError: 96 pass 97 98 idx = len(self.table) 99 self.table += array 100 self.idxs.add(idx) 101 return idx 102 103 def emit(self, filp, name, static=True): 104 """ 105 Write 106 [static] const typename name[] = { ... }; 107 to filp. 108 """ 109 idxs = sorted(self.idxs) + [len(self.table)] 110 111 fragments = [ 112 ('\t/* %s */ %s' % ( 113 idxs[i], 114 ' '.join((str(elt) + ',') for elt in self.table[idxs[i]:idxs[i+1]]) 115 )) 116 for i in range(len(idxs) - 1) 117 ] 118 119 filp.write('%sconst %s %s[] = {\n%s\n};\n' % ( 120 'static ' if static else '', 121 self.typename, name, 122 '\n'.join(fragments) 123 )) 124 125class Field: 126 def __init__(self, name, bits): 127 self.name = name 128 self.bits = bits # [first, last] 129 self.values = [] # [(name, value), ...] 130 131 def format(self, string_table, idx_table): 132 mask = ((1 << (self.bits[1] - self.bits[0] + 1)) - 1) << self.bits[0] 133 if len(self.values): 134 values_offsets = [] 135 for value in self.values: 136 while value[1] >= len(values_offsets): 137 values_offsets.append(-1) 138 values_offsets[value[1]] = string_table.add(value[0]) 139 return '{{{0}, 0x{mask:X}, {1}, {2}}}'.format( 140 string_table.add(self.name), 141 len(values_offsets), idx_table.add(values_offsets), 142 **locals() 143 ) 144 else: 145 return '{{{0}, 0x{mask:X}}}'.format(string_table.add(self.name), **locals()) 146 147 def __eq__(self, other): 148 return (self.name == other.name and 149 self.bits[0] == other.bits[0] and self.bits[1] == other.bits[1] and 150 len(self.values) == len(other.values) and 151 all(a[0] == b[0] and a[1] == b[1] for a, b, in zip(self.values, other.values))) 152 153 def __ne__(self, other): 154 return not (self == other) 155 156 157class FieldTable: 158 """ 159 A class for collecting multiple arrays of register fields in a single big 160 array that is used by indexing (to avoid relocations in the resulting binary) 161 """ 162 def __init__(self): 163 self.table = [] 164 self.idxs = set() 165 self.name_to_idx = defaultdict(lambda: []) 166 167 def add(self, array): 168 """ 169 Add an array of Field objects, and return the index of where to find 170 the array in the table. 171 """ 172 # Check if we can find the array in the table already 173 for base_idx in self.name_to_idx.get(array[0].name, []): 174 if base_idx + len(array) > len(self.table): 175 continue 176 177 for i, a in enumerate(array): 178 b = self.table[base_idx + i] 179 if a != b: 180 break 181 else: 182 return base_idx 183 184 base_idx = len(self.table) 185 self.idxs.add(base_idx) 186 187 for field in array: 188 self.name_to_idx[field.name].append(len(self.table)) 189 self.table.append(field) 190 191 return base_idx 192 193 def emit(self, filp, string_table, idx_table): 194 """ 195 Write 196 static const struct si_field sid_fields_table[] = { ... }; 197 to filp. 198 """ 199 idxs = sorted(self.idxs) + [len(self.table)] 200 201 filp.write('static const struct si_field sid_fields_table[] = {\n') 202 203 for start, end in zip(idxs, idxs[1:]): 204 filp.write('\t/* %s */\n' % (start)) 205 for field in self.table[start:end]: 206 filp.write('\t%s,\n' % (field.format(string_table, idx_table))) 207 208 filp.write('};\n') 209 210 211def parse_packet3(filp): 212 """ 213 Parse PKT3 commands from the given header file. 214 """ 215 packets = [] 216 for line in filp: 217 if not line.startswith('#define '): 218 continue 219 220 line = line[8:].strip() 221 222 if line.startswith('PKT3_') and line.find('0x') != -1 and line.find('(') == -1: 223 packets.append(line.split()[0]) 224 return packets 225 226 227class TableWriter(object): 228 def __init__(self): 229 self.__strings = StringTable() 230 self.__strings_offsets = IntTable('int') 231 self.__fields = FieldTable() 232 233 def write(self, regdb, packets, file=sys.stdout): 234 def out(*args): 235 print(*args, file=file) 236 237 out('/* This file is autogenerated by sid_tables.py from sid.h. Do not edit directly. */') 238 out() 239 out(CopyRight.strip()) 240 out(''' 241#ifndef SID_TABLES_H 242#define SID_TABLES_H 243 244struct si_field { 245 unsigned name_offset; 246 unsigned mask; 247 unsigned num_values; 248 unsigned values_offset; /* offset into sid_strings_offsets */ 249}; 250 251struct si_reg { 252 unsigned name_offset; 253 unsigned offset; 254 unsigned num_fields; 255 unsigned fields_offset; 256}; 257 258struct si_packet3 { 259 unsigned name_offset; 260 unsigned op; 261}; 262''') 263 264 out('static const struct si_packet3 packet3_table[] = {') 265 for pkt in packets: 266 out('\t{%s, %s},' % (self.__strings.add(pkt[5:]), pkt)) 267 out('};') 268 out() 269 270 regmaps_by_chip = defaultdict(list) 271 272 for regmap in regdb.register_mappings(): 273 for chip in regmap.chips: 274 regmaps_by_chip[chip].append(regmap) 275 276 regtypes = {} 277 278 # Sorted iteration over chips for deterministic builds 279 for chip in sorted(regmaps_by_chip.keys()): 280 regmaps = regmaps_by_chip[chip] 281 regmaps.sort(key=lambda regmap: (regmap.map.to, regmap.map.at)) 282 283 out('static const struct si_reg {chip}_reg_table[] = {{'.format(**locals())) 284 285 for regmap in regmaps: 286 if hasattr(regmap, 'type_ref'): 287 if not regmap.type_ref in regtypes: 288 regtype = regdb.register_type(regmap.type_ref) 289 fields = [] 290 for dbfield in regtype.fields: 291 field = Field(dbfield.name, dbfield.bits) 292 if hasattr(dbfield, 'enum_ref'): 293 enum = regdb.enum(dbfield.enum_ref) 294 for entry in enum.entries: 295 field.values.append((entry.name, entry.value)) 296 fields.append(field) 297 298 num_fields = len(regtype.fields) 299 fields_offset = self.__fields.add(fields) 300 regtypes[regmap.type_ref] = (num_fields, fields_offset) 301 else: 302 num_fields, fields_offset = regtypes[regmap.type_ref] 303 304 print('\t{{{0}, {regmap.map.at}, {num_fields}, {fields_offset}}},' 305 .format(self.__strings.add(regmap.name), **locals())) 306 else: 307 print('\t{{{0}, {regmap.map.at}}},' 308 .format(self.__strings.add(regmap.name), **locals())) 309 310 out('};\n') 311 312 self.__fields.emit(file, self.__strings, self.__strings_offsets) 313 314 out() 315 316 self.__strings.emit(file, "sid_strings") 317 318 out() 319 320 self.__strings_offsets.emit(file, "sid_strings_offsets") 321 322 out() 323 out('#endif') 324 325 326def main(): 327 # Parse PKT3 types 328 with open(sys.argv[1], 'r') as filp: 329 packets = parse_packet3(filp) 330 331 # Register database parse 332 regdb = None 333 for filename in sys.argv[2:]: 334 with open(filename, 'r') as filp: 335 try: 336 db = RegisterDatabase.from_json(json.load(filp)) 337 if regdb is None: 338 regdb = db 339 else: 340 regdb.update(db) 341 except json.JSONDecodeError as e: 342 print('Error reading {}'.format(sys.argv[1]), file=sys.stderr) 343 raise 344 345 # The ac_debug code only distinguishes by gfx_level 346 regdb.merge_chips(['gfx8', 'fiji', 'stoney'], 'gfx8') 347 348 # Write it all out 349 w = TableWriter() 350 w.write(regdb, packets) 351 352if __name__ == '__main__': 353 main() 354 355# kate: space-indent on; indent-width 4; replace-tabs on; 356