zephyr/arch/xtensa/core/xtensa_intgen.py
Andy Ross bcf6b27c6b arch/xtensa: xtensa_intgen.py: Emit handlers for all levels
The original code would (unsurprisingly) only emit handler functions
for interrupt levels with interrupts associated with them.  But it
turns out that it's possible to configure an xtensa device with an
empty-but-otherwise-real interrupt level (specifically mt8195 has a
"Level 3" interrupt not associated with any input IRQS, it's one level
above EXCM_LEVEL and one level below the DEBUG exception).

This script is old, and not set up to parse the full core-isa.h
directly, so modifying it to detect this condition is difficult.
Instead, just emit all 15 possible interrupt handlers, even empty
ones.  The extra stubs are harmless as they'll be dropped if uncalled.

Signed-off-by: Andy Ross <andyross@google.com>
2024-05-20 20:50:55 -04:00

139 lines
4.6 KiB
Python
Executable file

#!/usr/bin/env python3
import re
import fileinput
# Pass an Xtensa core-isa.h file on stdin or the command line, emits a
# C file on output containing optimized interrupt dispatch routines.
# FIXME: looking at the assembly generated by the ESP-32 toolchain,
# this isn't as optimal as I'd hoped. the individual cases are tested
# using a L32R + BNONE (i.e. a full mask test) instead of a BBSI, and
# the handlers are being invoked with CALL8 instead of CALL4,
# inexplicably wasting four words of stack. Maybe this should be
# emitting assembly instead. Wouldn't be much more complicated and
# would share all the same structure.
# My manual count of instructions says that a linear search becomes
# faster on average when there are three or fewer bits to test. Would
# be four, if the compiler would generate BBSI instructions.
MAX_TESTS = 3
ints_by_lvl = {}
# print() wrapper that automatically handles indentation levels
cindent = 0
def cprint(s):
global cindent
if s.endswith(":"):
print(s)
return
if s.find("}") >= 0:
cindent -= 1
s = cindent*"\t" + s
print(s)
if s.find("{") >= 0:
cindent += 1
def emit_int_handler(ints):
if len(ints) <= MAX_TESTS:
for i in ints:
# FIXME: a little work could allow us to extract the
# handler pointer and argument as literals, saving a few
# instructions and avoiding the need to link in
# _sw_isr_table entirely.
cprint("if (mask & BIT(%d)) {" % i)
cprint("mask = BIT(%d);" % i)
cprint("irq = %d;" % i)
cprint("goto handle_irq;")
cprint("}")
else:
half = int(len(ints)/2)
m = 0
for i in ints[0:half]:
m |= 1 << i
cprint("if (mask & " + ("0x%x" % (m)) + ") {")
emit_int_handler(ints[0:half])
cprint("} else {")
emit_int_handler(ints[half:])
cprint("}")
########################################################################
# Annoyingly need to join lines and remove #-marked annotations. Some
# versions of the preprocessor (ahem, esp32 SDK) like to include
# newlines in the output where the original expressions are expanded
# from 100% single line macros. Slurp it into a single string and
# parse via whitespace.
blob = ""
for l in fileinput.input():
l = l if l.find("#") < 0 else l[0:l.find("#")]
blob += l.rstrip() + " "
for match in re.finditer(r'__xtensa_int_level_magic__\s+(\d+)\s+(\d+)', blob):
irq = int(match.group(1))
lvl = int(match.group(2))
if lvl not in ints_by_lvl:
ints_by_lvl[lvl] = []
ints_by_lvl[lvl].append(irq)
cprint("/*")
cprint(" * THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT.")
cprint(" *")
cprint(" * Functions here are designed to produce efficient code to")
cprint(" * search an Xtensa bitmask of interrupts, inspecting only those bits")
cprint(" * declared to be associated with a given interrupt level. Each")
cprint(" * dispatcher will handle exactly one flagged interrupt, in numerical")
cprint(" * order (low bits first) and will return a mask of that bit that can")
cprint(" * then be cleared by the calling code. Unrecognized bits for the")
cprint(" * level will invoke an error handler.")
cprint(" */")
cprint("")
# Re-include the core-isa header and be sure our definitions match, for sanity
cprint("#include <xtensa/config/core-isa.h>")
cprint("#include <zephyr/sys/util.h>")
cprint("#include <zephyr/sw_isr_table.h>")
cprint("")
for l in ints_by_lvl:
for i in ints_by_lvl[l]:
v = "XCHAL_INT" + str(i) + "_LEVEL"
cprint("#if !defined(" + v + ") || " + str(v) + " != " + str(l))
cprint("#error core-isa.h interrupt level does not match dispatcher!")
cprint("#endif")
cprint("")
# Populate all theoretical levels just in case. Odd cores have been
# seen in the wild with "empty" interrupt levels that exist in the
# hardware but without any interrupts associated with them. The
# unused handlers will be ignored if uncalled.
max = 15
for lvl in range(0, max+1):
if not lvl in ints_by_lvl:
ints_by_lvl[lvl] = []
# Emit the handlers
for lvl in ints_by_lvl:
cprint("static inline int _xtensa_handle_one_int" + str(lvl) + "(unsigned int mask)")
cprint("{")
if not ints_by_lvl[lvl]:
cprint("return 0;")
cprint("}")
continue
cprint("int irq;")
print("")
emit_int_handler(sorted(ints_by_lvl[lvl]))
cprint("return 0;")
cprint("handle_irq:")
cprint("_sw_isr_table[irq].isr(_sw_isr_table[irq].arg);")
cprint("return mask;")
cprint("}")
cprint("")