freedreno/registers: Generate perfcntr tables

Use the register xml in combination with the json describing the
different perfcntr groups to generate perfcntr tables.

Signed-off-by: Rob Clark <rob.clark@oss.qualcomm.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/40522>
This commit is contained in:
Rob Clark 2026-03-19 08:18:42 -07:00 committed by Marge Bot
parent 2b81514c3e
commit 546d4daa6d
2 changed files with 160 additions and 0 deletions

View file

@ -17,6 +17,13 @@ xml_reg_files = [
'a8xx_perfcntrs.xml',
]
perfcntr_files = [
[ 'a2xx.xml', 'a2xx_perfcntrs.json' ],
[ 'a5xx.xml', 'a5xx_perfcntrs.json' ],
[ 'a6xx.xml', 'a6xx_perfcntrs.json' ],
[ 'a6xx.xml', 'a7xx_perfcntrs.json' ],
]
xml_files = xml_reg_files
xml_files += [
'a6xx_gmu.xml',
@ -82,6 +89,17 @@ foreach f : xml_files
endif
endforeach
foreach e : perfcntr_files
_name = e[1] + '.h'
freedreno_xml_header_files += custom_target(
_name,
input: [gen_header_py, e[0], e[1], freedreno_schema, freedreno_copyright],
output: _name,
command: [prog_python, '@INPUT0@', '--validate', '--rnn', rnn_src_path, '--xml', '@INPUT1@', 'perfcntrs', '--json', '@INPUT2@'],
capture: true,
)
endforeach
freedreno_xml_header_files += custom_target(
'a6xx-pack.xml.h',
input: [gen_header_py, 'a6xx.xml', freedreno_schema, freedreno_copyright],

View file

@ -11,6 +11,7 @@ import collections
import argparse
import time
import datetime
import json
class Error(Exception):
@ -32,6 +33,11 @@ class Enum(object):
def names(self):
return [n for (n, value) in self.values]
def value(self, name):
for (n, v) in self.values:
if n == name:
return v
def dump(self, has_variants):
use_hex = False
for (name, value) in self.values:
@ -1017,6 +1023,138 @@ def dump_c_pack_structs(args):
dump_c(args, guard, lambda p: p.dump_structs())
def dump_perfcntrs(args):
p = Parser()
try:
p.parse(args.rnn, args.xml, args.validate)
except Error as e:
print(e, file=sys.stderr)
exit(1)
perfcntrs = json.load(open(args.json, "r", encoding="utf-8"))
chip_type = p.enums['chip']
chip = perfcntrs['chip']
if not chip_type.has_name(chip):
raise Error("Invalid chip: " + chip)
groups = perfcntrs['groups']
guard = "__" + chip + "_PERFCNTRS_"
print("#ifndef %s\n#define %s\n" % (guard, guard))
print("/* Autogenerated file, DO NOT EDIT manually! */")
print()
print("#ifdef __KERNEL__")
print("#include \"msm_perfcntr.h\"")
print("#endif")
print()
def has_variant(variant):
if variant is None:
return True
if "-" in variant:
start = chip_type.value(variant[:variant.index("-")])
end = chip_type.value(variant[variant.index("-") + 1:])
chipn = chip_type.value(chip)
return (start is None or chipn >= start) and (end is None or chipn <= end)
return chip == variant
# Split out arrays and regs for later access:
arrays = {}
regs = {}
for e in p.file:
if isinstance(e, Array) and has_variant(e.variant):
arrays[e.local_name] = e
if isinstance(e, Reg):
regs[e.name] = e
# For variant regs, overwrite 'regs' entries with correct variant:
for regname in p.variant_regs:
for (variant, reg) in p.variant_regs[regname].items():
if has_variant(variant):
regs[regname] = reg
break
for group in groups:
name = group['name']
name_low = name.lower()
num = group['num']
countable_type_name = group['countable_type']
if not countable_type_name in p.enums:
raise Error("Invalid type: " + countable_type_name)
countable_type = p.enums[countable_type_name]
print("#ifndef __KERNEL__")
print("static const struct fd_perfcntr_countable " + name_low + "_countables[] = {")
for (name, value) in countable_type.values:
# if the countable is prefixed with the chip, strip that:
# (note: avoid py3.9 dependency for kernel)
if name.startswith(chip + "_"):
name = name[len(chip)+1:]
print(" { \"" + name + "\", " + str(value) + " },")
print("};")
print("#endif")
print("static const struct fd_perfcntr_counter " + name_low + "_counters[] = {")
for i in range(0, num):
if "reserved" in group and i in group["reserved"]:
continue
def get_reg(name):
# if reg has {} pattern, expand that first:
name = name.format(i)
if name in arrays:
arr = arrays[name]
return arr.offset + (i * arr.stride)
if not name in regs:
raise Error("Invalid reg: " + name)
reg = regs[name]
return reg.offset
def get_counter():
# if the counter is <reg64> just a single "counter" value
# should be specified in the json, but for legacy separate
# hi/lo <reg32> pairs "counter_lo" and "counter_hi" should
# be specified
if "counter" in group:
counter = get_reg(group["counter"])
return [counter, counter+1]
counter_lo = get_reg(group["counter_lo"])
counter_hi = get_reg(group["counter_hi"])
return [counter_lo, counter_hi]
(counter_lo, counter_hi) = get_counter()
select = get_reg(group['select'])
if "select_offset" in group:
select = select + int(group["select_offset"])
# TODO add support for things that need enable/clear regs
print(" { 0x%04x, 0x%04x, 0x%04x }," % (select, counter_lo, counter_hi))
print("};")
print()
print("const struct fd_perfcntr_group " + chip.lower() + "_perfcntr_groups[] = {")
for group in groups:
name = group['name']
name_low = name.lower()
print(" GROUP(\"%s\", %s_counters, %s_countables)," % (name, name_low, name_low))
print("};")
print("const unsigned " + chip.lower() + "_num_perfcntr_groups = ARRAY_SIZE(" + chip.lower() + "_perfcntr_groups);")
print()
print("#endif /* %s */" % guard)
def dump_py_defines(args):
p = Parser()
@ -1052,6 +1190,10 @@ def main():
parser_c_pack_structs = subparsers.add_parser('c-pack-structs')
parser_c_pack_structs.set_defaults(func=dump_c_pack_structs)
parser_perfcntrs = subparsers.add_parser('perfcntrs')
parser_perfcntrs.add_argument('--json', type=str, required=True)
parser_perfcntrs.set_defaults(func=dump_perfcntrs)
parser_py_defines = subparsers.add_parser('py-defines')
parser_py_defines.set_defaults(func=dump_py_defines)