Quality RTOS & Embedded Software

 Real time embedded FreeRTOS RSS feed 
Real time embedded FreeRTOS mailing list 
Quick Start Supported MCUs PDF Books Trace Tools Ecosystem TCP & FAT Training




Loading

AVR: Scheduler gets disabled unintentionally

Posted by Felix E. on August 4, 2009
Hi there,

I included FreeRTOS into a little AVR software project (using ATMega328P and preemptive scheduling), I work on and so far it works pretty well. But while implementing some more features (using binary semaphores to sync with an interrupt) I noticed a strange behaviour, which can cause the whole system to lock.(It might be a general issue with that port.)

Imagine the following scenario:

Task A waits for an interrupt event using binary semaphores, it blocks when calling "xSemaphoreTake()" - the context is saved (including a set I-flag because we are not in an ISR or deleted the flag manually), then a switchover to task B is performed and the corresponding context restored. Code execution continues...
Now an Interrupt occurs (the ISR Task A waits for) - the I-flag is deleted (by the CPU) causing all interrupts to be disabled (including the scheduler) and the ISR code is called. Now the context (of task B) is saved (including the deleted I-flag), some operations are performed and "xSemaphoreGiveFromISR()" is called, waking task A. Because it has a higher priority than B, a context switch (to task A) is performed. The context is restored with the I-Flag set (ok so far, because this flag would have been set by the "reti" instruction anyway).
Task A executes and then blocks again waiting for the next Interrupt - the context is saved, the switch to task B is done.
Now the context of task B is restored but with the I-flag being DELETED (because it was saved from within an ISR). Since the return is done using "ret", the flag is not set and the interrupts - including the scheduler - remain disabled!
Task B executes, but has no idea that interrupts got disabled. Unless interrupts are (re)enabled manually, task B executes forever waiting for a scheduler to interrupt it, but this will NEVER happen.

IMHO this could also happen if tasks are switched by the scheduler (in a timer-ISR) AND if one task blocks waiting for an event (maybe occuring in another task or an ISR). Here the contexts - except for the blocked one - will always be saved with the I-flag cleared. If a task is resumed after another task entered the blocked state from outside an ISR, the I-flag will always remain deleted and the scheduler does not execute anymore.

Do I miss an important fact, or is this really a problem in the AVR port (and maybe others)?
I already implemented a solution that works for me, but I really want to be sure if this might be a general issue (which was kind of hard to track down).

kind regards

Felix

RE: AVR: Scheduler gets disabled unintentiona

Posted by Dave on August 5, 2009
I've been looking at this and from what I can see a yield is only ever done from the function vPortYield().

If a task is running and gets interrupted then its stack will hold the interrupt stack frame. If the interrupt calls vPortYield() then the stack will also hold the function call stack frame when the task stops running. When the task next starts running it will complete executing vPortYield() at the end of which there is a ret instruction that returns it to the interrupt function, at the end of which is a reti instruction that returns it to the task function with interrupts enabled.


Looking at your scenario -

> Task A waits for an interrupt event using binary semaphores,
> it blocks when
> calling "xSemaphoreTake()" - the context is saved (including
> a set I-flag because
> we are not in an ISR or deleted the flag manually),


So the saved status register for Task A has interrupts enabled.


> then a
> switchover to task
> B is performed and the corresponding context restored. Code execution
> continues...
> Now an Interrupt occurs (the ISR Task A waits for) - the
> I-flag is deleted (by
> the CPU) causing all interrupts to be disabled (including the
> scheduler) and
> the ISR code is called. Now the context (of task B) is saved
> (including the
> deleted I-flag),


Correct, so when the context of Task B is saved it has the vPortYield() stack frame with interrupts disabled on top of the interrupt stack frame which has interrupts enabled (otherwise the interrupt would not have occurred).


> some operations are performed and
> "xSemaphoreGiveFromISR()"
> is called, waking task A. Because it has a higher priority
> than B, a context
> switch (to task A) is performed. The context is restored with
> the I-Flag set



Yes, when task A continues its status register is restored by the ret instruction at the end of the vPortYield() function that caused it to yield in the first place.



