Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Published by Scroll Versions from this space and version 3.06.00

...

Recall that our application consist of app.c which contains the code shown below:

Code Block
          void/* app.c main*/
(void)          #include { "os.h"
          #include   OS_ERR"app_cfg.h"
 err;         static  OS_TCB                OSInit(&err)App_TaskStartTCB;
          static    OSStart(&err)CPU_STK_SIZE  App_TaskStartStk[APP_CFG_TASK_START_STK_SIZE];
          static  void          App_TaskStart(void  *p_arg);
 
          void  main (void)
 <- Set a BREAKPOINT here!         {
              OS_ERR  err;
}

STEP 1

You now need to build and download this project to your target. Building is obviously highly toolchain specific. Of course, if you encounter errors during the build, you will need to resolve those before being able to move to the next step.

Once all build errors have been resolved, you need to download the target code onto the evaluation board you selected for the tests.

STEP 2

You then need to set a breakpoint at the OSStart() line. In other words, have your target stop AFTER executing OSInit(). You should then examine the contents of ‘err’ and confirm that it has the value OS_ERR_NONE (or, 0). If you get anything other than OS_ERR_NONE, the error code will tell you where the problem is (see section A-20).

STEP 3

If err is OS_ERR_NONE then you can ‘Step Into’ OSStart() (file os_core.c). You should see the following code:

