When interrupt requests can appear at an arbitrary rate, there is the possibility of overflowing the stack, even with an external interrupt controller:
When the ZPU is in the process of leaving an interrupt service routine, it first has to acknowledge the serviced interrupt at the interrupt controller. This should be the last C statement in _zpu_interrupt(). When execution reaches the end of _zpu_interrupt(), roughly the following assembler code is executed (this is taken from the assembler output of the compiled _zpu_interrupt() function in our project):
store storesp storesp storesp im pushapadd popsp poppc
The last poppc causes the execution to jump back into crt0 code, where the _zpu_interrupt() function was called. From there, another poppc causes execution to return to the code executed before the interrupt request happened and execution jsr'ed into _zpu_interrupt().
When an interrupt request happens after the interrupt controller has been told that servicing the interrupt request is ended, but before the final poppc from crt0, then execution jumps to the interrupt vector code in crt0 etc.
If the interrupt request frequency is high enough (misconfigured timer, demanding hardware), a stack overflow is the cause.
We investigated several options, including delaying the next interrupt request for several cycles inside the interrupt controller, but since the memory subsystem can cause the ZPU to need any number of cycles for one instruction, this is infeasible without status information from the ZPU to the interrupt controller.
What we did then was add another instruction to the ZPU, namely "Return from Interrupt". It duplicates functionality of poppc, but in addition, it resets the inInterrupt flag. Also, this new instruction is now the ONLY way to reset the inInterrupt flag.
To support this, we had to patch crt0.S to insert our new opcode via a .byte statement instead the poppc. It would be great if the instruction can be added to the ZPU and the toolchain, maybe as an emulated opcode with a fallback in crt0 for cores which do not support the instruction.