Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | #!/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 not lvl 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 <misc/util.h>")
cprint("#include <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 empty levels just for sanity. The second-to-last interrupt
# level (usually "debug") typically doesn't have any associated
# vectors, but we don't have any way to know that a-prioi.
max = 0
for lvl in ints_by_lvl:
if lvl > max:
max = lvl
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("")
|