Code Block

              OSInit(&err);

              OSTaskCreate(&App_TaskStartTCB,
                         void  OSStart (OS_ERR  *p_err)"App Task Start",
           {           #ifdef OS_SAFETY_CRITICAL     App_TaskStart,
          if (p_err == (OS_ERR *)0) {                  0,
         OS_SAFETY_CRITICAL_EXCEPTION();                   return;APP_CFG_TASK_START_PRIO,
              }           #endif  &App_TaskStartStk[0],
                        if (OSRunning == OS_STATE_OS_STOPPED) { (APP_CFG_TASK_START_STK_SIZE / 10u),
                   OSPrioHighRdy   = OS_PrioGetHighest();     APP_CFG_TASK_START_STK_SIZE,
                 (1)           0,
        OSPrioCur       = OSPrioHighRdy;            0,
      OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;          (2)          0,
        OSTCBCurPtr     = OSTCBHighRdyPtr;             (OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
   OSRunning       = OS_STATE_OS_RUNNING;                &err);

 OSStartHighRdy();             OSStart(&err);
          }
          static  void  App_TaskStart     (3(void *p_arg)
          {
      *p_err           = OS_ERR_FATAL_RETURN;  err;

                      
   OSTaskSuspend(0, &err);
          }
else {
      

STEP 1

You now need to build and download this project to your target. Building is obviously highly toolchain specific. Of course, if you encounter errors during the build, you will need to resolve those before being able to move to the next step.

Once all build errors have been resolved, you need to download the target code onto the evaluation board you selected for the tests.

STEP 2

You then need to set a breakpoint at the OSStart() line. In other words, have your target stop AFTER executing OSInit(). You should then examine the contents of ‘err’ and confirm that it has the value OS_ERR_NONE (or, 0). If you get anything other than OS_ERR_NONE, the error code will tell you where the problem is (see section A-20).

STEP 3

If err is OS_ERR_NONE then you can ‘Step Into’ OSStart() (file os_core.c). You should see the following code:

Code Block
          void  OSStart (OS_ERR  *p_err)
          {
          #ifdef OS_SAFETY_CRITICAL
              if (p_err == (OS_ERR *)0) {
                  OS_SAFETY_CRITICAL_EXCEPTION();
                  return;
              }
          #endif
 
              :
              :
 
              if (OSTaskQty <= kernel_task_cnt) {                         /* No application task created                          */
                  *p_err = OS_ERR_OS_NO_APP_TASK;
                   return;
              }
              if (OSRunning == OS_STATE_OS_STOPPED) {
                  OSPrioHighRdy   = OS_PrioGetHighest();                       (1)
                  OSPrioCur       = OSPrioHighRdy;
                  OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;          (2)
                  OSTCBCurPtr     = OSTCBHighRdyPtr;
                  OSRunning       = OS_STATE_OS_RUNNING;
                  OSStartHighRdy();                                            (3)
                 *p_err           = OS_ERR_FATAL_RETURN;                          
              } else {
                 *p_err           = OS_ERR_OS_RUNNING;             
              }
          }

STEP 4

Step into the code and stop just before executing OSStartHighRdy(). You should confirm that OSPrioCur is the same value as OS_CFG_TICK_TASK_PRIO (see os_cfg_app.h) and that OSTCBHighRdyPtr point at OSTickTaskTCB. In other words, the highest priority task should be the tick task because we should only have two task created after OSInit() and the tick task always has a higher priority than the idle task.

STEP 5

Now, ‘Step Into’ OSStartHighRdy() (file os_cpu_a.asm). You should see the assembly language shown below.

Code Block
          OSStartHighRdy:
              OSTaskSwHook();
              SP = OSTCBHighRdyPtr->StkPtr;
              OS_CTX_RESTORE
              Return from Interrupt/Exception;

You can ‘Step Over’ OSTaskSwHook() and the code to load the stack pointer. However, you should set a breakpoint at the ‘Return from Interrupt/Exception’ instruction. Once you executed the OS_CTX_RESTORE macro, you should look at the CPU registers and confirm that they all have their expected value (0x12121212 for R120x05050505 for R5, etc.). If not then something is not quite right with either OSTaskStkInit() or the OS_CTX_RESTORE macro. Basically, OSTaskStkInit() sets up the stack and OS_CTX_RESTORE sets up the registers based on what’s on the stack.

STEP 6

If the CPU registers appear to have their proper value then you can ‘Single Step’ and execute the ‘Return from Interrupt/Exception’ instruction. If all is well, you should be looking at the OS_TickTask() code which should look something like this:

Code Block
          void  OS_TickTask (void  *p_arg)
          {
              OS_ERR  err;
              CPU_TS  ts;
           
           
      *p_err        p_arg = p_arg;                                      
              =while OS_ERR_OS_RUNNING;(DEF_ON) {
                  (void)OSTaskSemPend((OS_TICK  )0,
                }           }

STEP 4

Step into the code and stop just before executing OSStartHighRdy(). You should confirm that OSPrioCur is the same value as OS_CFG_TICK_TASK_PRIO (see os_cfg_app.h) and that OSTCBHighRdyPtr point at OSTickTaskTCB. In other words, the highest priority task should be the tick task because we should only have two task created after OSInit() and the tick task always has a higher priority than the idle task.

STEP 5

Now, ‘Step Into’ OSStartHighRdy() (file os_cpu_a.asm). You should see the assembly language shown below.

Code Block
             (OS_OPT   )OS_OPT_PEND_BLOCKING,
                                      (CPU_TS  *)&ts,
                                      (OS_ERR  *)&err); 
      OSStartHighRdy:            if (err  OSTaskSwHook();== OS_ERR_NONE) {                 SP = OSTCBHighRdyPtr->StkPtr;     <- Set a BREAKPOINT here!
     OS_CTX_RESTORE               Return from Interrupt/Exception;

You can ‘Step Over’ OSTaskSwHook() and the code to load the stack pointer. However, you should set a breakpoint at the ‘Return from Interrupt/Exception’ instruction. Once you executed the OS_CTX_RESTORE macro, you should look at the CPU registers and confirm that they all have their expected value (0x12121212 for R120x05050505 for R5, etc.). If not then something is not quite right with either OSTaskStkInit() or the OS_CTX_RESTORE macro. Basically, OSTaskStkInit() sets up the stack and OS_CTX_RESTORE sets up the registers based on what’s on the stack.

STEP 6

If the CPU registers appear to have their proper value then you can ‘Single Step’ and execute the ‘Return from Interrupt/Exception’ instruction. If all is well, you should be looking at the OS_TickTask() code which should look something like this:

Code Block
          void  OS_TickTask (void  *p_arg)if (OSRunning == OS_STATE_OS_RUNNING) {
                          OS_TickListUpdate(); 
                      }
                  }
              }
          }

If the debugger doesn’t show you this code then it’s possible that the PC and PSW are not properly setup on the task stack by OSTaskStkInit().

If you end up in OS_TickTask() your code for OSTaskStkInit() and the macro OS_CTX_RESTORE is correct.

You should now set a breakpoint on the line following OSTaskSemPend().

STEP 7

You need to set another breakpoint in OSCtxSw() as shown below.

Code Block
          OSCtxSw: {               OS_ERR  err;               CPU_TS  ts;                   <- Set a BREAKPOINT here! 
              p_arg = p_arg;OS_CTX_SAVE
              OSTCBCurPtr->StkPtr = SP;
              OSTaskSwHook();
              OSPrioCur   = OSPrioHighRdy;
 while (DEF_ON) {           OSTCBCurPtr        (void)OSTaskSemPend((OS_TICK  )0,= OSTCBHighRdyPtr;
              SP               = OSTCBCurPtr->StkPtr;
           (OS_OPT   )OS_OPT_PEND_BLOCKING,CTX_RESTORE
              Return from                       (CPU_TS  *)&ts,
                                      (OS_ERR  *)&err); 
                  if (err == OS_ERR_NONE) {                        <- Set a BREAKPOINT here!
                      if (OSRunning == OS_STATE_OS_RUNNING) {
                          OS_TickListUpdate(); 
             Interrupt/Exception;

You can now run the code at full speed. Because of the breakpoint in OSCtxSw(), the debugger should stop and show you the code for OSCtxSw().

Basically, what’s happening here is that OS_TickTask() will be waiting for the tick ISR to signal the task that a tick has expired. Since we haven’t setup the tick interrupt (not yet anyway), OS_TickTask() would never get to execute. However, I had you modify the idle task hook to simulate signaling the tick task so µC/OS-III will eventually switch back to this code. In the meantime, µC/OS-III will switch to the next task that’s ready-to-run. This happens to be the idle task. We’ll be following the code until we get to OS_IdleTask().

STEP 8

You can ‘Step Over’ OS_CTX_SAVE and verify that the stack (pointed to by SP) contains the value of the CPU registers saved in the same order as they are in OSTaskStkInit(). In fact, you can verify this when context switches back out of the idle task in just a few more steps.

STEP 9

‘Step Into’ the code one more time and verify that the SP was saved in OSTickTaskTCB.StkPtr.

STEP 10

‘Step Into’ the code and stop just before executing the ‘Return from Interrupt/Exception’ instruction. At this point, the CPU registers should contain the proper register values (similar to what we had when we restored the CPU registers for OSTickTask() (but this time it’s for OS_IdleTask()).

STEP 11

‘Step Into’ the return from interrupt/exception instruction and the CPU should now jump into the idle task (os_core.c) as shown below. You should then set a breakpoint as shown.

Code Block
         } void  OS_IdleTask (void  *p_arg)
          {
 }             CPU_SR_ALLOC();
 }           }

If the debugger doesn’t show you this code then it’s possible that the PC and PSW are not properly setup on the task stack by OSTaskStkInit().

If you end up in OS_TickTask() your code for OSTaskStkInit() and the macro OS_CTX_RESTORE is correct.

You should now set a breakpoint on the line following OSTaskSemPend().

STEP 7

You need to set another breakpoint in OSCtxSw() as shown below.

Code Block
     
           
              p_arg = p_arg;  
          OSCtxSw:    while (DEF_ON) {
                  CPU_CRITICAL_ENTER();                                 <- Set a BREAKPOINT here!
               OS_CTX_SAVE   OSIdleTaskCtr++;
           OSTCBCurPtr->StkPtr = SP;#if OS_CFG_STAT_TASK_EN > 0u
              OSTaskSwHook();    OSStatTaskCtr++;
          OSPrioCur #endif
 = OSPrioHighRdy;               OSTCBCurPtr = OSTCBHighRdyPtr CPU_CRITICAL_EXIT();
           
  SP          = OSTCBCurPtr->StkPtr;    <strong> OSIdleTaskHook();</strong> 
        OS_CTX_RESTORE      }
        Return from Interrupt/Exception;

You can now run the code at full speed. Because of the breakpoint in OSCtxSw(), the debugger should stop and show you the code for OSCtxSw().

...

}

STEP 12

‘Step Into’ the idle task and then, ‘Step Into’ OSIdleTaskHook(). Recall that I had you modify the idle task hook to simulate signaling the tick task so µC/OS-III will eventually switch back to this code. In the meantimeas shown below. What we’re doing here is simulate the occurrence of the tick interrupt.

STEP 13

Have your debugger run the code at full speed. You should actually hit the breakpoint in OSCtxSw() as shown below. What happened here is that µC/OS-III signaled the tick task and since the tick task is more important than the idle task, µC/OS-III will switch is switching back to the next task that’s ready-to-run. This happens to be the idle task. We’ll be following the code until we get to tick task.

STEP 14

You can run the target at full speed and the debugger should bring you back at the breakpoint in OS_TickTask().

If you were to repeatedly run the target at full speed, your debugger should now stop at the following places:

OSCtxSw()
OS_IdleTask()

...

STEP 8

You can ‘Step Over’ OS_CTX_SAVE and verify that the stack (pointed to by SP) contains the value of the CPU registers saved in the same order as they are in OSTaskStkInit(). In fact, you can verify this when context switches back out of the idle task in just a few more steps.

STEP 9

‘Step Into’ the code one more time and verify that the SP was saved in OSTickTaskTCB.StkPtr.

STEP 10

‘Step Into’ the code and stop just before executing the ‘Return from Interrupt/Exception’ instruction. At this point, the CPU registers should contain the proper register values (similar to what we had when we restored the CPU registers for OSTickTask() (but this time it’s for OS_IdleTask()).

STEP 11

‘Step Into’ the return from interrupt/exception instruction and the CPU should now jump into the idle task (os_core.c) as shown below. You should then set a breakpoint as shown.

...

OSCtxSw()
OS_TickTask()
OSCtxSw()
OS_IdleTask()
OSCtxSw()
etc.

Verifying Interrupt Context Switches

In this section, we will verify the proper operation of the following functions/macros:

BSP_OS_TickISR()     (bsp_os.c, bsp_os_a.asm, os_cpu_c.c or os_cpu_a.asm)
OSIntCtxSw()         (os_cpu_a.asm)
OS_ISR_ENTER         (os_cpu_a.inc)
OS_ISR_EXIT          (os_cpu_a.inc)
CPU_INT_EN()         (cpu.h)  

...

     
CPU_IntEn()          

...

(cpu_a.asm)
 

You can now remove the code we added in OSIdleTaskHook(). The code should now be as shown below.

Code Block
          void  OSIdleTaskHook (void)
          {
              p_arg = p_arg;#if OS_CFG_APP_HOOKS_EN > 0u
              if  while(OS_AppIdleTaskHookPtr != (DEF_ONOS_APP_HOOK_VOID)0) {
                  CPU_CRITICAL_ENTER(*OS_AppIdleTaskHookPtr)();
              }
          #endif
      <- Set a BREAKPOINT here!
}

You should now setup the tick interrupt in main() (app.c) as shown below.

Code Block
          /*     app.c */
  OSIdleTaskCtr++;           #if OS_CFG_STAT_TASK_EN > 0u   #include  <os.h>
          #include   OSStatTaskCtr++;"app_cfg.h"
          static  OS_TCB  #endif      App_TaskStartTCB;
          static  CPU_CRITICAL_EXIT()_STK_SIZE  App_TaskStartStk[APP_CFG_TASK_START_STK_SIZE];
          static  void          App_TaskStart(void  *p_arg);
    <strong> OSIdleTaskHook();</strong>      
         } void  main (void)
      }

STEP 12

‘Step Into’ the idle task and then, ‘Step Into’ OSIdleTaskHook(). Recall that I had you modify the idle task hook as shown below. What we’re doing here is simulate the occurrence of the tick interrupt.

STEP 13

Have your debugger run the code at full speed. You should actually hit the breakpoint in OSCtxSw() as shown below. What happened here is that µC/OS-III signaled the tick task and since the tick task is more important than the idle task, µC/OS-III is switching back to the tick task.

STEP 14

You can run the target at full speed and the debugger should bring you back at the breakpoint in OS_TickTask().

If you were to repeatedly run the target at full speed, your debugger should now stop at the following places:

OSCtxSw()
OS_IdleTask()
OSCtxSw()
OS_TickTask()
OSCtxSw()
OS_IdleTask()
OSCtxSw()
etc.

Verifying Interrupt Context Switches

In this section, we will verify the proper operation of the following functions/macros:

BSP_OS_TickISR()     (bsp_os.c, bsp_os_a.asm, os_cpu_c.c or os_cpu_a.asm)

...

     {
              OS_ERR  err;
           
              OSInit(&err);
              /* (1) Install interrupt vector for OSTickISR()                           */
              /* (2) Initialize the tick timer to generate interrupts every millisecond */
              OSTaskCreate(&App_TaskStartTCB,
                           "App Task Start",
                            App_TaskStart,
           

...

          

...

       0,
  

...

         

...

       

...

          

...

 

You can now remove the code we added in OSIdleTaskHook(). The code should now be as shown below.

Code Block
APP_CFG_TASK_START_PRIO,
         void  OSIdleTaskHook (void)           {    &App_TaskStartStk[0],
      #if OS_CFG_APP_HOOKS_EN > 0u               if (OS_AppIdleTaskHookPtr != (OS_APP_HOOK_VOID)0) {
APP_CFG_TASK_START_STK_SIZE / 10u),
                 (*OS_AppIdleTaskHookPtr)();            APP_CFG_TASK_START_STK_SIZE,
  }           #endif           } 

You should now setup the tick interrupt in main() (app.c) as shown below.

Code Block
   0,
      /* app.c */                    0,
  #include  "includes.h"                       void 0,
main (void)           {               (OS_ERR  err;OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                           OSInit(&err););
              CPU_INT_EN();       /* (13) InstallEnable interruptinterrupts vector for OSTickISR()                           */
              /* (2) Initialize the tick timer to generate interrupts every millisecond */
  OSStart(&err);
          }

 
           CPU_INT_EN();       /* (3) Enable interruptsstatic  void  App_TaskStart (void *p_arg)
          {
              OS_ERR  err;
 

*/              OSTaskSuspend(0, OSStart(&err);
          }

At this point, you need to remove all breakpoints you inserted to test the task level context switch code and insert the following breakpoints. You should note that the C-like code should actually be replaced with assembly language instructions for your processor.

...