Does my fiq_handler need to call portSAVE_CONTEXT() and portRESTORE_CONTEXT the way my vectored IRQ handlers do? For an FIQ handler, this sure seems like a lot of overhead. (I have a demanding app where microseconds matter). I saw some references to portENTER_FIQ() and portEXIT_FIQ(), but it is not available in my port for the lpc2xxx on the Rowley compiler. Ultimately, if I rewrite my fiq handler in assembly using only the registers unique to the FIQ mode (F7_fiq to R14_fiq), shouldn’t I be able to avoid a lot of this pushing and popping?
I am not 100% familiar with your platform/compiler but I can tell you that generally speaking you only need to save restore context if you are interacting with the Kernel in some way. If you call any of the queue functionality or anything else that could trigger a Task switch, then you need to save and restore the context at the beginning and end of your interrupt handlers.
Otherwise, as long as there is no chance of a task switch then you are free to write what interrupt handler you want. Indeed, for ports where there is interrupt nesting, typically, the RTOS reserves the lower half of the priorities for interrupts with xQueueSendFromISR and xQueueReceiveFromISR and the top half for half for v.fast interrupt handlers. Therefore, you need to ensure that all of the Kernel interrupts are masked off as well when you are writing your handler.
I hope that answers your question?
I have some interrupt handlers in which I do not need to trigger an RTOS context switch, but when I try commenting out portSAVE_CONTEXT(); and portRESTORE_CONTEXT(); below, my interrupts no longer work properly. I have verified that I have this identical problem for 2 different vectored interrupt types that have no RTOS interaction: external and timer1 (on lpc2148). For both, they work perfectly provided I make these save and restore calls, but if I don’t the interrupt never gets cleared and repeatedly fires. Does anyone know why this might be? I had thought that perhaps the portSAVE_CONTEXT call was introducing a needed timing delay, and tried replacing it with some code that produced equivalent timing delay to see if this would work just as well. Not so. Then I thought maybe it had something to do with the IRQ and FIQ bits in the CPSR, so I tried replacing the lines with portDISABLE_INTERRUPTS() and portENABLE_INTERRUPTS. This didn’t work either. I realize that Rowley doesn’t support freeRTOS and conversely, there is not an freeRTOS port specific to Rowley (I’m using the GNU port). Perhaps the problem lies in the differences between the GNU and Rowley compiler/Rowley startup code? (You’ll notice that I’m not using the GNU interrupt attribute because I don’t think Rowley recommends it.) Here’s the code:
static void vTimer1_ISREntry(void) __attribute__ ((naked));
static void vTimer1_ISRHandler(void) __attribute__ ((noinline));
static void vTimer1_ISREntry(void)
static void vTimer1_ISRHandler(void)
static const unsigned long CLEAR_VIC_INTERRUPT = 0;
// Clear all timer1 interrupts
T1_IR = 0xFF;
// Clear interrupt in VIC
VIC_VectAddr = CLEAR_VIC_INTERRUPT;
Okay. I will try to find some time tomorrow to have a look at the port layer files that you started from, I would like to know what those macros contain. Which port is it? LPC2168 GCC?
The portSAVE_CONTEXT() and restore will be allowing you to do work with the registers and the stack and then replacing anything you clobbered. Without those macros, you need to make sure that any registers that your C code clobbers, or any space you take up on the stack is reclaimed at the end of the interrupt entry function. The easiest way to do this is to look at the disassembly when you are debugging and follow the pre-amble through, ensuring that the before the ‘return from interrupt’ assembly instruction is called, that all of the registers have identical/expected values in them. It could even be something as trivial as the PSW status bits aren’t the same.
Typically, you would push as many registers as you need to the stack, use the registers you have saved and then pop those registers back off the stack. I would have hoped that the compiler has some way of declaring interrupt handlers so that you don’t need to do this manually but it could be that FIQ handlers are intended to be hand-coded to make them as fast as possible, hence why you are not getting the protection.
I think you’re right - it doesn’t look like the Rowley assembly in my project is doing the pushing and popping for me, and that the port save and restore makes it work since it does it for me. I am surprised as well, as I have never seen a vendor that didn’t do this. But I know that there are various assembly files that are included in the build depending on which Rowley project type I choose - I guess I chose the wrong type. I have this problem regardless of whether I use vectored IRQs or FIQs. I’ll try adding in code to push and pop the registers, and that should do the trick. By doing it myself, I’ll also be able to optimize them. I’m using the GCC port for ARM7_LPC2000, V6.0.5 released mid-May 2010. Thanks again for your thoughtful response.
Most compilers have some way to declare that a function is an ISR and needs to have the context saved based on this condition. Often this saves more context than a general subroutine, as the calling convention rules often declare some registers a “scrap” and if a caller needs them saved over the call it is their responsibility, not the subroutine, an interrupt routine must of course save all registers that it might use. In your case, you haven’t declared the function to be an ISR, but have declared it just the opposite, to be “naked” which means that the compiler will generate NO prologue/epilogue to save registers (because we need to use the FreeRTOS macros to make sure they are done identically in all usages. If you then remove these saves, your ISR will clobber the registers it used.
This makes sense. Thanks for your nice explanation Richard!
I’ve tested that my interrupts work just fine when I declare my interrupt handlers with
void ext1_handler (void)__attribute__((interrupt(“IRQ”)));
void fiq_handler (void)__attribute__((interrupt(“FIQ”)));
for IRQ and FIQ respectively. I also remove the calls to save and restore context. The compiler generates very fast push and pop assembly for me given these attributes. Nice to have options!