vTaskEndScheduler not working as stated

Hi there,
I’m using FreeRTOS on a project of my own with a custom board and an AT90USB647 uController. At some point I need to jump to the USB boot loader code and before I do it I stop the scheduler with
vTaskEndScheduler
function.
Problem is:
- in the documentation it is stated that execution will return to the instruction after the call to
vTaskStartScheduler
just as if it had returned (see http://www.freertos.org/a00133.html). The code does not behave that way. After some time trying to make it work I checked the source code and
vTaskEndScheduler
has only 3 lines of code that I pasted below.
void vTaskEndScheduler( void )
{
    /* Stop the scheduler interrupts and call the portable scheduler end
    routine so the original ISRs can be restored if necessary.  The port
    layer must ensure interrupts enable bit is left in the correct state. */
    portDISABLE_INTERRUPTS();
    xSchedulerRunning = pdFALSE;
    vPortEndScheduler();
}
this means that unless somehow i make code on
vPortEndScheduler
to return to the instruction after
vTaskStartScheduler
the return will be to the instruction after
vTaskEndScheduler
. Can someone please tell me how to make it return to the statement after
vTaskStartScheduler
??
Or does the documentation need to be changed?
Anyway, this is a either a bug or an unimplemented feature on FreeRTOS isn’t it? Regards,
Bernardo Marques.

vTaskEndScheduler not working as stated

This is the same question but readable, i did not now that the ‘code’ tag would do that and i do not know how to edit the post. Hi there,
I’m using FreeRTOS on a project of my own with a custom board and an AT90USB647 uController. At some point I need to jump to the USB boot loader code and before I do it I stop the scheduler with vTaskEndScheduler function.
Problem is:
- in the documentation it is stated that execution will return to the instruction after the call to vTaskStartScheduler just as if it had returned (see http://www.freertos.org/a00133.html).
- The code does not behave that way. After some time trying to make it work I checked the source code and vTaskEndScheduler has only 3 lines of code that I pasted below.
void vTaskEndScheduler( void )
{
    /* Stop the scheduler interrupts and call the portable scheduler end
    routine so the original ISRs can be restored if necessary.  The port
    layer must ensure interrupts enable bit is left in the correct state. */
    portDISABLE_INTERRUPTS();
    xSchedulerRunning = pdFALSE;
    vPortEndScheduler();
}
this means that unless somehow i make code on vPortEndScheduler to return to the instruction after vTaskStartScheduler the return will be to the instruction after vTaskEndScheduler. Can someone please tell me how to make it return to the statement after vTaskStartScheduler??
Or does the documentation need to be changed?
Anyway, this is a either a bug or an unimplemented feature on FreeRTOS isn’t it? Regards,
Bernardo Marques.

vTaskEndScheduler not working as stated

I think that function is only implemented for the PC port, where ending the scheduler allows you to return to DOS.  The implementation uses setjmp(), take a look at the OpenWatcom PC port implementation of the function. Regards.

vTaskEndScheduler not working as stated

Shouldn’t the documentation be changed to say that the function returns as expected unless using the PC port where it’ll return to the instruction after vTaskStartScheduler? Thanks for your help Richard. Regards,
Bernardo Marques.

vTaskEndScheduler not working as stated

I fallen into the same trap. Agree. Documentation should be corrected (it’s still unclear).

vTaskEndScheduler not working as stated

Rather than putting in the documentation differing behavior baed on the port, it might be better to document that this function is only fully implemented on the PC port, thus opening the door to implementing it on other ports later if desired without “breaking” the documentation. I would also think a different “default implementation” for the ports which don’t really implement the function, maybe vPortEndScheduler() should do something like a while(1); stall loop which is at least closer to documented behavior and puts up a nice big flag when debugging that makes it clear what wasn’t working.

vTaskEndScheduler not working as stated

I updated the online documentation page to say it was only implemented on the PC port a couple of hours ago – when the last page came in.  The reference manual already has the text “At the time of writing, vTaskEndScheduler() is only implemented for the real mode x86 OpenRTOS port.”. Regards.

vTaskEndScheduler not working as stated

Thank you, Richard.
I’ve implemented this behavior for GCC ARM Cortex-M3 MPU port. Following is diff applied to original FreeRTOS V7.1.0 port:
diff --git a/FreeRTOSV7.1.0/Source/portable/GCC/ARM_CM3_MPU/port.c b/FreeRTOSV7.1.0/Source/portable/GCC/ARM_CM3_MPU/port.c
--- a/FreeRTOSV7.1.0/Source/portable/GCC/ARM_CM3_MPU/port.c
+++ b/FreeRTOSV7.1.0/Source/portable/GCC/ARM_CM3_MPU/port.c
@@ -316,8 +316,11 @@ static void prvRestoreContextOfFirstTask( void )
 /*
  * See header file for description.
  */
+volatile char sh_stop_request = 0; // indicates whether xPortStartScheduler() called from vPortEndScheduler() to stop sheduler
+volatile unsigned long sh_context[4]; // stores context of vTaskEndScheduler() call to restore it when sheduler being requested to stop
 portBASE_TYPE xPortStartScheduler( void )
 {
+   if (!sh_stop_request) {
    /* Make PendSV and SysTick the same priroity as the kernel. */
    *(portNVIC_SYSPRI2) |= portNVIC_PENDSV_PRI;
    *(portNVIC_SYSPRI2) |= portNVIC_SYSTICK_PRI;
@@ -332,19 +335,49 @@ portBASE_TYPE xPortStartScheduler( void )
    /* Initialise the critical nesting count ready for the first task. */
    uxCriticalNesting = 0;

+   /* Save context to return from vTaskEndScheduler() */
+   __asm volatile
+   (
+           "   push {r0-r12,lr}            n"
+           "   mrs %[basepri_val], basepri n"
+           "   mrs %[control_val], control n"
+           "   mrs %[psp_val], psp         n"
+           "   mrs %[msp_val], msp         n"
+           : [basepri_val]"=r" (sh_context[0]), [psp_val]"=r" (sh_context[1]), [msp_val]"=r" (sh_context[2]), [control_val]"=r" (sh_context[3])
+   );
+
    /* Start the first task. */
    __asm volatile( "   svc %0          n"
                    :: "i" (portSVC_START_SCHEDULER) );
+   } else {
+       sh_stop_request = 0;
+
+       /* Cancel most important system changes made when sheduler started */
+       *(portNVIC_SYSTICK_CTRL) &= ~(portNVIC_SYSTICK_CLK | portNVIC_SYSTICK_INT | portNVIC_SYSTICK_ENABLE);
+       *portMPU_CTRL &= ~portMPU_ENABLE;
+       /* Restore context to return back to vTaskStartScheduler() */
+       __asm volatile
+       (
+               "   msr basepri, %[basepri_val] n"
+               "   msr control, %[control_val] n"
+               "   msr psp, %[psp_val]         n"
+               "   msr msp, %[msp_val]         n"
+               "   isb                         n"
+               "   pop {r0-r12,lr}             n"
+               :: [basepri_val]"r" (sh_context[0]), [psp_val]"r" (sh_context[1]), [msp_val]"r" (sh_context[2]), [control_val]"r" (sh_context[3])
+               : "sp"
+       );
+   }

-   /* Should not get here! */
-   return 0;
+   return pdFALSE;
 }
 /*-----------------------------------------------------------*/

 void vPortEndScheduler( void )
 {
-   /* It is unlikely that the CM3 port will require this function as there
-   is nothing to return to.  */
+   /* Restore point where sheduler started with tricky change of control flow */
+   sh_stop_request = 1;
+   xPortStartScheduler();
 }
 /*-----------------------------------------------------------*/
It works as expected but I didn’t tested heavily.
It must work also on non-MPU port if you remove
*portMPU_CTRL &= ~portMPU_ENABLE;
line.

vTaskEndScheduler not working as stated

Oops. It’s wrong solution. I found that prvRestoreContextOfFirstTask() unwinds main stack which corrupts saved “context” needed to return back to vTaskStartScheduler(). Why ???
P.S. It’s very strange that it worked for me very first time.

vTaskEndScheduler not working as stated

I didn’t found any reason to unwind stack and removed few assembler instructions as useless (and destructive). Now it works perfectly. A final patch as follows:
diff -u -r FreeRTOSV7.1.0/Source/portable/GCC/ARM_CM3_MPU/port.c FreeRTOSV7.1.0_improved/Source/portable/GCC/ARM_CM3_MPU/port.c
--- FreeRTOSV7.1.0/Source/portable/GCC/ARM_CM3_MPU/port.c   2011-12-13 22:38:23.906250000 +0700
+++ FreeRTOSV7.1.0_improved/Source/portable/GCC/ARM_CM3_MPU/port.c  2012-08-02 19:20:05.640625000 +0700
@@ -288,10 +288,6 @@
 {
    __asm volatile 
    (
-       "   ldr r0, =0xE000ED08             n" /* Use the NVIC offset register to locate the stack. */
-       "   ldr r0, [r0]                    n"
-       "   ldr r0, [r0]                    n"
-       "   msr msp, r0                     n" /* Set the msp back to the start of the stack. */
        "   ldr r3, pxCurrentTCBConst2      n" /* Restore the context. */
        "   ldr r1, [r3]                    n"
        "   ldr r0, [r1]                    n" /* The first item in the TCB is the task top of stack. */
@@ -316,8 +312,11 @@
 /*
  * See header file for description.
  */
+static volatile char sh_stop_request = 0; // indicates whether xPortStartScheduler() called from vPortEndScheduler() to stop sheduler
+static volatile unsigned long sh_context[4]; // stores context of vTaskEndScheduler() call to restore it when sheduler being requested to stop
 portBASE_TYPE xPortStartScheduler( void )
 {
+   if (!sh_stop_request) {
    /* Make PendSV and SysTick the same priroity as the kernel. */
    *(portNVIC_SYSPRI2) |= portNVIC_PENDSV_PRI;
    *(portNVIC_SYSPRI2) |= portNVIC_SYSTICK_PRI;
@@ -332,19 +331,49 @@
    /* Initialise the critical nesting count ready for the first task. */
    uxCriticalNesting = 0;

+   /* Save context to return from vTaskEndScheduler() */
+   __asm volatile
+   (
+           "   push {r0-r12,lr}            n"
+           "   mrs %[basepri_val], basepri n"
+           "   mrs %[control_val], control n"
+           "   mrs %[psp_val], psp         n"
+           "   mrs %[msp_val], msp         n"
+           : [basepri_val]"=r" (sh_context[0]), [psp_val]"=r" (sh_context[1]), [msp_val]"=r" (sh_context[2]), [control_val]"=r" (sh_context[3])
+   );
+
    /* Start the first task. */
    __asm volatile( "   svc %0          n"
                    :: "i" (portSVC_START_SCHEDULER) );
+   } else {
+       sh_stop_request = 0;

-   /* Should not get here! */
-   return 0;
+       /* Cancel most important system changes made when sheduler started */
+       *(portNVIC_SYSTICK_CTRL) &= ~(portNVIC_SYSTICK_CLK | portNVIC_SYSTICK_INT | portNVIC_SYSTICK_ENABLE);
+       *portMPU_CTRL &= ~portMPU_ENABLE;
+       /* Restore context to return back to vTaskStartScheduler() */
+       __asm volatile
+       (
+               "   msr basepri, %[basepri_val] n"
+               "   msr control, %[control_val] n"
+               "   msr psp, %[psp_val]         n"
+               "   msr msp, %[msp_val]         n"
+               "   isb                         n"
+               "   pop {r0-r12,lr}             n"
+               :: [basepri_val]"r" (sh_context[0]), [psp_val]"r" (sh_context[1]), [msp_val]"r" (sh_context[2]), [control_val]"r" (sh_context[3])
+               : "sp"
+       );
+   }
+
+   return pdFALSE;
 }
 /*-----------------------------------------------------------*/

 void vPortEndScheduler( void )
 {
-   /* It is unlikely that the CM3 port will require this function as there
-   is nothing to return to.  */
+   /* Restore point where sheduler started with tricky change of control flow */
+   sh_stop_request = 1;
+   xPortStartScheduler();
 }
 /*-----------------------------------------------------------*/