From 546d4daa6d493324af2633ee95eb24affaa868a3 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Thu, 19 Mar 2026 08:18:42 -0700 Subject: [PATCH] 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 Part-of: --- src/freedreno/registers/adreno/meson.build | 18 +++ src/freedreno/registers/gen_header.py | 142 +++++++++++++++++++++ 2 files changed, 160 insertions(+) diff --git a/src/freedreno/registers/adreno/meson.build b/src/freedreno/registers/adreno/meson.build index 693b68df39a..dde10fe5f6d 100644 --- a/src/freedreno/registers/adreno/meson.build +++ b/src/freedreno/registers/adreno/meson.build @@ -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], diff --git a/src/freedreno/registers/gen_header.py b/src/freedreno/registers/gen_header.py index 789edcca80b..040db6c53f8 100644 --- a/src/freedreno/registers/gen_header.py +++ b/src/freedreno/registers/gen_header.py @@ -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 just a single "counter" value + # should be specified in the json, but for legacy separate + # hi/lo 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)