#!/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 ") cprint("#include ") cprint("#include ") 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("")