trap instruction for taskYIELD

rbornet wrote on Monday, June 14, 2010:

Hi FreeRTOS board!

I’m trying to understand in details how FreeRTOS works internally to port it to a new architecture (details in a previous thread: https://sourceforge.net/projects/freertos/forums/forum/382005/topic/3738228).

On this architecture I have following possible exceptions/interruptions (simplified subset for readability):

- 8 IRQs (1 high-prio/non maskable and 7 prioritized)
- 1 software trap exception with higher priority than above mentioned IRQs
- 1 software trap exception with lower priority than above mentioned IRQs

I intended to implement portYield as a software trap exception/interrupt and use one of the 7 level-programmable IRQs for my timer/tick interrupt.

In a thread dedicated to ColdFire porting ( https://sourceforge.net/projects/freertos/forums/forum/382005/topic/3443802 ) I read that trap should not be used for yield since they are synchronous. I don’t really understand why this is a problem ?

Unfortunately I cannot manually generate “normal” interrupts (as with INT_FORCE on ColdFire) and only basically have the above mentioned mechanisms at disposal (2 traps and 8 IRQs). If I understand correctly both YIELD and interrupts must leave the stack in compatible states. I therefore do not really see how I can achieve this with my configuration mentioned above. Any hint? Does this sound similar to any other supported architecture which I could take as example ? I went through the different implementations and didn’t find any  with a similar infrastructure, perhaps I missed one… What is the best approach to adopt in such a situation?

Thanks in advance for any hint/help in this matter !

rtel wrote on Monday, June 14, 2010:

It is difficult to offer an opinion without knowing a few more details about the architecture and your intentions.  For a starts:

1) Do IRQs and software traps generate identical stack frames?
2) Can an interrupt and a software trap be exited from using the same instruction?
3) Are you wanting to support interrupt nesting?

Regards.

rbornet wrote on Tuesday, June 15, 2010:

Hi Richard

Thanks for your prompt answer and assistance. I know that the information I provided is rather sparse but unfortunately I cannot give much details for moment since the chip documentation has not been publicly disclosed for now.

Your questions already gave me some ideas on where to look at and where are the critical points.

> 1) Do IRQs and software traps generate identical stack frames?
Yes. Software traps generate identical stack frames as IRQs (similar to ColdFire’s trap instructions generating identical stack frames as external IRQs). What mainly differs on my architecture is that exceptions/IRQs stack frames are pushed on a separate on-chip hardware stack and not atop the software stack pointed by the SP. This mechanism is useful and very efficient for bare-metal/OS-less applications but requires some additional handling when dealing with OS implementations especially context switches.

> 2) Can an interrupt and a software trap be exited from using the same instruction?
Yes both are exited using the same “return from exception” instruction. This is one of the reasons why I intended ti implement the yield functionality with a software exception/trap. Since a task can relinquish CPU ownership cooperatively (yield,delay) or preemptively, both mechanisms must be compatible one with another and leave the running state with a compatible stack layout allowing a future “switch in” by any method (cooperative or preemptive). Am I alright until here ?

> 3) Are you wanting to support interrupt nesting?
Interrupt nesting would be nice but I fear that this would not be (easily) possible with the described infrastructure. Since software exceptions/traps are unmaskable and either have a higher or lower priority than *all* IRQs, I’m not sure if nesting could be supported. Interrupt nesting would also imply rather complicated handling of the separate hardware (interrupt) stack. In a first try I will probably not support interrupt nesting.

For now I can imagine following implementation:
* portYIELD implemented by a software exception/trap
* timer interrupt with one of the 7 maskable IRQs level
* separate (but similar) handlers for timer ints and sw traps: this is as far as I understand what is implemented in ARM7 ports with SWI (GCC/ARM7_xxx/portISR.c) and in the deprecated GCC/MCF5235 port where portYIELD was implemented with a “trap” assembler instruction.

Do you see any “no-go” with this solution or any caveats I would not have taken into consideration?

I will start with this “roadmap” and keep you posted on my progress.

Thanks for your “professional-grade” open source support!

Regards

    Romain

davedoors wrote on Tuesday, June 15, 2010:

What mainly differs on my architecture is that exceptions/IRQs stack frames are pushed on a separate on-chip hardware stack

Sounds like the PIC18. That copies the hardware stack to the software stack (and back again) in the context switch. Not efficient.

rbornet wrote on Tuesday, June 15, 2010:

Hi davedoors

Thanks for the hint to PIC18. I didn’t know that this arch had such a hardware stack. I’ll take a deeper look at it.

That copies the hardware stack to the software stack (and back again) in the context switch. Not efficient.

I know that this is not efficient but no ways to do this differently. This is given by the architecture and is not configurable. This is even worse than just “in the context switch”. It would be in every IRQ entry *possibly* causing a context switch (everywhere portSAVE_CONTEXT() is called).

I will have to consider all these aspects and try to find the best suited implementation to handle all possible cases but I fear that I will have to live with “dumps/restores” of the HW stack.

rtel wrote on Tuesday, June 15, 2010:

> 2) Can an interrupt and a software trap be exited from using the same instruction?
Yes both are exited using the same “return from exception” instruction. This is one of the reasons why I intended ti implement the yield functionality with a software exception/trap. Since a task can relinquish CPU ownership cooperatively (yield,delay) or preemptively, both mechanisms must be compatible one with another and leave the running state with a compatible stack layout allowing a future “switch in” by any method (cooperative or preemptive). Am I alright until here ?

This sounds right to me, although each architecture has its own eccentricities so I can’t say for sure.

It sounds like using a trap is a workable solution.  Normally traps are synchronous though, so will be actioned immediately even when inside a critical section.  There are some places in the FreeRTOS code where this will (intentionally) happen, but it means you have to restore the interrupt mask state as part of the task context.  Attempting to put that simply, a task can have interrupts disabled (either to a certain level or completely) when it yields and yield to a task that has interrupts enabled, when the original task executes again the interrupt mask must be exactly as it was when it yielded.

Yielding in a critical section is not a good idea for application code, by the way.

> 3) Are you wanting to support interrupt nesting?
Interrupt nesting would be nice but I fear that this would not be (easily) possible with the described infrastructure. Since software exceptions/traps are unmaskable and either have a higher or lower priority than *all* IRQs, I’m not sure if nesting could be supported. Interrupt nesting would also imply rather complicated handling of the separate hardware (interrupt) stack. In a first try I will probably not support interrupt nesting.

Without knowing your architecture fully I could not comment absolutely, but what you say sounds sensible.  I would always try to get a port running without nesting first in any case.

For now I can imagine following implementation:
* portYIELD implemented by a software exception/trap
* timer interrupt with one of the 7 maskable IRQs level
* separate (but similar) handlers for timer ints and sw traps: this is as far as I understand what is implemented in ARM7 ports with SWI (GCC/ARM7_xxx/portISR.c) and in the deprecated GCC/MCF5235 port where portYIELD was implemented with a “trap” assembler instruction.

I would say that sounded like a good starting point.  Give it a try and see how you get on.  The ARM7 interrupt handlers save the task context as part of their prologue, and restore it as part of their epilogue.  This means you have to defined the ISR functions as naked, and provide your own entry and exit code.  This is not as hard as it sounds!

Regards.