> (ok so far, because this flag would have been set by the
> "reti" instruction
> anyway).


I don't think this statement is correct. Task A continues with its original non interrupt context. There is not reti instruction executed because Task A was not in an interrupt when it stopped executing. It continues executing from the point it last executed which was inside a call to vPortYield() made from a task, not an interrupt.



> Task A executes and then blocks again waiting for the next
> Interrupt - the context
> is saved, the switch to task B is done.
> Now the context of task B is restored but with the I-flag
> being DELETED (because
> it was saved from within an ISR).


Yes.


> Since the return is done
> using "ret", the
> flag is not set and the interrupts - including the scheduler
> - remain disabled!


Again, I think that is incorrect. Task B continues from where it was last executing which was in vPortYield(). When it completes vPortYield() the ret instruction takes it back to the interrupt context where it then executes a reti instruction and returns to its task function with interrupts enabled.


This is just my interpretation of the code, but if what you were proposing was true I think the demo app would fail immediately after the idle task was interrupted by a tick interrupt.

RE: AVR: Scheduler gets disabled unintentiona

Posted by Felix E. on August 5, 2009
After reading your comments and going through the code again, I have
to agree with you...
With the common scheduler ISR the problem does not occur because the
return address saved is not the return address within the task, but the
return address to the "reti" statement within the scheduler ISR.
So the flag is deleted but restored within the next clock cycles
in a correct manner.
That was the little detail I missed so far.

BUT:
In my case I did not want to save the stack frame twice (first after
entering the ISR and then again after task A has been woken up).
So I saved the context, containing the return address to the task
(task B in the scenario) at the beginning of the ISR. Then eventually
performed a context switch after all things had been done in the ISR.
Since the return address does not point to the "reti" instruction at
the end of the ISR (as it is the case for the scheduler), the I-flag might
not be restored in a correct manner - if task B is resumed from outside
an ISR (e.g. after task A blocks again).

I think a solution (for me) without the need to change something in
the RTOS files would be to include a second return address (to the
'reti' command at the end of the ISR) before saving the context.
This is ugly, but would only need two more bytes, which is much better
than 33 bytes.

Thanks for the hint!

regards

Felix


[ Back to the top ]    [ About FreeRTOS ]    [ Sitemap ]    [ ]




Copyright (C) 2004-2010 Richard Barry. Copyright (C) 2010-2016 Real Time Engineers Ltd.
Any and all data, files, source code, html content and documentation included in the FreeRTOSTM distribution or available on this site are the exclusive property of Real Time Engineers Ltd.. See the files license.txt (included in the distribution) and this copyright notice for more information. FreeRTOSTM and FreeRTOS.orgTM are trade marks of Real Time Engineers Ltd.

Latest News:

FreeRTOS V9.0.0 is now available for download.


Free TCP/IP and file system demos for the RTOS


Sponsored Links

⇓ Now With No Code Size Limit! ⇓
⇑ Free Download Without Registering ⇑


FreeRTOS Partners

ARM Connected RTOS partner for all ARM microcontroller cores

Renesas Electronics Gold Alliance RTOS Partner.jpg

Microchip Premier RTOS Partner

RTOS partner of NXP for all NXP ARM microcontrollers

Atmel RTOS partner supporting ARM Cortex-M3 and AVR32 microcontrollers

STMicro RTOS partner supporting ARM7, ARM Cortex-M3, ARM Cortex-M4 and ARM Cortex-M0

Xilinx Microblaze and Zynq partner

Silicon Labs low power RTOS partner

Altera RTOS partner for Nios II and Cortex-A9 SoC

Freescale Alliance RTOS Member supporting ARM and ColdFire microcontrollers

Infineon ARM Cortex-M microcontrollers

Texas Instruments MCU Developer Network RTOS partner for ARM and MSP430 microcontrollers

Cypress RTOS partner supporting ARM Cortex-M3

Fujitsu RTOS partner supporting ARM Cortex-M3 and FM3

Microsemi (previously Actel) RTOS partner supporting ARM Cortex-M3

Atollic Partner

IAR Partner

Keil ARM Partner

Embedded Artists