Custom RTOS - general ideas veryfication

Greetings Community !

 

Im currently working on my custom real time kernel project (STM32F7). It was really in mature stage of developement, when i figured that its not compleately ISR safe, gets stucked sometimes, corrupts some data etc. Im rebuilding it and right now and need some clarifycation about certain things which I could made wrong back then. Lets start !

 

Question 1: Is my interrupt flow ok ?

- Im currently using 3 interrupts: SysTick (cyclical maintenance, scheduling), PendSV (task yielding, context saving/switching), SVCall (starting first task)

 

Question 2: Should SysTick and PendSV have equal and lowest priority ?

 

Question 3: Is it ok to yield task just by pending SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk  ?

- PendSV is an interrupt so it does not call its handler immediately. Does only pending this bit ensures that the code wouldn't go any further and reach invalid data? (i.e when task is waiting for semaphore). When in debug mode it looks like code goes beyond this point and unprepared data could be reached (but maybe its only debugger prespective). freeRTOS adds __DSB() + __ISB(), but they are commenting it to be unnecessary.

 

Question 4: Kernel is using global pointer to actual task. Is it a good practice? Should it be volatile pointer or not?

- This methodology was forced by asm functions. Global variable can be referenced there.

 

Question 5: Is my context save/switch code is safe ? Could it be optimised ?

 

__attribute__((naked))  static void  port_context_save()

{

    asm(

        "cpsid         i                                               \n" // disable all interrupts

        "mrs           r12,        psp                            \n" // load psp to r12

        "stmdb       r12!,        {r4 - r11}                   \n" // store r4-r11 registers

        "ldr             r0,        =    _current_task        \n" // load pointer to pointer to current tcb

        "ldr             r0,            [r0]                           \n" // load pointer to current tcb

        "str             r12,        [r0,#16]                      \n" // store r12 (new top of stack) to adequate memory location (sp variable is 4 words from top of tcb)

        "cpsie        i                                                \n" // enable all interrupts

        "isb                                                             \n" // isb

        "dsb                                                            \n" // dsb

        "bx              lr                                              \n" // branch back

    );

}

 

__attribute__((naked, noreturn)) static void port_context_switch()

{

    asm(

        "cpsid        i                                                \n" // disable all interrupts

        "ldr             r0,        =    _current_task         \n" // load pointer to pointer to current tcb

        "ldr             r0,            [r0]                            \n" // load pointer to current tcb

        "ldr             r0,            [r0,#16]                     \n" // load SP value (sp variable is 4 words from top of tcb)

        "ldmia        r0!,        { r4 - r11 }                    \n" // load r4-r11 registers

        "msr           psp,        r0                               \n" // update psp

        "movs         lr,            #0xFFFFFFFD         \n" // prepare branch code

        "cpsie         i                                                \n" // enable all interrupts

        "isb                                                             \n" // isb

        "dsb                                                            \n" // dsb

        "bx            lr                                                \n" // branch back to thread

    );

}

 

Question 6: Should scheduling be marked as critical section ?

- Disable all interrupts or maybe just rise BASEPRI ?

 

Question 7: User code modyfing kernel objects (i.e semaphore signaled from user code -> semaphore waiting list must be flushed to kernel ready list). Is it a really bad practice ?

- That code should be also marked as critical section. Any other way to tackle this problem?

 

Question 8: How to verify correctness of RTOS working in ISR heavy enviroment ?

- Currently im using agressive timer update interrupt alongside with RTOS working. Is it sufficient ?

 

Uff ! Thats all ! If any of you have some idea on any of this do not hesistate to help !!

 

Thanks in advance

  • Hi there,
    Sorry for the delay (usually I only check the "processor" place).
    1. I would expect task yielding to be an SVC service.
    Otherwise, if the yielding request happens right after a context switch, the SysTick haven't been triggered (scheduler hasn't run) and the pendSV will not switch to a different task.
    2. PendSV should have lowest priority, but SysTick can be higher.
    3. As mentioned in 1, I expected yield should be an SVC service.
    4. I can't answer unless I see the code.
    5. You code doesn't cover context switch of floating point registers.
    I have an example of context switching code for Cortex-M4, which should also work on Cortex-M7.
    booksite.elsevier.com/.../ (see examples for chapter 10)
    booksite.elsevier.com/.../Book.zip
    I don't think you need to disable all interrupts for the context switch.
    When you are in the context switch code you are in handler (using MSP),
    and the changes you are making is for PSP only, and if there is any
    higher priority interrupt coming in that would also use MSP.
    So it would work without disabling interrupt.
    I don't have any answer for 6, 7 and 8. These are sort of application dependent.
    For 8, one of the things to check is that if the context switch has been delayed for very long time due to IRQ request, and execute just before SysTick is triggered, the task scheduler might need to skip the task scheduling for that tick to let the context switching finished. 
    regards,
    Joseph