taskYIELD in interrupt
Hi,
I need help in understanding how FreeRTOS handles interrupts.
I got a ISR wich sends in a queue with cQueueSendFromISR.
As I need very fast reaction I put a taskYield at the end of the interrupt (if cQueueSendFromISR returns true).
The software is unstable and I suspect the ISR for the hangups.
The compiler puts some push at the beginning of the ISR and some pop at the end of it.
Calling taskyield prevents the pops to be executed, right?
taskYield is doing some push and pops itself but the pops of the ISR remain on the stack?
Ok, I think i missed something….
I still use 2.60, will try 3.0 asap.
taskYIELD in interrupt
The serial port driver of each demo application provides an example of how this can be done. Generally the switch has to be right at the end of the ISR.
How this mechanism works is very dependent on the processor, and to a lesser extend the compiler. Which are you using?
taskYIELD in interrupt
Yes, I do it like in the serial demo.
taskYIELD (switch?) is at the end of the ISR.
In the assembler listing I can see after a call to vPortYield only the pops of the ISR.
I use gcc 3.4.3 (latest WinAVR) with Atmel ATMega128.
The tick is every 10 ms.
I’m sorry, but regarding earlyer posts here I thought that PortYield activates the woken task directly? Or what happens after the RET command you put at the end of PortYield?
taskYIELD in interrupt
What I noticed when writing the wizC port for the PIC18 was:
When you declare the ISR to use local variables, space is reserved by
compiler-generated-code on the stack upon ISR-entry. Then the context is
saved ( portSave_Context ). At the end the context is restored (
portResore_Context ) which results in a change of the programcounter. The
compiler-generated-code to restore the space used by the local variables is
never executed. This rapidly leads to stackoverflows.
What I did to prevent this is to forbid (in text) the use of local variables
in the ISR. The alternatives I came up with are:
- Call a function from the ISR (after the contextsave) and declare the
locals there.
- Declare the needed variables as global ( iow declare them outside of the
ISR, or as static within the IST)
- Find some (currently) unused globals or registers you can use. These are
the ones that got saved at a contextsave, but are not used by your ISR. This
only works if you always do a contextrestore upon isr-exit.
And as Richard said: All CPU’s/compilers are different here. But the above
might help you.
Marcel
taskYIELD in interrupt
In the GCC AVR port the Yield macro just calls a naked function. The function saves the context, switches the active task to the one with the highest priority, then restores the context of the (possible) new task. The ISR then completes in the normal way. Because the context has been switched, when the ISR completes it pops registers from the newly activated task and returns to the newly activated task.
This will only work if the yield is the last part of the ISR routine, so the stack is not used between the yield and the ISR completing.
Provided the example of the serial port ISR routine is followed and the Yield only comes at the end of the ISR, then your code should work. The most likely cause of any problem would be stack overrun. Switching context from within the ISR will use more stack than the ISR would otherwise. Can you check the amount of stack available? Try increasing the stack of the relevant tasks – this might fix the problem.
Regards.
taskYIELD in interrupt
Hi Marcel,
The GCC AVR port allows you to use local variables in the ISR because the __attribute__((naked)) attribute is not used on the ISR. This means the function prologue and epilogue code will get executed.
If a task is interrupted by and ISR, the prologue code moves the stack pointer to make room for the local variables. The task can then be switched out with the stack in tact. When it (sometime later) gets switched back it continues from the same point – so the function epilogue code executes setting the stack back to the correct value before returning to the task code.
This is all interesting stuff and where each system differs. I use so many different CPU’s that I always have to remind myself how each works before answering questions like these.
Regards.
taskYIELD in interrupt
Hello,
I (again) found out that the error was on my side ;-)
There was some buffer pointer problem in my ISR.
Debugging such a deeply embedded software is not easy, even more when you add an alien RTOS wich you dont’t understand in every way.
So I learned something (I hope):
- the naked attribute leaves out the RET instruction, so
taskYIELD returns to the ISR
- the FreeRTOS task switch is done with the pxCurrentTCB pointer and the following register restore with the ‘new’ stack pointer
But Richard said:
‘ Because the context has been switched, when the ISR completes it pops registers from the newly activated task and returns to the newly activated task.’
What does the ISR pop? It pushed the registers on the old stack an pops them from the new stack?
So its a kind of don’t care?
What’s with the new stack pointer when changing it with
unexpected pops?
Joerg
taskYIELD in interrupt
The stack contains the return address. So changing the stack allows the pops to return to a different return address within a different task.
Have you seen this part? /wp-content/uploads/2019/07/index.html
it is explained.
taskYIELD in interrupt
Hi,
> Have you seen this part?
> /wp-content/uploads/2019/07/index.html
> it is explained.
yes I’ve seen it. But the the case of calling taskYIELD out of my own ISR is not exlpained there?
I think I was wrong in my last post. taskYIELD does not return to my ISR, because of the new program counter put on the stack by RestoreContext. Right?
But taskYIELD uses a RET instruction not the RETI
needed here?
So the pops of the compiler ISR code doesn’t get executed.
And when the first task comes to execution again it crashes because the the expected program counter is not there but the pushes of the interrupt code?
Maybe Richard can clear this point?
I think this is the right place to ask?
Regards, Joerg
taskYIELD in interrupt
There are two key points here:
1) The Yield appears only at the end of the interrupt service routine – when the interrupt has been serviced but the ISR has not completed.
2) Each task maintains its own interrupt status.
This is the sequence:
1) Task A is running when an interrupt occurs. The interrupt causes some CPU registers to be saved including the interrupt flags. Registers are saved so as to be restored by a RETI instruction. For the interrupt to have occurred the interrupt flags must have been set with interrupts enabled – but within the ISR they are disabled.
2) The ISR code executes. During the execution it is discovered that a yield is required. The yield does not occur however – just a flag is set to say one is necessary.
3) At the end of the interrupt the flag is checked which says a yield is required, and yield is called.
4) Yield saves the context of the task A – which has been interrupted. The registers are saved to the stack on top of the registers that were already pushed onto the stack when the ISR was entered. The program counter that is saved points to the end of the ISR function.
5) Yield then switches the stack pointer to point to the saved context of task B – and proceeds to pop the registers from task B context. When all the general purpose registers have been popped the task B interrupt flags and return address are left on the stack. The flags are popped first. This will return the interrupt enable status to whatever it was when task B last ran (could be enabled or disabled). Finally RET is executed which causes the return address to be popped and the program counter set to the correct place to run task B.
6) At some point task B blocks. In our example assume this makes task A the candidate to run again. Having saved the task B context the context switch this time sets the stack pointer back to the saved context of task A – the task that was interrupted.
7) The general purpose registers for task A are restored. Next the interrupt flags are restored. The interrupt flags were stored with interrupts disabled as they were saved from within the ISR. Finally the RET instruction is executed which sets the program counter back to the place where task A was last executing. In this case task A was last executing in the ISR. The next items on the stack are the registers pushed onto the stack by the processor when the interrupt occurred. [NB there may actually be some registers on the stack from the function prologue generated by the compiler, which will be popped by the epilogue generated by the compiler].
8) Task A returns to within the ISR. The ISR processing has already been performed but the ISR itself has not finished. The next instruction that task A executes is from the function epilogue – in a RETI. This pops the interrupt flags and return address from the stack – these are those placed there when the ISR was entered. The interrupt flags re-enables interrupts (remember they must have been enabled for the interrupt to get called at all) and returns task A to the place it was prior to it getting interrupt in the first place.
Hope this helps!
Regards.
taskYIELD in interrupt
Yes, thank very you much, it helps.
I couldn’t imagine that task A returns back into the end of the ISR.
I got a bad headache but I think I understand it now.
taskYIELD in interrupt
Hmm, was it planned this way? :-)
Isn’t it easier and clearer to let XFromISR take care for
pending task switches?
Maybe they could set the interrupt flag for the tick function so it executes immediately afterwards?
It shouldn’t increase the tick count then of course.
What is the advantage of the current solution?
taskYIELD in interrupt
I’m not sure which answer this is refering to. Can you either quote the text, or reply to the message to which your are referring, rather than at the top level.
Thanks.
taskYIELD in interrupt
…,or reply to the message to which your are referring, rather than at the top level.
Aha, this is possible?
Sorry, I was referring to the longer explanation,
where it says that the interrupt is completed
not until switching back to task A.
I wonder if the XFromInterrupt functions could initiate (not execute) a task switch by themselves rather than let the programmer coding it manually at the end of the ISR.
Regards,
taskYIELD in interrupt
> Hmm, was it planned this way? :-)
The mechanism for achieving this functionality is very architecture dependent. The AVR port does not utilise a software interrupts to perform context switches so this is the best way of achieving the context switch from within an ISR (IMHO). So yes it was done this way by design but only for the AVR and similar architectures.
>Isn’t it easier and clearer to let XFromISR take care for
>pending task switches?
These functions and most probably do occur within an ISR not just at the end so they cannot perform the switch themselves. Doing so would mean that the stacks would get messed up and the interrupt might not be cleared prior to the switch occurring. They do take care of the switch in as much as they set a flag to say whether a switch is required or not.
> Maybe they could set the interrupt flag for the tick function so it executes immediately afterwards?
Allowing the end of interrupt instruction to execute would take you back to the (wrong) task code and a switch would not occur to the correct task until the next RTOS tick. Setting an interrupt flag to force an RTOS switch immediately following the return from interrupt would mean the real time (tick count) would no longer be real time. As you say the count could be prevented from incrementing but this would then include a coding overhead. The call to yield at the end of the ISR is effectively doing exactly as you suggest but the application writer has to add it manually if they want the switch to occur immediately.
> What is the advantage of the current solution?
As above. I dont know of a viable alternative. Other architectures that use an interrupt to force a task switch allow the interrupt to complete. The return from interrupt instruction inserted by the compiler causes an automatic return to the new task.
Regards.