FIQ problems under -O2 optimization

liamstask wrote on Monday, July 16, 2007:

I have some code that makes use of FIQs to provide callbacks for a high-resolution timing system.  This code works great when I build with -O0 optimization (both under Rowley CrossWorks GCC build, and GnuArm toolchain builds).  However, once I notch the optimization up to O2 (or O1 even, but I’m hoping ultimately to use O2), the FIQ code goes a bit wonky. 

It’s tricky to track down exactly where it goes awry since stepping through optimized code is a bit of a crap shoot, but I can confirm that the interrupt still works (at least a few times).  We’ve modified the FreeRTOS code to disable interrupts from thumb to only disable fast interrupts, as follows:

void DisableFIQFromThumb( void )
  asm volatile ( "STMDB   SP!, {R0}" );           /* Push R0.                                                                     */
  asm volatile ( "MRS             R0, CPSR" );            /* Get CPSR.                                                            */
  asm volatile ( "ORR             R0, R0, #0x40" );       /* Disable FIQ.                                         */
  asm volatile ( "MSR             CPSR, R0" );            /* Write back modified value.                           */
  asm volatile ( "LDMIA   SP!, {R0}" );           /* Pop R0.                                                                      */
  asm volatile ( "BX              R14" );                         /* Return back to thumb.                                        */

I wonder if anybody has any general information about how FreeRTOS behaves with regard to stack space, etc when a an interrupt handler is declared with the GCC "FIQ" attribute as follows:

void FastTimer_Isr( void ) __attribute__ ((interrupt("FIQ")));

Is this likely to cause any problems on the FreeRTOS side of things at a higher optimization level?  Thanks for any hints!


davedoors wrote on Monday, July 16, 2007:

Sorry to ask an obvious question but have you allocated a stack to the FIQ mode?  The demos in the FreeRTOS download usually don’t bother with this.  Check the startup file.

liamstask wrote on Monday, July 16, 2007:

I have indeed - I have it set to 256 at the moment, and have experimented putting it even as high as 1024.  Thanks for the thought!

jorick23 wrote on Tuesday, July 17, 2007:

I’m using fast interrupts off Timer 2.  My FIQ handler doesn’t do any FreeRTOS calls or task switching so FreeRTOS doesn’t know (and doesn’t care) that fast interrupts are occurring.  I set my stack to be 192 bytes long and I still have 124 bytes free.

You say you’ve modified FreeRTOS to disable FIQ?  Since FreeRTOS doesn’t have any fast interrupt handling, I assume you mean that you’ve changed the places where FreeRTOS disables interrupts and replaced it with your own function that disables only the FIQ (correct me if I’m wrong since I probably don’t have all the information).  Since FreeRTOS requires the IRQ to be disabled while task switching and other critical places, it seems to me you should be disabling the IRQ and leaving FIQ enabled.

I use the ARM compiler so I’m not up to speed on the GCC tools, but I hope this info helps.

liamstask wrote on Tuesday, July 17, 2007:

Thanks for your response, Ricky - I should have been a bit clearer.  I’m only disabling the FIQs when my normal non-interrupt code is modifying variables that the FIQ handler will be trying to get at as well.  I haven’t changed anything about where FreeRTOS turns IRQs on or off.  And, I’m not doing any RTOS or task switching kinds of things in my interrupt handler either. 

I still can’t quite imagine what’s responsible for this working at optimization -O0 and breaking at -O2.  Thanks for your help.

liamstask wrote on Tuesday, July 17, 2007:

Ricky - a couple other questions:

Just to make sure, I assume you’re letting your FIQs interrupt FreeRTOS?

Secondly, I wonder if you have any visibility into the code generated by the IAR compiler for getting in and out of the FIQ handler.  As I originally posted, I’ve been declaring my FIQ handler with the “FIQ” attribute - void FastTimer_Isr( void ) __attribute__ ((interrupt(“FIQ”)));  But FreeRTOS declares all its normal ISR handlers as “naked” and then uses portENTER_SWITCHING_ISR( ) to handle all the business of switching stacks itself. 

