2021-09-22 04:37:32 +08:00
|
|
|
#!/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>")
|
2022-05-25 23:35:50 +08:00
|
|
|
cprint("#include <zephyr/sys/util.h>")
|
|
|
|
cprint("#include <zephyr/sw_isr_table.h>")
|
2021-09-22 04:37:32 +08:00
|
|
|
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("")
|