1#!/usr/bin/env python3 2import re 3import fileinput 4 5# Pass an Xtensa core-isa.h file on stdin or the command line, emits a 6# C file on output containing optimized interrupt dispatch routines. 7 8# FIXME: looking at the assembly generated by the ESP-32 toolchain, 9# this isn't as optimal as I'd hoped. the individual cases are tested 10# using a L32R + BNONE (i.e. a full mask test) instead of a BBSI, and 11# the handlers are being invoked with CALL8 instead of CALL4, 12# inexplicably wasting four words of stack. Maybe this should be 13# emitting assembly instead. Wouldn't be much more complicated and 14# would share all the same structure. 15 16# My manual count of instructions says that a linear search becomes 17# faster on average when there are three or fewer bits to test. Would 18# be four, if the compiler would generate BBSI instructions. 19MAX_TESTS = 3 20 21ints_by_lvl = {} 22 23# print() wrapper that automatically handles indentation levels 24cindent = 0 25def cprint(s): 26 global cindent 27 if s.endswith(":"): 28 print(s) 29 return 30 if s.find("}") >= 0: 31 cindent -= 1 32 s = cindent*"\t" + s 33 print(s) 34 if s.find("{") >= 0: 35 cindent += 1 36 37def emit_int_handler(ints): 38 if len(ints) <= MAX_TESTS: 39 for i in ints: 40 # FIXME: a little work could allow us to extract the 41 # handler pointer and argument as literals, saving a few 42 # instructions and avoiding the need to link in 43 # _sw_isr_table entirely. 44 cprint("if (mask & BIT(%d)) {" % i) 45 cprint("mask = BIT(%d);" % i) 46 cprint("irq = %d;" % i) 47 cprint("goto handle_irq;") 48 cprint("}") 49 else: 50 half = int(len(ints)/2) 51 52 m = 0 53 for i in ints[0:half]: 54 m |= 1 << i 55 cprint("if (mask & " + ("0x%x" % (m)) + ") {") 56 emit_int_handler(ints[0:half]) 57 cprint("} else {") 58 emit_int_handler(ints[half:]) 59 cprint("}") 60 61######################################################################## 62 63# Annoyingly need to join lines and remove #-marked annotations. Some 64# versions of the preprocessor (ahem, esp32 SDK) like to include 65# newlines in the output where the original expressions are expanded 66# from 100% single line macros. Slurp it into a single string and 67# parse via whitespace. 68blob = "" 69for l in fileinput.input(): 70 l = l if l.find("#") < 0 else l[0:l.find("#")] 71 blob += l.rstrip() + " " 72 73for match in re.finditer(r'__xtensa_int_level_magic__\s+(\d+)\s+(\d+)', blob): 74 irq = int(match.group(1)) 75 lvl = int(match.group(2)) 76 77 if lvl not in ints_by_lvl: 78 ints_by_lvl[lvl] = [] 79 80 ints_by_lvl[lvl].append(irq) 81 82cprint("/*") 83cprint(" * THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT.") 84cprint(" *") 85cprint(" * Functions here are designed to produce efficient code to") 86cprint(" * search an Xtensa bitmask of interrupts, inspecting only those bits") 87cprint(" * declared to be associated with a given interrupt level. Each") 88cprint(" * dispatcher will handle exactly one flagged interrupt, in numerical") 89cprint(" * order (low bits first) and will return a mask of that bit that can") 90cprint(" * then be cleared by the calling code. Unrecognized bits for the") 91cprint(" * level will invoke an error handler.") 92cprint(" */") 93cprint("") 94 95# Re-include the core-isa header and be sure our definitions match, for sanity 96cprint("#include <xtensa/config/core-isa.h>") 97cprint("#include <zephyr/sys/util.h>") 98cprint("#include <zephyr/sw_isr_table.h>") 99cprint("") 100for l in ints_by_lvl: 101 for i in ints_by_lvl[l]: 102 v = "XCHAL_INT" + str(i) + "_LEVEL" 103 cprint("#if !defined(" + v + ") || " + str(v) + " != " + str(l)) 104 cprint("#error core-isa.h interrupt level does not match dispatcher!") 105 cprint("#endif") 106cprint("") 107 108# Populate empty levels just for sanity. The second-to-last interrupt 109# level (usually "debug") typically doesn't have any associated 110# vectors, but we don't have any way to know that a-prioi. 111max = 0 112for lvl in ints_by_lvl: 113 if lvl > max: 114 max = lvl 115 116for lvl in range(0, max+1): 117 if not lvl in ints_by_lvl: 118 ints_by_lvl[lvl] = [] 119 120# Emit the handlers 121for lvl in ints_by_lvl: 122 cprint("static inline int _xtensa_handle_one_int" + str(lvl) + "(unsigned int mask)") 123 cprint("{") 124 125 if not ints_by_lvl[lvl]: 126 cprint("return 0;") 127 cprint("}") 128 continue 129 130 cprint("int irq;") 131 print("") 132 133 emit_int_handler(sorted(ints_by_lvl[lvl])) 134 135 cprint("return 0;") 136 cprint("handle_irq:") 137 cprint("_sw_isr_table[irq].isr(_sw_isr_table[irq].arg);") 138 cprint("return mask;") 139 cprint("}") 140 cprint("") 141