I have no idea if if the GCC “FIQ” attribute is doing the right thing, so I’m wondering…how are you declaring yours?  Using the equivalent of the “FIQ” attribute in IAR land or using a modified version of portENTER_SWITCHING_ISR( )? 

Have you built your code at different optimization levels? Seen any differences?  Thanks again!

jorick23 wrote on Wednesday, July 18, 2007:

When that happens to me, I generate a full listing file for the unoptimized code and another listing file for the optimized code, and then compare the two to see what the differences are (make sure you specify C source interleaving).

Perhaps a variable was optimized out because you didn’t put the “volatile” keyword on it?

jorick23 wrote on Wednesday, July 18, 2007:

Yes, the FIQs are highly timing critical and they interrupt FreeRTOS even during critical code sections.

The IRQ handler is completely written by me, even down to the vector.  Although IAR has the ability to define FIQ handlers and generate the proper code to jump to my function, I thought it would be best for me to generate the code and declare the interrupt function as a normal function.  It even runs in Thumb mode.

I don’t use portENTER_SWITCHING_ISR to switch tasks in the FIQ because I wanted the function to be completely transparent to FreeRTOS.  The critical timing nature requires this.  The FIQ function doesn’t call any FreeRTOS functions nor does it touch any FreeRTOS variables.

My FIQ path starts in the file Vectors.s79 where I have the interrupt vector table.  I placed the FIQ function at the FIQ vector so that I wouldn’t waste time with a branch.  Since both Timer 2 and the watchdog can cause a FIQ, there is code to handle both.  The function is as follows:

// The fast interrupt is caused by either a Timer 2 interrupt or the watchdog
// timer ran out.  If the watchdog timer ran out, the processor is reset.

________ldr_____r8, =EIC_FIPR___________// Get the FIQ pending bits
________ldr_____r8, [r8]
________ldr_____r9, =_FIQVectors - 4____// Point to the FIQ vectors
________ldr_____r9, [r9, r8, lsl #2]____// Get the FIQ function address

// The interrupt is handled.

________stmdb   sp!, {r0-r7, lr}________// Save the user environment
________mov_____lr, pc__________________// Set up the return address
________bx      r9______________________// Service the interrupt
________ldmfd   sp!, {r0-r7, lr}________// Restore the user environment

// The EXTIT line pending bit is cleared by writing a 1 to it.

________ldr_____r8, =EXTIT_PR___________// Point to the EXTIT pending register
________mov_____r9, #1
________str_____r9, [r8]________________// Clear the line 0 pending bit

// The FIQ pending bit is cleared by writing a 1 to it.

________ldr_____r8, =EIC_FIPR___________// Point to the FIQ pending register
________str_____r9, [r8]________________// Clear the timer FIQ pending bit
________subs    pc, lr, #4______________// Return from the interrupt

________dcd_____TIM2_OC1_FIQHandler_____// Timer 2: stepper motor handler
________dcd_____MRCC_GenerateSWReset____// Watchdog: generate software reset

The function prototype for my FIQ handler is:

void TIM2_OC1_FIQHandler (void);

Again, this is an ordinary function in a module that’s compiled in Thumb mode.  I could call this function from mainline code with no bad side effects if I wanted to.

My code is always being built at the maximum optimization level even in debug mode.  I’ve had problems with code running when not optimized and not running when optimized.  So to save development time, I ensure that the code that I debug is the same code that goes into the final product.

liamstask wrote on Thursday, July 19, 2007:

Ricky - thanks very much for sharing your handlers.  I was able to piece together something that would work for our app.

Without switching over properly to the FIQ stack space, when using no optimization (-O0) the FIQ handler was still trying to shove things onto R11 even though that’s not available when in FIQ mode.  At -O2 it kept within the available registers and all is well.