1 2CopyRight = ''' 3/* 4 * Copyright 2015 Advanced Micro Devices, Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * on the rights to use, copy, modify, merge, publish, distribute, sub 10 * license, and/or sell copies of the Software, and to permit persons to whom 11 * the Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the next 14 * paragraph) shall be included in all copies or substantial portions of the 15 * Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 20 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, 21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 23 * USE OR OTHER DEALINGS IN THE SOFTWARE. 24 * 25 */ 26''' 27 28import collections 29import functools 30import itertools 31import os.path 32import re 33import sys 34 35 36class StringTable: 37 """ 38 A class for collecting multiple strings in a single larger string that is 39 used by indexing (to avoid relocations in the resulting binary) 40 """ 41 def __init__(self): 42 self.table = [] 43 self.length = 0 44 45 def add(self, string): 46 # We might get lucky with string being a suffix of a previously added string 47 for te in self.table: 48 if te[0].endswith(string): 49 idx = te[1] + len(te[0]) - len(string) 50 te[2].add(idx) 51 return idx 52 53 idx = self.length 54 self.table.append((string, idx, set((idx,)))) 55 self.length += len(string) + 1 56 57 return idx 58 59 def emit(self, filp, name, static=True): 60 """ 61 Write 62 [static] const char name[] = "..."; 63 to filp. 64 """ 65 fragments = [ 66 '"%s\\0" /* %s */' % ( 67 te[0].encode('string_escape'), 68 ', '.join(str(idx) for idx in te[2]) 69 ) 70 for te in self.table 71 ] 72 filp.write('%sconst char %s[] =\n%s;\n' % ( 73 'static ' if static else '', 74 name, 75 '\n'.join('\t' + fragment for fragment in fragments) 76 )) 77 78class IntTable: 79 """ 80 A class for collecting multiple arrays of integers in a single big array 81 that is used by indexing (to avoid relocations in the resulting binary) 82 """ 83 def __init__(self, typename): 84 self.typename = typename 85 self.table = [] 86 self.idxs = set() 87 88 def add(self, array): 89 # We might get lucky and find the array somewhere in the existing data 90 try: 91 idx = 0 92 while True: 93 idx = self.table.index(array[0], idx, len(self.table) - len(array) + 1) 94 95 for i in range(1, len(array)): 96 if array[i] != self.table[idx + i]: 97 break 98 else: 99 self.idxs.add(idx) 100 return idx 101 102 idx += 1 103 except ValueError: 104 pass 105 106 idx = len(self.table) 107 self.table += array 108 self.idxs.add(idx) 109 return idx 110 111 def emit(self, filp, name, static=True): 112 """ 113 Write 114 [static] const typename name[] = { ... }; 115 to filp. 116 """ 117 idxs = sorted(self.idxs) + [len(self.table)] 118 119 fragments = [ 120 ('\t/* %s */ %s' % ( 121 idxs[i], 122 ' '.join((str(elt) + ',') for elt in self.table[idxs[i]:idxs[i+1]]) 123 )) 124 for i in range(len(idxs) - 1) 125 ] 126 127 filp.write('%sconst %s %s[] = {\n%s\n};\n' % ( 128 'static ' if static else '', 129 self.typename, name, 130 '\n'.join(fragments) 131 )) 132 133class Field: 134 def __init__(self, reg, s_name): 135 self.s_name = s_name 136 self.name = strip_prefix(s_name) 137 self.values = [] 138 139 def format(self, string_table, idx_table): 140 if len(self.values): 141 values_offsets = [] 142 for value in self.values: 143 while value[1] >= len(values_offsets): 144 values_offsets.append(-1) 145 values_offsets[value[1]] = string_table.add(strip_prefix(value[0])) 146 return '{%s, %s(~0u), %s, %s}' % ( 147 string_table.add(self.name), self.s_name, 148 len(values_offsets), idx_table.add(values_offsets)) 149 else: 150 return '{%s, %s(~0u)}' % (string_table.add(self.name), self.s_name) 151 152 def __eq__(self, other): 153 return (self.s_name == other.s_name and 154 self.name == other.name and 155 len(self.values) == len(other.values) and 156 all(a[0] == b[0] and a[1] == b[1] for a, b, in zip(self.values, other.values))) 157 158 def __ne__(self, other): 159 return not (self == other) 160 161 162class FieldTable: 163 """ 164 A class for collecting multiple arrays of register fields in a single big 165 array that is used by indexing (to avoid relocations in the resulting binary) 166 """ 167 def __init__(self): 168 self.table = [] 169 self.idxs = set() 170 self.name_to_idx = collections.defaultdict(lambda: []) 171 172 def add(self, array): 173 """ 174 Add an array of Field objects, and return the index of where to find 175 the array in the table. 176 """ 177 # Check if we can find the array in the table already 178 for base_idx in self.name_to_idx.get(array[0].name, []): 179 if base_idx + len(array) > len(self.table): 180 continue 181 182 for i, a in enumerate(array): 183 b = self.table[base_idx + i] 184 if a != b: 185 break 186 else: 187 return base_idx 188 189 base_idx = len(self.table) 190 self.idxs.add(base_idx) 191 192 for field in array: 193 self.name_to_idx[field.name].append(len(self.table)) 194 self.table.append(field) 195 196 return base_idx 197 198 def emit(self, filp, string_table, idx_table): 199 """ 200 Write 201 static const struct si_field sid_fields_table[] = { ... }; 202 to filp. 203 """ 204 idxs = sorted(self.idxs) + [len(self.table)] 205 206 filp.write('static const struct si_field sid_fields_table[] = {\n') 207 208 for start, end in zip(idxs, idxs[1:]): 209 filp.write('\t/* %s */\n' % (start)) 210 for field in self.table[start:end]: 211 filp.write('\t%s,\n' % (field.format(string_table, idx_table))) 212 213 filp.write('};\n') 214 215 216class Reg: 217 def __init__(self, r_name): 218 self.r_name = r_name 219 self.name = strip_prefix(r_name) 220 self.fields = [] 221 222 def __eq__(self, other): 223 if not isinstance(other, Reg): 224 return False 225 return (self.r_name == other.r_name and 226 self.name == other.name and 227 len(self.fields) == len(other.fields) and 228 all(a == b for a, b in zip(self.fields, other.fields))) 229 230 def __ne__(self, other): 231 return not (self == other) 232 233 234def strip_prefix(s): 235 '''Strip prefix in the form ._.*_, e.g. R_001234_''' 236 return s[s[2:].find('_')+3:] 237 238 239class Asic: 240 """ 241 Store the registers of one ASIC class / group of classes. 242 """ 243 def __init__(self, name): 244 self.name = name 245 self.registers = [] 246 247 def parse(self, filp, packets, older_asics): 248 """ 249 Parse registers from the given header file. Packets are separately 250 stored in the packets array. 251 """ 252 for line in filp: 253 if not line.startswith('#define '): 254 continue 255 256 line = line[8:].strip() 257 258 if line.startswith('R_'): 259 name = line.split()[0] 260 261 for it in self.registers: 262 if it.r_name == name: 263 sys.exit('Duplicate register define: %s' % (name)) 264 else: 265 reg = Reg(name) 266 self.registers.append(reg) 267 268 elif line.startswith('S_'): 269 name = line[:line.find('(')] 270 271 for it in reg.fields: 272 if it.s_name == name: 273 sys.exit('Duplicate field define: %s' % (name)) 274 else: 275 field = Field(reg, name) 276 reg.fields.append(field) 277 278 elif line.startswith('V_'): 279 split = line.split() 280 name = split[0] 281 value = int(split[1], 0) 282 283 for (n,v) in field.values: 284 if n == name: 285 sys.exit('Duplicate value define: name = ' + name) 286 287 field.values.append((name, value)) 288 289 elif line.startswith('PKT3_') and line.find('0x') != -1 and line.find('(') == -1: 290 packets.append(line.split()[0]) 291 292 # Copy values for corresponding fields from older ASICs if they were 293 # not redefined 294 for reg in self.registers: 295 old_reg = False 296 for field in reg.fields: 297 if len(field.values) > 0: 298 continue 299 if old_reg is False: 300 for old_reg in itertools.chain( 301 *(asic.registers for asic in reversed(older_asics))): 302 if old_reg.name == reg.name: 303 break 304 else: 305 old_reg = None 306 if old_reg is not None: 307 for old_field in old_reg.fields: 308 if old_field.name == field.name: 309 field.values = old_field.values 310 break 311 312 # Copy fields to indexed registers which have their fields only defined 313 # at register index 0. 314 # For example, copy fields from CB_COLOR0_INFO to CB_COLORn_INFO, n > 0. 315 match_number = re.compile('[0-9]+') 316 reg_dict = dict() 317 318 # Create a dict of registers with fields and '0' in their name 319 for reg in self.registers: 320 if len(reg.fields) and reg.name.find('0') != -1: 321 reg_dict[reg.name] = reg 322 323 # Assign fields 324 for reg in self.registers: 325 if not len(reg.fields): 326 reg0 = reg_dict.get(match_number.sub('0', reg.name)) 327 if reg0 != None: 328 reg.fields = reg0.fields 329 330 331def write_tables(asics, packets): 332 strings = StringTable() 333 strings_offsets = IntTable("int") 334 fields = FieldTable() 335 336 print '/* This file is autogenerated by sid_tables.py from sid.h. Do not edit directly. */' 337 print 338 print CopyRight.strip() 339 print ''' 340#ifndef SID_TABLES_H 341#define SID_TABLES_H 342 343struct si_field { 344 unsigned name_offset; 345 unsigned mask; 346 unsigned num_values; 347 unsigned values_offset; /* offset into sid_strings_offsets */ 348}; 349 350struct si_reg { 351 unsigned name_offset; 352 unsigned offset; 353 unsigned num_fields; 354 unsigned fields_offset; 355}; 356 357struct si_packet3 { 358 unsigned name_offset; 359 unsigned op; 360}; 361''' 362 363 print 'static const struct si_packet3 packet3_table[] = {' 364 for pkt in packets: 365 print '\t{%s, %s},' % (strings.add(pkt[5:]), pkt) 366 print '};' 367 print 368 369 regs = {} 370 for asic in asics: 371 print 'static const struct si_reg %s_reg_table[] = {' % (asic.name) 372 for reg in asic.registers: 373 # Only output a register that was changed or added relative to 374 # the previous generation 375 previous = regs.get(reg.r_name, None) 376 if previous == reg: 377 continue 378 379 if len(reg.fields): 380 print '\t{%s, %s, %s, %s},' % (strings.add(reg.name), reg.r_name, 381 len(reg.fields), fields.add(reg.fields)) 382 else: 383 print '\t{%s, %s},' % (strings.add(reg.name), reg.r_name) 384 385 regs[reg.r_name] = reg 386 print '};' 387 print 388 389 fields.emit(sys.stdout, strings, strings_offsets) 390 391 print 392 393 strings.emit(sys.stdout, "sid_strings") 394 395 print 396 397 strings_offsets.emit(sys.stdout, "sid_strings_offsets") 398 399 print 400 print '#endif' 401 402 403def main(): 404 asics = [] 405 packets = [] 406 for arg in sys.argv[1:]: 407 basename = os.path.basename(arg) 408 m = re.match(r'(.*)\.h', basename) 409 asic = Asic(m.group(1)) 410 with open(arg) as filp: 411 asic.parse(filp, packets, asics) 412 asics.append(asic) 413 write_tables(asics, packets) 414 415 416if __name__ == '__main__': 417 main() 418 419# kate: space-indent on; indent-width 4; replace-tabs on; 420