Suggestion for AT91SAM7 port: enable FIQ

incrediball wrote on Sunday, April 13, 2008:

I’m writing this because of my experiences last week trying to write a high speed interrupt for implementing an I²C slave receiver in firmware. I could not make use of the TWI peripheral since this was being used for something else and when I read the Atmel documentation I’m actually not even sure it supports slave mode anyway. Due to the baud rates involved, the interrupt had to be called and complete execution within microseconds. I realise that this is not an everyday situation but I have had to implement high speed interrupts in the past for playing back PCM, where any significant interrupt latencies would have introduced distortion in the sounds. So, I am sure that the need for fast interrupts is not too insignificant.

I implemented the necessary interrupt handler in ARM mode in RAM and although its execution time was acceptable, the interrupt latency often wasn’t and events were being missed. This was due to two reasons:

1. Nested interrupts and interrupt priorities are not supported under FreeRTOS.

2. All interrupts are routinely disabled for comparably long periods under FreeRTOS.

I was initially surprised when I first stumbled over the first problem. I mean, the AT91SAM7 provides us with all this great 8 priority support and then it’s not used. The portSAVE_CONTEXT macro, which is used by all interrupt handlers under FreeRTOS, leaves the processor in interrupt mode with interrupts disabled so any nesting of normal interrupts is currently not possible. The reasoning is explained under although I do not agree entirely with the explanation since one of the arguments is based on the idea that the programmer may want to use priorities for implementing slow interrupts and not for implementing interrupts that are faster than FreeRTOS’s own tick interrupt.

I managed to get around this limitation using the FIQ and the AIC’s fast forcing feature. The FIQ can pre-empt regular interrupts because only the I bit is set in CPSR when an IRQ is raised. The F bit is only set as well if an FIQ is raised. Some changes are needed in the FreeRTOS code (which is the reason why I am writing this):

a. A stack for the FIQ interrupt must be defined. This is done by changing the FIQ_STACK_SIZE parameter in boot.s to a useful size.

b. At the end of boot.s the exceptions are handled. Currently the FIQ vector contains "ldr   pc, _fiq", which just executes an endless loop. This must be changed to "ldr   pc, [pc,#-0xF20]", which is the same instruction as for the IRQ but because the PC is 4 bytes further along, it actually loads the vector for the fast interrupt.

The interrupt handler is as usual has the naked attribute and compiled in ARM mode. The entry code is very simple:

asm volatile ("stmfd   sp!, {r0-r7, lr}");    // save non-banked registers and lr

The exit code is also very simple:

asm volatile ("ldmia   sp!, {r0-r7, lr}  \n\t"   // restore non-banked registers and lr
   "subs   pc,lr,#4");   // restore pc (return from interrupt)

Note that the interrupt handler can obviously not interact with FreeRTOS. It wouldn’t make sense for it to do so, since it would only slow itself down, if it were to cause task switches and the like. The interrupt is executing outside of the “realm” of FreeRTOS and if it needs to do things such as signalling task switches or queuing data, other mechanisms must be used. For example, my I²C interrupt triggered another (normal) interrupt (using AT91F_AIC_Trig) when it had finished receiving some data. That interrupt then processed the raw data and queued it for a task to further process it.

So, this fixed the first problem but there was still often an unacceptable latency starting the interrupt. Despite the fact that FIQs were obviously never used under this FreeRTOS port (FIQs just caused everything to lock up if they did occur), the FIQ interrupt was being disabled by portDISABLE_INTERRUPTS, vPortDisableInterruptsFromThumb and vPortEnterCritical. FreeRTOS disables interrupts in order to protect certain data structures from changes made during an interrupt. However, if an interrupt does not interact with FreeRTOS at all and assuming that this is always the case, there is no reason why such an interrupt needs to be disabled. Especially if the interrupt is fast and there are no other side effects, it does not need to be disabled. So, this is the third change to the FreeRTOS code: the 0xC0 constants (I and F bits) in the above mentioned 3 macros/functions need changing to 0x80.

Once these changes were made, my I²C slave receiver interrupt finally worked fine, and without destabilizing FreeRTOS.

davedoors wrote on Wednesday, April 16, 2008:

Thanks for taking the time to share this.  I think it has been touched on a few times in the past - searching for FIQ might find the posts.  This is one of the areas where the Cortex M3 shows itself as a much better architecture than the ARM7.

fepmj wrote on Friday, November 13, 2009:

i have exactly the same problem with the FIQ and Free RTOS, using a AT91SAM7X512 and Rowley Crossstudio.

The FIQ stack size is set in Rowley to 512 bytes, i created a fiq_handler (still without functionality for testing), including your asm directives like this:

        void fiq_handler (void)
    asm volatile (“stmfd sp!, {r0-r7, lr}”); // save non-banked registers and lr
    AT91C_BASE_AIC->AIC_ICCR = 1; // Clear Interrupt Source
        AT91C_BASE_AIC->AIC_EOICR = 0; // End of interrupt
    asm volatile (“ldmia sp!, {r0-r7, lr} \n\t” // restore non-banked registers and lr
    “subs pc,lr,#4”); // restore pc (return from interrupt)

But when i activate this interrupt, the RTOS runs into the undef_handler, not starting any task.

I’m trapped with this issue at the moment, not knowing why this happens.

Background is the worse implementation of the hardware TWI of the Atmel AT91SAM7X. It automatically generates a stop after the TWI shift register is transferred with no new data written into TWI_THR. Fast TWi rates higher than 100kHz are not possible with this, because of the interrupt lengths using FREE RTOS. My intention is here to make the TWI transfers in FIQ interrupt … but for this, i have to solve the issue of the FIQ usage.

Any ideas are highly appreciated.