File Index
Files and directories
Sign in
FILE

gen_header.py

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/msm/registers/gen_header.py PYTHON
   1#!/usr/bin/python3
   2#
   3# Copyright © 2019-2024 Google, Inc.
   4#
   5# SPDX-License-Identifier: MIT
   6
   7import xml.parsers.expat
   8import sys
   9import os
  10import collections
  11import argparse
  12import time
  13import datetime
  14
  15class Error(Exception):
  16	def __init__(self, message):
  17		self.message = message
  18
  19class Enum(object):
  20	def __init__(self, name):
  21		self.name = name
  22		self.values = []
  23
  24	def has_name(self, name):
  25		for (n, value) in self.values:
  26			if n == name:
  27				return True
  28		return False
  29
  30	def names(self):
  31		return [n for (n, value) in self.values]
  32
  33	def dump(self, is_deprecated):
  34		use_hex = False
  35		for (name, value) in self.values:
  36			if value > 0x1000:
  37				use_hex = True
  38
  39		print("enum %s {" % self.name)
  40		for (name, value) in self.values:
  41			if use_hex:
  42				print("\t%s = 0x%08x," % (name, value))
  43			else:
  44				print("\t%s = %d," % (name, value))
  45		print("};\n")
  46
  47	def dump_pack_struct(self, is_deprecated):
  48		pass
  49
  50class Field(object):
  51	def __init__(self, name, low, high, shr, type, parser):
  52		self.name = name
  53		self.low = low
  54		self.high = high
  55		self.shr = shr
  56		self.type = type
  57
  58		builtin_types = [ None, "a3xx_regid", "boolean", "uint", "hex", "int", "fixed", "ufixed", "float", "address", "waddress" ]
  59
  60		maxpos = parser.current_bitsize - 1
  61
  62		if low < 0 or low > maxpos:
  63			raise parser.error("low attribute out of range: %d" % low)
  64		if high < 0 or high > maxpos:
  65			raise parser.error("high attribute out of range: %d" % high)
  66		if high < low:
  67			raise parser.error("low is greater than high: low=%d, high=%d" % (low, high))
  68		if self.type == "boolean" and not low == high:
  69			raise parser.error("booleans should be 1 bit fields")
  70		elif self.type == "float" and not (high - low == 31 or high - low == 15):
  71			raise parser.error("floats should be 16 or 32 bit fields")
  72		elif self.type not in builtin_types and self.type not in parser.enums:
  73			raise parser.error("unknown type '%s'" % self.type)
  74
  75	def ctype(self, var_name):
  76		if self.type is None:
  77			type = "uint32_t"
  78			val = var_name
  79		elif self.type == "boolean":
  80			type = "bool"
  81			val = var_name
  82		elif self.type == "uint" or self.type == "hex" or self.type == "a3xx_regid":
  83			type = "uint32_t"
  84			val = var_name
  85		elif self.type == "int":
  86			type = "int32_t"
  87			val = var_name
  88		elif self.type == "fixed":
  89			type = "float"
  90			val = "((int32_t)(%s * %d.0))" % (var_name, 1 << self.radix)
  91		elif self.type == "ufixed":
  92			type = "float"
  93			val = "((uint32_t)(%s * %d.0))" % (var_name, 1 << self.radix)
  94		elif self.type == "float" and self.high - self.low == 31:
  95			type = "float"
  96			val = "fui(%s)" % var_name
  97		elif self.type == "float" and self.high - self.low == 15:
  98			type = "float"
  99			val = "_mesa_float_to_half(%s)" % var_name
 100		elif self.type in [ "address", "waddress" ]:
 101			type = "uint64_t"
 102			val = var_name
 103		else:
 104			type = "enum %s" % self.type
 105			val = var_name
 106
 107		if self.shr > 0:
 108			val = "(%s >> %d)" % (val, self.shr)
 109
 110		return (type, val)
 111
 112def tab_to(name, value):
 113	tab_count = (68 - (len(name) & ~7)) // 8
 114	if tab_count <= 0:
 115		tab_count = 1
 116	print(name + ('\t' * tab_count) + value)
 117
 118def mask(low, high):
 119	return ((0xffffffffffffffff >> (64 - (high + 1 - low))) << low)
 120
 121def field_name(reg, f):
 122	if f.name:
 123		name = f.name.lower()
 124	else:
 125		# We hit this path when a reg is defined with no bitset fields, ie.
 126		# 	<reg32 offset="0x88db" name="RB_RESOLVE_SYSTEM_BUFFER_ARRAY_PITCH" low="0" high="28" shr="6" type="uint"/>
 127		name = reg.name.lower()
 128
 129	if (name in [ "double", "float", "int" ]) or not (name[0].isalpha()):
 130			name = "_" + name
 131
 132	return name
 133
 134# indices - array of (ctype, stride, __offsets_NAME)
 135def indices_varlist(indices):
 136	return ", ".join(["i%d" % i for i in range(len(indices))])
 137
 138def indices_prototype(indices):
 139	return ", ".join(["%s i%d" % (ctype, idx)
 140			for (idx, (ctype, stride, offset)) in  enumerate(indices)])
 141
 142def indices_strides(indices):
 143	return " + ".join(["0x%x*i%d" % (stride, idx)
 144					if stride else
 145					"%s(i%d)" % (offset, idx)
 146			for (idx, (ctype, stride, offset)) in  enumerate(indices)])
 147
 148def is_number(str):
 149	try:
 150		int(str)
 151		return True
 152	except ValueError:
 153		return False
 154
 155def sanitize_variant(variant):
 156	if variant and "-" in variant:
 157		return variant[:variant.index("-")]
 158	return variant
 159
 160class Bitset(object):
 161	def __init__(self, name, template):
 162		self.name = name
 163		self.inline = False
 164		self.reg = None
 165		if template:
 166			self.fields = template.fields[:]
 167		else:
 168			self.fields = []
 169
 170	# Get address field if there is one in the bitset, else return None:
 171	def get_address_field(self):
 172		for f in self.fields:
 173			if f.type in [ "address", "waddress" ]:
 174				return f
 175		return None
 176
 177	def dump_regpair_builder(self, reg):
 178		print("#ifndef NDEBUG")
 179		known_mask = 0
 180		for f in self.fields:
 181			known_mask |= mask(f.low, f.high)
 182			if f.type in [ "boolean", "address", "waddress" ]:
 183				continue
 184			type, val = f.ctype("fields.%s" % field_name(reg, f))
 185			print("    assert((%-40s & 0x%08x) == 0);" % (val, 0xffffffff ^ mask(0 , f.high - f.low)))
 186		print("    assert((%-40s & 0x%08x) == 0);" % ("fields.unknown", known_mask))
 187		print("#endif\n")
 188
 189		print("    return (struct fd_reg_pair) {")
 190		print("        .reg = (uint32_t)%s," % reg.reg_offset())
 191		print("        .value =")
 192		cast = "(uint64_t)" if reg.bit_size == 64 else ""
 193		for f in self.fields:
 194			if f.type in [ "address", "waddress" ]:
 195				continue
 196			else:
 197				type, val = f.ctype("fields.%s" % field_name(reg, f))
 198				print("            (%s%-40s << %2d) |" % (cast, val, f.low))
 199		value_name = "dword"
 200		if reg.bit_size == 64:
 201			value_name = "qword"
 202		print("            fields.unknown | fields.%s," % (value_name,))
 203
 204		address = self.get_address_field()
 205		if address:
 206			print("        .bo = fields.bo,")
 207			print("        .is_address = true,")
 208			if f.type == "waddress":
 209				print("        .bo_write = true,")
 210			print("        .bo_offset = fields.bo_offset,")
 211			print("        .bo_shift = %d," % address.shr)
 212			print("        .bo_low = %d," % address.low)
 213
 214		print("    };")
 215
 216	def dump_pack_struct(self, is_deprecated, reg=None):
 217		if not reg:
 218			return
 219
 220		prefix = reg.full_name
 221
 222		print("struct %s {" % prefix)
 223		for f in self.fields:
 224			if f.type in [ "address", "waddress" ]:
 225				tab_to("    __bo_type", "bo;")
 226				tab_to("    uint32_t", "bo_offset;")
 227				continue
 228			name = field_name(reg, f)
 229
 230			type, val = f.ctype("var")
 231
 232			tab_to("    %s" % type, "%s;" % name)
 233		if reg.bit_size == 64:
 234			tab_to("    uint64_t", "unknown;")
 235			tab_to("    uint64_t", "qword;")
 236		else:
 237			tab_to("    uint32_t", "unknown;")
 238			tab_to("    uint32_t", "dword;")
 239		print("};\n")
 240
 241		depcrstr = ""
 242		if is_deprecated:
 243			depcrstr = " FD_DEPRECATED"
 244		if reg.array:
 245			print("static inline%s struct fd_reg_pair\npack_%s(uint32_t __i, struct %s fields)\n{" %
 246				  (depcrstr, prefix, prefix))
 247		else:
 248			print("static inline%s struct fd_reg_pair\npack_%s(struct %s fields)\n{" %
 249				  (depcrstr, prefix, prefix))
 250
 251		self.dump_regpair_builder(reg)
 252
 253		print("\n}\n")
 254
 255		if self.get_address_field():
 256			skip = ", { .reg = 0 }"
 257		else:
 258			skip = ""
 259
 260		if reg.array:
 261			print("#define %s(__i, ...) pack_%s(__i, __struct_cast(%s) { __VA_ARGS__ })%s\n" %
 262				  (prefix, prefix, prefix, skip))
 263		else:
 264			print("#define %s(...) pack_%s(__struct_cast(%s) { __VA_ARGS__ })%s\n" %
 265				  (prefix, prefix, prefix, skip))
 266
 267
 268	def dump(self, is_deprecated, prefix=None, reg=None):
 269		if prefix is None:
 270			prefix = self.name
 271		reg64 = reg and self.reg and self.reg.bit_size == 64
 272		if reg64:
 273			print("static inline uint32_t %s_LO(uint32_t val)\n{" % prefix)
 274			print("\treturn val;\n}")
 275			print("static inline uint32_t %s_HI(uint32_t val)\n{" % prefix)
 276			print("\treturn val;\n}")
 277		for f in self.fields:
 278			if f.name:
 279				name = prefix + "_" + f.name
 280			else:
 281				name = prefix
 282
 283			if not f.name and f.low == 0 and f.shr == 0 and f.type not in ["float", "fixed", "ufixed"]:
 284				pass
 285			elif f.type == "boolean" or (f.type is None and f.low == f.high):
 286				tab_to("#define %s" % name, "0x%08x" % (1 << f.low))
 287			else:
 288				typespec = "ull" if reg64 else "u"
 289				tab_to("#define %s__MASK" % name, "0x%08x%s" % (mask(f.low, f.high), typespec))
 290				tab_to("#define %s__SHIFT" % name, "%d" % f.low)
 291				type, val = f.ctype("val")
 292				ret_type = "uint64_t" if reg64 else "uint32_t"
 293				cast = "(uint64_t)" if reg64 else ""
 294
 295				print("static inline %s %s(%s val)\n{" % (ret_type, name, type))
 296				if f.shr > 0:
 297					print("\tassert(!(val & 0x%x));" % mask(0, f.shr - 1))
 298				print("\treturn (%s(%s) << %s__SHIFT) & %s__MASK;\n}" % (cast, val, name, name))
 299		print()
 300
 301class Array(object):
 302	def __init__(self, attrs, domain, variant, parent, index_type):
 303		if "name" in attrs:
 304			self.local_name = attrs["name"]
 305		else:
 306			self.local_name = ""
 307		self.domain = domain
 308		self.variant = variant
 309		self.parent = parent
 310		self.children = []
 311		if self.parent:
 312			self.name = self.parent.name + "_" + self.local_name
 313		else:
 314			self.name = self.local_name
 315		if "offsets" in attrs:
 316			self.offsets = map(lambda i: "0x%08x" % int(i, 0), attrs["offsets"].split(","))
 317			self.fixed_offsets = True
 318		elif "doffsets" in attrs:
 319			self.offsets = map(lambda s: "(%s)" % s , attrs["doffsets"].split(","))
 320			self.fixed_offsets = True
 321		else:
 322			self.offset = int(attrs["offset"], 0)
 323			self.stride = int(attrs["stride"], 0)
 324			self.fixed_offsets = False
 325		if "index" in attrs:
 326			self.index_type = index_type
 327		else:
 328			self.index_type = None
 329		self.length = int(attrs["length"], 0)
 330		if "usage" in attrs:
 331			self.usages = attrs["usage"].split(',')
 332		else:
 333			self.usages = None
 334
 335	def index_ctype(self):
 336		if not self.index_type:
 337			return "uint32_t"
 338		else:
 339			return "enum %s" % self.index_type.name
 340
 341	# Generate array of (ctype, stride, __offsets_NAME)
 342	def indices(self):
 343		if self.parent:
 344			indices = self.parent.indices()
 345		else:
 346			indices = []
 347		if self.length != 1:
 348			if self.fixed_offsets:
 349				indices.append((self.index_ctype(), None, "__offset_%s" % self.local_name))
 350			else:
 351				indices.append((self.index_ctype(), self.stride, None))
 352		return indices
 353
 354	def total_offset(self):
 355		offset = 0
 356		if not self.fixed_offsets:
 357			offset += self.offset
 358		if self.parent:
 359			offset += self.parent.total_offset()
 360		return offset
 361
 362	def dump(self, is_deprecated):
 363		depcrstr = ""
 364		if is_deprecated:
 365			depcrstr = " FD_DEPRECATED"
 366		proto = indices_varlist(self.indices())
 367		strides = indices_strides(self.indices())
 368		array_offset = self.total_offset()
 369		if self.fixed_offsets:
 370			print("static inline%s uint32_t __offset_%s(%s idx)" % (depcrstr, self.local_name, self.index_ctype()))
 371			print("{\n\tswitch (idx) {")
 372			if self.index_type:
 373				for val, offset in zip(self.index_type.names(), self.offsets):
 374					print("\t\tcase %s: return %s;" % (val, offset))
 375			else:
 376				for idx, offset in enumerate(self.offsets):
 377					print("\t\tcase %d: return %s;" % (idx, offset))
 378			print("\t\tdefault: return INVALID_IDX(idx);")
 379			print("\t}\n}")
 380		if proto == '':
 381			tab_to("#define REG_%s_%s" % (self.domain, self.name), "0x%08x\n" % array_offset)
 382		else:
 383			tab_to("#define REG_%s_%s(%s)" % (self.domain, self.name, proto), "(0x%08x + %s )\n" % (array_offset, strides))
 384
 385	def dump_pack_struct(self, is_deprecated):
 386		pass
 387
 388	def dump_regpair_builder(self):
 389		pass
 390
 391class Reg(object):
 392	def __init__(self, attrs, domain, array, bit_size):
 393		self.name = attrs["name"]
 394		self.domain = domain
 395		self.array = array
 396		self.offset = int(attrs["offset"], 0)
 397		self.type = None
 398		self.bit_size = bit_size
 399		if array:
 400			self.name = array.name + "_" + self.name
 401			array.children.append(self)
 402		self.full_name = self.domain + "_" + self.name
 403		if "stride" in attrs:
 404			self.stride = int(attrs["stride"], 0)
 405			self.length = int(attrs["length"], 0)
 406		else:
 407			self.stride = None
 408			self.length = None
 409
 410	# Generate array of (ctype, stride, __offsets_NAME)
 411	def indices(self):
 412		if self.array:
 413			indices = self.array.indices()
 414		else:
 415			indices = []
 416		if self.stride:
 417			indices.append(("uint32_t", self.stride, None))
 418		return indices
 419
 420	def total_offset(self):
 421		if self.array:
 422			return self.array.total_offset() + self.offset
 423		else:
 424			return self.offset
 425
 426	def reg_offset(self):
 427		if self.array:
 428			offset = self.array.offset + self.offset
 429			return "(0x%08x + 0x%x*__i)" % (offset, self.array.stride)
 430		return "0x%08x" % self.offset
 431
 432	def dump(self, is_deprecated):
 433		depcrstr = ""
 434		if is_deprecated:
 435			depcrstr = " FD_DEPRECATED "
 436		proto = indices_prototype(self.indices())
 437		strides = indices_strides(self.indices())
 438		offset = self.total_offset()
 439		if proto == '':
 440			tab_to("#define REG_%s" % self.full_name, "0x%08x" % offset)
 441		else:
 442			print("static inline%s uint32_t REG_%s(%s) { return 0x%08x + %s; }" % (depcrstr, self.full_name, proto, offset, strides))
 443
 444		if self.bitset.inline:
 445			self.bitset.dump(is_deprecated, self.full_name, self)
 446		print("")
 447
 448	def dump_pack_struct(self, is_deprecated):
 449		if self.bitset.inline:
 450			self.bitset.dump_pack_struct(is_deprecated, self)
 451
 452	def dump_regpair_builder(self):
 453		self.bitset.dump_regpair_builder(self)
 454
 455	def dump_py(self):
 456		print("\tREG_%s = 0x%08x" % (self.full_name, self.offset))
 457
 458
 459class Parser(object):
 460	def __init__(self):
 461		self.current_array = None
 462		self.current_domain = None
 463		self.current_prefix = None
 464		self.current_prefix_type = None
 465		self.current_stripe = None
 466		self.current_bitset = None
 467		self.current_bitsize = 32
 468		# The varset attribute on the domain specifies the enum which
 469		# specifies all possible hw variants:
 470		self.current_varset = None
 471		# Regs that have multiple variants.. we only generated the C++
 472		# template based struct-packers for these
 473		self.variant_regs = {}
 474		# Information in which contexts regs are used, to be used in
 475		# debug options
 476		self.usage_regs = collections.defaultdict(list)
 477		self.bitsets = {}
 478		self.enums = {}
 479		self.variants = set()
 480		self.file = []
 481		self.xml_files = []
 482
 483	def error(self, message):
 484		parser, filename = self.stack[-1]
 485		return Error("%s:%d:%d: %s" % (filename, parser.CurrentLineNumber, parser.CurrentColumnNumber, message))
 486
 487	def prefix(self, variant=None):
 488		if self.current_prefix_type == "variant" and variant:
 489			return sanitize_variant(variant)
 490		elif self.current_stripe:
 491			return self.current_stripe + "_" + self.current_domain
 492		elif self.current_prefix:
 493			return self.current_prefix + "_" + self.current_domain
 494		else:
 495			return self.current_domain
 496
 497	def parse_field(self, name, attrs):
 498		try:
 499			if "pos" in attrs:
 500				high = low = int(attrs["pos"], 0)
 501			elif "high" in attrs and "low" in attrs:
 502				high = int(attrs["high"], 0)
 503				low = int(attrs["low"], 0)
 504			else:
 505				low = 0
 506				high = self.current_bitsize - 1
 507
 508			if "type" in attrs:
 509				type = attrs["type"]
 510			else:
 511				type = None
 512
 513			if "shr" in attrs:
 514				shr = int(attrs["shr"], 0)
 515			else:
 516				shr = 0
 517
 518			b = Field(name, low, high, shr, type, self)
 519
 520			if type == "fixed" or type == "ufixed":
 521				b.radix = int(attrs["radix"], 0)
 522
 523			self.current_bitset.fields.append(b)
 524		except ValueError as e:
 525			raise self.error(e)
 526
 527	def parse_varset(self, attrs):
 528		# Inherit the varset from the enclosing domain if not overriden:
 529		varset = self.current_varset
 530		if "varset" in attrs:
 531			varset = self.enums[attrs["varset"]]
 532		return varset
 533
 534	def parse_variants(self, attrs):
 535		if "variants" not in attrs:
 536				return None
 537
 538		variant = attrs["variants"].split(",")[0]
 539		varset = self.parse_varset(attrs)
 540
 541		if "-" in variant:
 542			# if we have a range, validate that both the start and end
 543			# of the range are valid enums:
 544			start = variant[:variant.index("-")]
 545			end = variant[variant.index("-") + 1:]
 546			assert varset.has_name(start)
 547			if end != "":
 548				assert varset.has_name(end)
 549		else:
 550			assert varset.has_name(variant)
 551
 552		return variant
 553
 554	def add_all_variants(self, reg, attrs, parent_variant):
 555		# TODO this should really handle *all* variants, including dealing
 556		# with open ended ranges (ie. "A2XX,A4XX-") (we have the varset
 557		# enum now to make that possible)
 558		variant = self.parse_variants(attrs)
 559		if not variant:
 560			variant = parent_variant
 561
 562		if reg.name not in self.variant_regs:
 563			self.variant_regs[reg.name] = {}
 564		else:
 565			# All variants must be same size:
 566			v = next(iter(self.variant_regs[reg.name]))
 567			assert self.variant_regs[reg.name][v].bit_size == reg.bit_size
 568
 569		self.variant_regs[reg.name][variant] = reg
 570
 571	def add_all_usages(self, reg, usages):
 572		if not usages:
 573			return
 574
 575		for usage in usages:
 576			self.usage_regs[usage].append(reg)
 577
 578		self.variants.add(reg.domain)
 579
 580	def do_validate(self, schemafile):
 581		if not self.validate:
 582			return
 583
 584		try:
 585			from lxml import etree
 586
 587			parser, filename = self.stack[-1]
 588			dirname = os.path.dirname(filename)
 589
 590			# we expect this to look like <namespace url> schema.xsd.. I think
 591			# technically it is supposed to be just a URL, but that doesn't
 592			# quite match up to what we do.. Just skip over everything up to
 593			# and including the first whitespace character:
 594			schemafile = schemafile[schemafile.rindex(" ")+1:]
 595
 596			# this is a bit cheezy, but the xml file to validate could be
 597			# in a child director, ie. we don't really know where the schema
 598			# file is, the way the rnn C code does.  So if it doesn't exist
 599			# just look one level up
 600			if not os.path.exists(dirname + "/" + schemafile):
 601				schemafile = "../" + schemafile
 602
 603			if not os.path.exists(dirname + "/" + schemafile):
 604				raise self.error("Cannot find schema for: " + filename)
 605
 606			xmlschema_doc = etree.parse(dirname + "/" + schemafile)
 607			xmlschema = etree.XMLSchema(xmlschema_doc)
 608
 609			xml_doc = etree.parse(filename)
 610			if not xmlschema.validate(xml_doc):
 611				error_str = str(xmlschema.error_log.filter_from_errors()[0])
 612				raise self.error("Schema validation failed for: " + filename + "\n" + error_str)
 613		except ImportError as e:
 614			print("lxml not found, skipping validation", file=sys.stderr)
 615
 616	def do_parse(self, filename):
 617		filepath = os.path.abspath(filename)
 618		if filepath in self.xml_files:
 619			return
 620		self.xml_files.append(filepath)
 621		file = open(filename, "rb")
 622		parser = xml.parsers.expat.ParserCreate()
 623		self.stack.append((parser, filename))
 624		parser.StartElementHandler = self.start_element
 625		parser.EndElementHandler = self.end_element
 626		parser.CharacterDataHandler = self.character_data
 627		parser.buffer_text = True
 628		parser.ParseFile(file)
 629		self.stack.pop()
 630		file.close()
 631
 632	def parse(self, rnn_path, filename, validate):
 633		self.path = rnn_path
 634		self.stack = []
 635		self.validate = validate
 636		self.do_parse(filename)
 637
 638	def parse_reg(self, attrs, bit_size):
 639		self.current_bitsize = bit_size
 640		if "type" in attrs and attrs["type"] in self.bitsets:
 641			bitset = self.bitsets[attrs["type"]]
 642			if bitset.inline:
 643				self.current_bitset = Bitset(attrs["name"], bitset)
 644				self.current_bitset.inline = True
 645			else:
 646				self.current_bitset = bitset
 647		else:
 648			self.current_bitset = Bitset(attrs["name"], None)
 649			self.current_bitset.inline = True
 650			if "type" in attrs:
 651				self.parse_field(None, attrs)
 652
 653		variant = self.parse_variants(attrs)
 654		if not variant and self.current_array:
 655			variant = self.current_array.variant
 656
 657		self.current_reg = Reg(attrs, self.prefix(variant), self.current_array, bit_size)
 658		self.current_reg.bitset = self.current_bitset
 659		self.current_bitset.reg = self.current_reg
 660
 661		if len(self.stack) == 1:
 662			self.file.append(self.current_reg)
 663
 664		if variant is not None:
 665			self.add_all_variants(self.current_reg, attrs, variant)
 666
 667		usages = None
 668		if "usage" in attrs:
 669			usages = attrs["usage"].split(',')
 670		elif self.current_array:
 671			usages = self.current_array.usages
 672
 673		self.add_all_usages(self.current_reg, usages)
 674
 675	def start_element(self, name, attrs):
 676		self.cdata = ""
 677		if name == "import":
 678			filename = attrs["file"]
 679			self.do_parse(os.path.join(self.path, filename))
 680		elif name == "domain":
 681			self.current_domain = attrs["name"]
 682			if "prefix" in attrs:
 683				self.current_prefix = sanitize_variant(self.parse_variants(attrs))
 684				self.current_prefix_type = attrs["prefix"]
 685			else:
 686				self.current_prefix = None
 687				self.current_prefix_type = None
 688			if "varset" in attrs:
 689				self.current_varset = self.enums[attrs["varset"]]
 690		elif name == "stripe":
 691			self.current_stripe = sanitize_variant(self.parse_variants(attrs))
 692		elif name == "enum":
 693			self.current_enum_value = 0
 694			self.current_enum = Enum(attrs["name"])
 695			self.enums[attrs["name"]] = self.current_enum
 696			if len(self.stack) == 1:
 697				self.file.append(self.current_enum)
 698		elif name == "value":
 699			if "value" in attrs:
 700				value = int(attrs["value"], 0)
 701			else:
 702				value = self.current_enum_value
 703			self.current_enum.values.append((attrs["name"], value))
 704		elif name == "reg32":
 705			self.parse_reg(attrs, 32)
 706		elif name == "reg64":
 707			self.parse_reg(attrs, 64)
 708		elif name == "array":
 709			self.current_bitsize = 32
 710			variant = self.parse_variants(attrs)
 711			index_type = self.enums[attrs["index"]] if "index" in attrs else None
 712			self.current_array = Array(attrs, self.prefix(variant), variant, self.current_array, index_type)
 713			if len(self.stack) == 1:
 714				self.file.append(self.current_array)
 715		elif name == "bitset":
 716			self.current_bitset = Bitset(attrs["name"], None)
 717			if "inline" in attrs and attrs["inline"] == "yes":
 718				self.current_bitset.inline = True
 719			self.bitsets[self.current_bitset.name] = self.current_bitset
 720			if len(self.stack) == 1 and not self.current_bitset.inline:
 721				self.file.append(self.current_bitset)
 722		elif name == "bitfield" and self.current_bitset:
 723			self.parse_field(attrs["name"], attrs)
 724		elif name == "database":
 725			self.do_validate(attrs["xsi:schemaLocation"])
 726
 727	def end_element(self, name):
 728		if name == "domain":
 729			self.current_domain = None
 730			self.current_prefix = None
 731			self.current_prefix_type = None
 732		elif name == "stripe":
 733			self.current_stripe = None
 734		elif name == "bitset":
 735			self.current_bitset = None
 736		elif name == "reg32":
 737			self.current_reg = None
 738		elif name == "array":
 739			# if the array has no Reg children, push an implicit reg32:
 740			if len(self.current_array.children) == 0:
 741				attrs = {
 742					"name": "REG",
 743					"offset": "0",
 744				}
 745				self.parse_reg(attrs, 32)
 746			self.current_array = self.current_array.parent
 747		elif name == "enum":
 748			self.current_enum = None
 749
 750	def character_data(self, data):
 751		self.cdata += data
 752
 753	def dump_reg_usages(self):
 754		d = collections.defaultdict(list)
 755		for usage, regs in self.usage_regs.items():
 756			for reg in regs:
 757				variants = self.variant_regs.get(reg.name)
 758				if variants:
 759					for variant, vreg in variants.items():
 760						if reg == vreg:
 761							d[(usage, sanitize_variant(variant))].append(reg)
 762				else:
 763					for variant in self.variants:
 764						d[(usage, sanitize_variant(variant))].append(reg)
 765
 766		print("#ifdef __cplusplus")
 767
 768		for usage, regs in self.usage_regs.items():
 769			print("template<chip CHIP> constexpr inline uint16_t %s_REGS[] = {};" % (usage.upper()))
 770
 771		for (usage, variant), regs in d.items():
 772			offsets = []
 773
 774			for reg in regs:
 775				if reg.array:
 776					for i in range(reg.array.length):
 777						offsets.append(reg.array.offset + reg.offset + i * reg.array.stride)
 778						if reg.bit_size == 64:
 779							offsets.append(offsets[-1] + 1)
 780				else:
 781					offsets.append(reg.offset)
 782					if reg.bit_size == 64:
 783						offsets.append(offsets[-1] + 1)
 784
 785			offsets.sort()
 786
 787			print("template<> constexpr inline uint16_t %s_REGS<%s>[] = {" % (usage.upper(), variant))
 788			for offset in offsets:
 789				print("\t%s," % hex(offset))
 790			print("};")
 791
 792		print("#endif")
 793
 794	def has_variants(self, reg):
 795		return reg.name in self.variant_regs and not is_number(reg.name) and not is_number(reg.name[1:])
 796
 797	def dump(self):
 798		enums = []
 799		bitsets = []
 800		regs = []
 801		for e in self.file:
 802			if isinstance(e, Enum):
 803				enums.append(e)
 804			elif isinstance(e, Bitset):
 805				bitsets.append(e)
 806			else:
 807				regs.append(e)
 808
 809		for e in enums + bitsets + regs:
 810			e.dump(self.has_variants(e))
 811
 812		self.dump_reg_usages()
 813
 814
 815	def dump_regs_py(self):
 816		regs = []
 817		for e in self.file:
 818			if isinstance(e, Reg):
 819				regs.append(e)
 820
 821		for e in regs:
 822			e.dump_py()
 823
 824
 825	def dump_reg_variants(self, regname, variants):
 826		if is_number(regname) or is_number(regname[1:]):
 827			return
 828		print("#ifdef __cplusplus")
 829		print("struct __%s {" % regname)
 830		# TODO be more clever.. we should probably figure out which
 831		# fields have the same type in all variants (in which they
 832		# appear) and stuff everything else in a variant specific
 833		# sub-structure.
 834		seen_fields = []
 835		bit_size = 32
 836		array = False
 837		address = None
 838		for variant in variants.keys():
 839			print("    /* %s fields: */" % variant)
 840			reg = variants[variant]
 841			bit_size = reg.bit_size
 842			array = reg.array
 843			for f in reg.bitset.fields:
 844				fld_name = field_name(reg, f)
 845				if fld_name in seen_fields:
 846					continue
 847				seen_fields.append(fld_name)
 848				name = fld_name.lower()
 849				if f.type in [ "address", "waddress" ]:
 850					if address:
 851						continue
 852					address = f
 853					tab_to("    __bo_type", "bo;")
 854					tab_to("    uint32_t", "bo_offset;")
 855					continue
 856				type, val = f.ctype("var")
 857				tab_to("    %s" %type, "%s;" %name)
 858		print("    /* fallback fields: */")
 859		if bit_size == 64:
 860			tab_to("    uint64_t", "unknown;")
 861			tab_to("    uint64_t", "qword;")
 862		else:
 863			tab_to("    uint32_t", "unknown;")
 864			tab_to("    uint32_t", "dword;")
 865		print("};")
 866		# TODO don't hardcode the varset enum name
 867		varenum = "chip"
 868		print("template <%s %s>" % (varenum, varenum.upper()))
 869		print("static inline struct fd_reg_pair")
 870		xtra = ""
 871		xtravar = ""
 872		if array:
 873			xtra = "int __i, "
 874			xtravar = "__i, "
 875		print("__%s(%sstruct __%s fields) {" % (regname, xtra, regname))
 876		for variant in variants.keys():
 877			if "-" in variant:
 878				start = variant[:variant.index("-")]
 879				end = variant[variant.index("-") + 1:]
 880				if end != "":
 881					print("  if ((%s >= %s) && (%s <= %s)) {" % (varenum.upper(), start, varenum.upper(), end))
 882				else:
 883					print("  if (%s >= %s) {" % (varenum.upper(), start))
 884			else:
 885				print("  if (%s == %s) {" % (varenum.upper(), variant))
 886			reg = variants[variant]
 887			reg.dump_regpair_builder()
 888			print("  } else")
 889		print("    assert(!\"invalid variant\");")
 890		print("  return (struct fd_reg_pair){};")
 891		print("}")
 892
 893		if bit_size == 64:
 894			skip = ", { .reg = 0 }"
 895		else:
 896			skip = ""
 897
 898		print("#define %s(VARIANT, %s...) __%s<VARIANT>(%s{__VA_ARGS__})%s" % (regname, xtravar, regname, xtravar, skip))
 899		print("#endif /* __cplusplus */")
 900
 901	def dump_structs(self):
 902		for e in self.file:
 903			e.dump_pack_struct(self.has_variants(e))
 904
 905		for regname in self.variant_regs:
 906			self.dump_reg_variants(regname, self.variant_regs[regname])
 907
 908
 909def dump_c(args, guard, func):
 910	p = Parser()
 911
 912	try:
 913		p.parse(args.rnn, args.xml, args.validate)
 914	except Error as e:
 915		print(e, file=sys.stderr)
 916		exit(1)
 917
 918	print("#ifndef %s\n#define %s\n" % (guard, guard))
 919
 920	print("/* Autogenerated file, DO NOT EDIT manually! */")
 921
 922	print()
 923	print("#ifdef __KERNEL__")
 924	print("#include <linux/bug.h>")
 925	print("#define assert(x) BUG_ON(!(x))")
 926	print("#else")
 927	print("#include <assert.h>")
 928	print("#endif")
 929	print()
 930
 931	print("#ifdef __cplusplus")
 932	print("#define __struct_cast(X)")
 933	print("#else")
 934	print("#define __struct_cast(X) (struct X)")
 935	print("#endif")
 936	print()
 937
 938	print("#ifndef FD_NO_DEPRECATED_PACK")
 939	print("#define FD_DEPRECATED __attribute__((deprecated))")
 940	print("#else")
 941	print("#define FD_DEPRECATED")
 942	print("#endif")
 943	print()
 944
 945	func(p)
 946
 947	print()
 948	print("#undef FD_DEPRECATED")
 949	print()
 950
 951	print("#endif /* %s */" % guard)
 952
 953
 954def dump_c_defines(args):
 955	guard = str.replace(os.path.basename(args.xml), '.', '_').upper()
 956	dump_c(args, guard, lambda p: p.dump())
 957
 958
 959def dump_c_pack_structs(args):
 960	guard = str.replace(os.path.basename(args.xml), '.', '_').upper() + '_STRUCTS'
 961	dump_c(args, guard, lambda p: p.dump_structs())
 962
 963
 964def dump_py_defines(args):
 965	p = Parser()
 966
 967	try:
 968		p.parse(args.rnn, args.xml, args.validate)
 969	except Error as e:
 970		print(e, file=sys.stderr)
 971		exit(1)
 972
 973	file_name = os.path.splitext(os.path.basename(args.xml))[0]
 974
 975	print("from enum import IntEnum")
 976	print("class %sRegs(IntEnum):" % file_name.upper())
 977
 978	os.path.basename(args.xml)
 979
 980	p.dump_regs_py()
 981
 982
 983def main():
 984	parser = argparse.ArgumentParser()
 985	parser.add_argument('--rnn', type=str, required=True)
 986	parser.add_argument('--xml', type=str, required=True)
 987	parser.add_argument('--validate', default=False, action='store_true')
 988	parser.add_argument('--no-validate', dest='validate', action='store_false')
 989
 990	subparsers = parser.add_subparsers()
 991	subparsers.required = True
 992
 993	parser_c_defines = subparsers.add_parser('c-defines')
 994	parser_c_defines.set_defaults(func=dump_c_defines)
 995
 996	parser_c_pack_structs = subparsers.add_parser('c-pack-structs')
 997	parser_c_pack_structs.set_defaults(func=dump_c_pack_structs)
 998
 999	parser_py_defines = subparsers.add_parser('py-defines')
1000	parser_py_defines.set_defaults(func=dump_py_defines)
1001
1002	args = parser.parse_args()
1003	args.func(args)
1004
1005
1006if __name__ == '__main__':
1007	main()
~/ Read-only file preview.