uC-OS-III Port
Porting µC/OS-III
The table following the figure below below shows the name of µC/OS-III files and where they are typically found.
For the purpose of demonstrating how to do a port, we will assume the generic 32-bit processor as described in Context Switching and shown in the figure below.
Our generic CPU contains 16 integer registers (R0 to R15), a separate ISR stack pointer, and a separate status register (SR). Every register is 32 bits wide and each of the 16 integer registers can hold either data or an address. The return address of a function call is placed in the Link Register ( LR
). The program counter (or instruction pointer) is R15 and there are two separate stack pointers labeled R14 and R14’. R14 represents a task stack pointer (TSP), and R14’ represents an ISR stack pointer (ISP). The CPU automatically switches to the ISR stack when servicing an exception or interrupt. The task stack is accessible from an ISR (i.e., we can push and pop elements onto the task stack when in an ISR), and the interrupt stack is also accessible from a task. The Status Register (SR) contains the interrupt mask as well as various status such as the Carry, Zero, Sign, Overflow, Parity, etc.
File | Directory |
---|---|
bsp_os.c bsp_os.h bsp_os_a.asm | \Micrium\Software\EvalBoards\<manufacturer>\<board>\BSP\OS\uCOS-III\ |
os_cpu.h | \Micrium\Software\uCOS-III\Ports\<processor>\<compiler>\ |
os_cpu_a.asm | \Micrium\Software\uCOS-III\Ports\<processor>\<compiler>\ |
os_cpu_a.inc | \Micrium\Software\uCOS-III\Ports\<processor>\<compiler> |
os_cpu_c.c | \Micrium\Software\uCOS-III\Ports\<processor>\<compiler>\ |
Here, <processor>
is the name of the processor that the os_cpu*.*
files apply to, and <compiler>
is the name of the compiler that these files assume because of the different assembly language directives that different toolchain uses.
The table below shows where you can find template files that will help you create a µC/OS-III port from scratch. You would simply copy these files in a folder specific to your processor/compiler as shown in the table above and then change the contents of these files per your processor/compiler.
File | Directory |
---|---|
os_cpu.h | \Micrium\Software\uCOS-III\Ports\Template\ |
os_cpu_a.asm | \Micrium\Software\uCOS-III\Ports\Template\ |
os_cpu_a.inc | \Micrium\Software\uCOS-III\Ports\Template\ |
os_cpu_c.c | \Micrium\Software\uCOS-III\Ports\Template\ |
bsp_os.c, bsp_os_a.asm and bsp_os.h — Periodic Tick
Board Support Package (BSP) specific code is generally needed to provide µC/OS-III with a periodic time source. This is typically obtained from a hardware timer that is configured to generate a ‘tick rate’ between 10 and 1000 Hz. By convention, we decided to call these files bsp_os.c, bsp_os_a.asm (optional) and bsp_os.h. These files, part of the BSP, typically contain code for just a couple of functions: BSP_OS_TickInit()
and BSP_OS_TickISR()
.
BSP_OS_TickInit()
This function must be called by the first task that executes under µC/OS-III and after you called CPU_Init()
. Alternatively, you can place BSP_OS_TickInit()
in os_cpu_c.c
depending on whether or not the tick ISR is generic for the CPU architecture you are using. In other words, if the CPU or MCU has a dedicated timer that can be assigned for the tick ISR so that it’s the same, irrespective of the target application then BSP_OS_TickInit()
can be placed in os_cpu_c.c
. The pseudo-code for this function is shown in the listing below.
void BSP_OS_TickInit (CPU_INT32U freq) (1) { Install the interrupt vector for the timer used to generate tick interrupts; (2) Configure the timer to generate interrupts at 'freq' Hz; (3) Enable timer interrupts; (4) }
BSP_OS_TickISR()
If ISRs are implemented in assembly language this function could be placed in a file called bsp_os_a.asm
(or other filename extension as needed by your assembler). Alternatively, you can place BSP_OS_TickISR()
in os_cpu_a.asm
depending on whether or not the tick ISR is generic for the CPU and, whether it needs to be implemented in assembly language. In other words, if the CPU or MCU has a dedicated timer that can be assigned for the tick ISR so that it’s the same, irrespective of the target application then BSP_OS_TickISR()
can be placed in os_cpu_a.asm
. The pseudo code for this function is shown below (the C-like code needs to be implemented in assembly language). You should note that all ISRs should be modeled after BSP_OS_TickISR()
.
BSP_OS_TickISR: (1) OS_CTX_SAVE (2) Disable Interrupts; (3) OSIntNestingCtr++; (4) if (OSIntNestingCtr == 1) { (5) OSTCBCurPtr->StkPtr = SP; } Clear tick interrupt; (6) OSTimeTick(); (7) OSIntExit(); (8) OS_CTX_RESTORE (9) Return from Interrupt/Exception; (10)
(1) BSP_OS_TickISR()
is generally invoked automatically by the interrupt controller when the tick interrupt occurs. Assuming again our generic 32-bit CPU, it’s assumed here that the SR
and PC
of the interrupted task are pushed automatically onto the stack of the interrupted task.
(2) Again, OS_CTX_SAVE
saves the CPU context onto the current task’s stack. For our generic 32-bit CPU, OS_CTX_SAVE
would push R0
through R13
onto the stack, in that order.
(3) Interrupts should be disabled here. On some processors, interrupts are automatically disabled when the processor accepts the interrupt. Some processors support multiple interrupt levels. In fact, some interrupts are allowed to make kernel calls while others are not. Typically, interrupts that do not make kernel calls (called Non-Kernel Aware Interrupts) would generally be high priority interrupts and kernel aware interrupts would all be grouped (in priority) below these. For example, if a processor has 16
different interrupt levels and level 0
is the lowest priority interrupt then, all kernel aware interrupts would be assigned from 0 to some number N
(let’s say 12
) and N+1
to 15
would be assigned to be non-kernel aware interrupts.
(4) BSP_OS_TickISR()
then needs to increment the interrupt nesting counter. This tells µC/OS-III that the code is servicing an interrupt. The nesting counter indicates how many levels of interrupts we are currently servicing (in case the application supports nested interrupts).
(5) If this interrupt interrupts a task then we need to save the stack pointer of that task into the OS_TCB
of that task.
(6) You need to clear the interrupting device so that it doesn’t re-issue the same interrupt upon returning from interrupts. This can be done here or, in the device handler (see below).
(7) At this point, BSP_OS_TickISR()
calls OSTimeTick()
which is responsible for notifying the tick task that a tick occurred.
If you model your ISR like BSP_OS_TickISR()
then you would call your own C function to service the interrupting device.
(8) OSIntExit()
is then called at the end of the ISR to notify µC/OS-III that you are done processing the ISR. µC/OS-III decrements the nesting counter and if OSIntNestingCtr
reaches 0
, µC/OS-III knows it’s returning to task level code. So, if the ISR made a more important task ready-to-run (more important than the interrupted task), µC/OS-III will context switch to that task instead of returning to the interrupted task.
(9) If the interrupted task is still the most important task then OSIntExit()
returns and the ISR will need to restore the saved registers. OS_CTX_RESTORE
does just that. For our generic 32-bit CPU, OS_CTX_RESTORE
would pop CPU registers R13
through R0
from the stack, in that order.
(10) Finally, the Return from Interrupt/Exception restores the Program Counter (PC
) and the Status Register (SR
) in a single instruction. At this point, the interrupted task will resume execution, exactly where it was interrupted.
It is actually possible to simplify the code for BSP_OS_TickISR()
or any of your ISRs. Notice that the code at the beginning and end of the ISR is common for all ISRs. Because of that, it’s possible to create two assembly language macros, OS_ISR_ENTER
and OS_ISR_EXIT
in os_cpu_a.inc
. The new BSP_OS_TickISR()
code would now look as shown below:
BSP_OS_TickISR: OS_ISR_ENTER Clear tick interrupt; OSTimeTick(); OS_ISR_EXIT
os_cpu.h
OS_TASK_SW()
OS_TASK_SW()
is a macro that is called by OSSched()
to perform a task-level context switch. The macro can translate directly to a call to OSCtxSw()
, trigger a software interrupt, or a TRAP
. If a software interrupt or TRAP
is used then you would most likely need to add the address of OSCtxSw()
in the interrupt vector table. The choice depends on the CPU architecture.
OS_TS_GET()
OS_TS_GET()
is a macro that obtains the current time stamp. It is expected that the time stamp is type CPU_TS
, which is typically declared as at least a 32-bit value.
OSCtxSw(), OSIntCtxSw() and OSStartHighRdy()
os_cpu.h
declares function prototypes for OSCtxSw()
, OSIntCtxSw()
, OSStartHighRdy()
and possibly other functions required by the port.
os_cpu_c.c
The functions are described in µC-OS-III API Reference . os_cpu_c.c
can declare any functions needed by the port, however the functions described below are mandatory. These functions are already implemented in the template file, but those can certainly be extended as needed. You should not have to change this file unless you have specific requirements.
OSIdleTaskHook()
This function is called repeatedly when µC/OS-III does not have any task ready-to-run. The port implemented might choose to put the processor in low power mode if the product being designed is battery operated. However, it would be preferable to defer this choice to the application level. You can do this by putting the processor in low power mode in a function called App_OS_IdleTaskHook()
and let the application decide whether or not it is appropriate to place the processor in low power mode. The template file contains the following code:
void OSIdleTaskHook (void) { #if OS_CFG_APP_HOOKS_EN > 0u if (OS_AppIdleTaskHookPtr != (OS_APP_HOOK_VOID)0) { (*OS_AppIdleTaskHookPtr)(); } #endif }
OSInitHook()
This function is called by OSInit()
at the very beginning of OSInit()
. This is done to allow the port implemented to add functionality to the port while hiding the details from the µC/OS-III user. For one thing, the port implementer could setup an ISR stack in OSInitHook()
. The template file contains the following code:
void OSInitHook (void) { }
OSRedzoneHitHook()
If Redzone Stack Checking is enabled (OS_CFG_TASK_STK_REDZONE_EN
set to DEF_ENABLED
in is os_cfg.h
), this function is called when µC/OS-III determines that a task's stack has overflowed its Redzone. The function calls an application hook, if defined. If not, it calls a software exception. The application hook could try to fix the stack, report an error or simply call the software exception.
void OSRedzoneHitHook (OS_TCB *p_tcb) { #if OS_CFG_APP_HOOKS_EN > 0u if (OS_AppRedzoneHitHookPtr != (OS_APP_HOOK_TCB)0) { (*OS_AppRedzoneHitHookPtr)(p_tcb); } #endif (void)p_tcb; CPU_SW_EXCEPTION(;); }
OSStatTaskHook()
This function is called when the statistic task executes. This hook allows the port developer the opportunity to add his or her own statistics. The template file contains the following code:
void OSStatTaskHook (void) { #if OS_CFG_APP_HOOKS_EN > 0u if (OS_AppStatTaskHookPtr != (OS_APP_HOOK_VOID)0) { (*OS_AppStatTaskHookPtr)(); } #endif }
OSTaskCreateHook()
This function is called by OSTaskCreate()
and is passed the address of the OS_TCB
of the newly created task. OSTaskCreateHook()
is called by OSTaskCreate()
after initializing the OS_TCB
fields and setting up the stack frame for the task. The template file contains the following code:
void OSTaskCreateHook (OS_TCB *p_tcb) { #if OS_CFG_APP_HOOKS_EN > 0u if (OS_AppTaskCreateHookPtr != (OS_APP_HOOK_TCB)0) { (*OS_AppTaskCreateHookPtr)(p_tcb); } #else (void)p_tcb; }
OSTaskDelHook()
This function is called by OSTaskDel()
after the task to delete has been removed either from the ready list or a wait list. The template file contains the following code:
void OSTaskDelHook (OS_TCB *p_tcb) { #if OS_CFG_APP_HOOKS_EN > 0u if (OS_AppTaskDelHookPtr != (OS_APP_HOOK_TCB)0) { (*OS_AppTaskDelHookPtr)(p_tcb); } #else (void)p_tcb; #endif }
OSTaskReturnHook()
This function is called by OS_TaskReturn()
if the user accidentally returns from the task code. The template file contains the following code:
void OSTaskReturnHook (OS_TCB *p_tcb) { #if OS_CFG_APP_HOOKS_EN > 0u if (OS_AppTaskReturnHookPtr != (OS_APP_HOOK_TCB)0) { (*OS_AppTaskReturnHookPtr)(p_tcb); } #else (void)p_tcb; }
OSTaskStkInit()
OSTaskStkInit()
is called by OSTaskCreate()
and is one of the most difficult port functions to create because it establishes the stack frame of every task created. The template file contains the following code:
CPU_STK *OSTaskStkInit (OS_TASK_PTR p_task, void *p_arg, CPU_STK *p_stk_base, CPU_STK *p_stk_limit, CPU_STK_SIZE stk_size, OS_OPT opt) { CPU_STK *p_stk; (void)opt; p_stk = &p_stk_base[stk_size]; (1) *--p_stk = (CPU_STK)0x00000000u; (2) *--p_stk = (CPU_STK)p_task; (3) *--p_stk = (CPU_STK)p_arg; (4) *--p_stk = (CPU_STK)0x01010101u; (5) *--p_stk = (CPU_STK)0x02020202u; *--p_stk = (CPU_STK)0x03030303u; *--p_stk = (CPU_STK)0x04040404u; *--p_stk = (CPU_STK)0x05050505u; *--p_stk = (CPU_STK)0x06060606u; *--p_stk = (CPU_STK)0x07070707u; *--p_stk = (CPU_STK)0x08080808u; *--p_stk = (CPU_STK)0x09090909u; *--p_stk = (CPU_STK)0x10101010u; *--p_stk = (CPU_STK)0x11111111u; *--p_stk = (CPU_STK)0x12121212u; *--p_stk = (CPU_STK)OS_TaskReturn; (6) return (p_stk); (7) }
(1) You need to initialize the top-of-stack. For our ‘generic 32-bit CPU, the top-of-stack (TOS) points at one location beyond the area reserved for the stack. This is because we will decrement the TOS pointer before storing a value into the stack.
If the stack for the processor grew from low memory to high memory, most likely you would have setup the TOS to point at the base of the memory or, &p_stk_base[0]
.
(2) Since we are simulating an interrupt and the stacking of registers in the same order as an ISR would place them on the stack, we start by putting the SR
(Status Register, also called the Program Status Word) of the CPU onto the stack first.
Also, the value stored at this location must be such that, once restored into the CPU, the SR
must enable ALL interrupts. Here we assumed that a value of 0x00000000
would do this. However, you need to check with the processor you are using to find out how this works on that processor.
(3) The address of the task code is then placed onto the next stack location. This way, when you perform a return from interrupt (or exception) instruction the PC will automatically be loaded with the address of the task to run.
(4) You should recall that a task is passed an argument, p_arg
. p_arg
is a pointer to some user define storage or function and its use is application specific. In the assumptions above, we indicated that a function called with a single argument gets this argument passed in R0
. You will need to check the compiler documentation to determine where ‘p_arg
’ is placed for your processor.
(5) The remaining registers are placed onto the stack. You will notice that we initialized the value of those registers with a hexadecimal number that corresponds to the register number. In other words, R12
should have the value 0x12121212
when the task starts, R11
should have the value 0x11111111
when the task starts and so on. This makes it easy to determine whether the stack frame was setup properly when you test the port. You would simply look at the register contents with a debugger and confirm that all registers have the proper values.
(6) Here we place the return address of the task into the location where the Link Register (LR
) will be retrieved from. In this case, we force the return address to actually be OS_TaskReturn()
allowing µC/OS-III to catch a task that is attempting to return. You should recall that this is not allowed with µC/OS-III.
(7) OSTaskStkInit()
needs to return the new top-of-stack location. In this case, the top-of-stack points at the last element placed onto the stack.
The figure below shows how the stack frame looks like just before the function returns. OSTaskCreate()
will actually save the new top-of-stack ( p_stk
) into the OS_TCB
of the task being created.
OSTaskSwHook()
The typical code for µC/OS-III’s context switch hook is shown below. What OSTaskSwHook()
does is highly dependent on a number of configuration options.
void OSTaskSwHook (void) { #if OS_CFG_TASK_PROFILE_EN > 0u CPU_TS ts; #endif #ifdef CPU_CFG_INT_DIS_MEAS_EN CPU_TS int_dis_time; #endif #if OS_CFG_APP_HOOKS_EN > 0u if (OS_AppTaskSwHookPtr != (OS_APP_HOOK_VOID)0) { (1) (*OS_AppTaskSwHookPtr)(); } #endif #if OS_CFG_TASK_PROFILE_EN > 0u ts = OS_TS_GET(); (2) if (OSTCBCurPtr != OSTCBHighRdyPtr) { OSTCBCurPtr->CyclesDelta = ts - OSTCBCurPtr->CyclesStart; OSTCBCurPtr->CyclesTotal += (OS_CYCLES)OSTCBCurPtr->CyclesDelta; } OSTCBHighRdyPtr->CyclesStart = ts; #endif #ifdef CPU_CFG_INT_DIS_MEAS_EN int_dis_time = CPU_IntDisMeasMaxCurReset(); (3) if (OSTCBCurPtr->IntDisTimeMax < int_dis_time) { OSTCBCurPtr->IntDisTimeMax = int_dis_time; } #endif #if OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u if (OSTCBCurPtr->SchedLockTimeMax < OSSchedLockTimeMaxCur) { (4) OSTCBCurPtr->SchedLockTimeMax = OSSchedLockTimeMaxCur; } OSSchedLockTimeMaxCur = (CPU_TS)0; #endif #if (OS_CFG_TASK_STK_REDZONE_EN == DEF_ENABLED) stk_status = OSTaskStkRedzoneChk(DEF_NULL); (5) if (stk_status != DEF_OK) { OSRedzoneHitHook(OSTCBCurPtr); } #endif }
(1) If the application code defined a hook function to be called during a context switch then this function is called first. You application hook function can assume that OSTCBCurPtr
points to the OS_TCB
of the task being switched out while OSTCBHighRdyPtr
points to the OS_TCB
of the task being switched in.
(2) OSTaskSwHook()
then computes the amount of time the current task ran. However, this includes the execution time of all the ISRs that happened while the task was running.
We then take a timestamp to mark the beginning of the task being switched in.
(3) OSTaskSwHook()
then stores the highest interrupt disable time into the OS_TCB
of the task being switched out. This allows a debugger or µC/Probe to display maximum interrupt disable time on a per-task basis.
(4) OSTaskSwHook()
then captures the highest scheduler lock time and stores that in the OS_TCB
of the task being switched out.
(5) If Redzone Stack Checking is enabled, the hook checks to see if the task that is about to be switched out has overflowed its stack.
OSTimeTickHook()
This function is called by OSTimeTick()
and is called before any other code is executed in OSTimeTick()
. The template file contains the following code. If the application code defines an application hook function then it is called as shown.
void OSTimeTickHook (void) { #if OS_CFG_APP_HOOKS_EN > 0u if (OS_AppTimeTickHookPtr != (OS_APP_HOOK_VOID)0) { (*OS_AppTimeTickHookPtr)(); } #endif }
os_cpu_a.asm
This file contains the implementation of the following assembly language functions:
OSStartHighRdy()
This function is called by OSStart()
to start multitasking. OSStart()
will have determined the highest priority task (OSTCBHighRdyPtr
will point to the OS_TCB
of that task) that was created prior to calling OSStart()
and will start executing that task. The pseudo code for this function is shown below (the C-like code needs to be implemented in assembly language):
OSStartHighRdy: OSTaskSwHook(); SP = OSTCBHighRdyPtr->StkPtr; (1) OS_CTX_RESTORE (2) Return from Interrupt/Exception; (3)
(1) The Stack Pointer (SP)
for the first task to execute is retrieved from the OS_TCB
of the highest priority task that was created prior to calling OSStart()
. The figure below shows the stack frame as pointed to by OSTCBHighRdy->StkPtr
.
(2) OS_CTX_RESTORE
is a macro (see os_cpu_a.inc
) that restores the context of the CPU (R0
through R13
) from the new task’s stack.
(3) The Return from Interrupt/Exception restores the Program Counter (PC
) and the Status Register (SR
) in a single instruction. At this point, the task will start executing. In fact, the task will think it was called by another function and thus, will receive ‘p_arg
’ as its argument. Of course, the task must not return.
OSCtxSw()
This function implements the task level context switch which is invoked by the OS_TASK_SW()
macro declared in os_cpu.h
. The pseudo code for this function is shown below (the C-like code needs to be implemented in assembly language). You should also refer to Chapter 8.
OSCtxSw: (1) OS_CTX_SAVE (2) OSTCBCurPtr->StkPtr = SP; (3) OSTaskSwHook(); OSPrioCur = OSPrioHighRdy; OSTCBCurPtr = OSTCBHighRdyPtr; SP = OSTCBCurPtr->StkPtr; (4) OS_CTX_RESTORE (5) Return from Interrupt/Exception; (6)
(1) OSCtxSw()
is invoked by OS_TASK_SW()
which is typically implemented as a software interrupt instruction or, a trap instruction. These types of instructions generally simulate the behavior of an interrupt, but is synchronous with the code. OSCtxSw()
is thus the entry point for this instruction. In other words, if a software interrupt or TRAP is used then you would most likely need to add the address of OSCtxSw()
in the interrupt vector table.
(2) OS_CTX_SAVE
is a macro (see os_cpu_a.inc
) that saves the CPU context onto the current task’s stack. For our generic 32-bit CPU, OS_CTX_SAVE
would push R0
through R13
onto the stack, in that order.
(3) OSCtxSw()
then needs to save the current top-of-stack pointer (i.e. R14
or SP
) into the OS_TCB
of the current task.
(4) The stack pointer for the new task is retrieved from the OS_TCB
of the new current task.
(5) OS_CTX_RESTORE
is a macro (see os_cpu_a.inc
) that restores the context of the CPU from the new task’s stack. For our generic 32-bit CPU, OS_CTX_RESTORE
would pop CPU registers R13
through R0
from the stack, in that order.
(6) The Return from Interrupt/Exception restores the Program Counter (PC
) and the Status Register (SR
) in a single instruction. At this point, the new task will resume execution, exactly where it was preempted.
OSIntCtxSw()
This function implements the interrupt level context switch which is called by OSIntExit()
(see os_core.c
). The pseudo code for this function is shown below (the C-like code needs to be implemented in assembly language). Refer also to Context Switching.
OSIntCtxSw: (1) OSTaskSwHook(); OSPrioCur = OSPrioHighRdy; OSTCBCurPtr = OSTCBHighRdyPtr; SP = OSTCBCurPtr->StkPtr; (2) OS_CTX_RESTORE (3) Return from Interrupt/Exception; (4)
(1) OSIntCtxSw()
is called by OSIntExit()
at the end of all nested ISRs. The ISR is assumed to have saved the context of the interrupted task onto that task’s stack. Also, the ISR is assumed to have saved the new top-of-stack of the interrupted task into the OS_TCB
of that task.
(2) The stack pointer for the new task is then retrieved from the OS_TCB
of the new current task.
(3) OS_CTX_RESTORE
is a macro (see os_cpu_a.inc
) that restores the context of the CPU from the new task’s stack. For our generic 32-bit CPU, OS_CTX_RESTORE
would pop CPU registers R13
through R0
from the stack, in that order.
(4) The Return from Interrupt/Exception restores the Program Counter (PC
) and the Status Register (SR
) in a single instruction. At this point, the new task will resume execution, exactly where it was preempted.
os_cpu_a.inc
This file contains the implementation of assembly language macros that are used to simplify the implementation of os_cpu_a.asm
. A macro replaces many assembly language instructions with a single macro invocation.
OS_CTX_SAVE
This macro is used to save the CPU context onto the current stack. OS_CTX_SAVE
needs to save the CPU registers in the same order as they are pushed in OSTaskStkInit()
which is described later. OS_CTX_SAVE
only saves the CPU registers that are not automatically saved by the CPU when the CPU accepts an interrupt. In other words, if the CPU automatically saves the PSW
and PC
onto the stack upon initiating an ISR then OS_CTX_SAVE
only needs to save the remaining CPU registers.
OS_CTX_SAVE Save all the CPU registers onto the current task stack (in the same order as in OSTaskStkInit())
Assuming our generic 32-bit CPU, OS_CTX_SAVE
would be implemented as follows.
OS_CTX_SAVE MACRO PUSH R0 PUSH R1 PUSH R2 PUSH R3 PUSH R4 PUSH R5 PUSH R6 PUSH R7 PUSH R8 PUSH R9 PUSH R10 PUSH R11 PUSH R12 PUSH R13 ENDM
OS_CTX_RESTORE
This macro is used to reverse the process done by OS_CTX_SAVE
. In other words, OS_CTX_RESTORE
loads the CPU registers from the stack in the reverse order.
OS_CTX_RESTORE Restore all the CPU registers from the new task's stack (in the reverse order that they were in OSTaskStkInit())
Assuming our generic 32-bit CPU, OS_CTX_RESTORE
would be implemented as follows.
OS_CTX_RESTORE MACRO POP R13 POP R12 POP R11 POP R10 POP R9 POP R8 POP R7 POP R6 POP R5 POP R4 POP R3 POP R2 POP R1 POP R0 ENDM
OS_ISR_ENTER
This macro allows you to simplify your assembly language ISRs. OS_ISR_ENTER
is basically the first line of code you would add to the ISR. The pseudo code for OS_ISR_ENTER
is shown below.
OS_ISR_ENTER OS_CTX_SAVE OSIntNestingCtr++; if (OSIntNestingCtr == 1) { OSTCBCurPtr->StkPtr = SP; }
Assuming our generic 32-bit CPU, OS_ISR_ENTER
would be implemented as follows. You should note that the C-like code would actually be implemented in assembly language.
OS_ISR_ENTER MACRO PUSH R0 PUSH R1 PUSH R2 PUSH R3 PUSH R4 PUSH R5 PUSH R6 PUSH R7 PUSH R8 PUSH R9 PUSH R10 PUSH R11 PUSH R12 PUSH R13 OSIntNestingCtr++; if (OSIntNestingCtr == 1) { OSTCBCurPtr->StkPtr = SP; } ENDM
OS_ISR_EXIT
This macro allows you to simplify your assembly language ISRs. OS_ISR_EXIT
is basically the last line of code you would add to the ISR. The pseudo code for OS_ISR_EXIT
is shown below.
OS_ISR_EXIT OSIntExit(); OS_CTX_RESTORE Return from Interrupt/Exception
Assuming our generic 32-bit CPU, OS_ISR_EXIT
would be implemented as follows. You should note that the C-like code would actually be implemented in assembly language.
OS_ISR_EXIT MACRO OSIntExit(); POP R13 POP R12 POP R11 POP R10 POP R9 POP R8 POP R7 POP R6 POP R5 POP R4 POP R3 POP R2 POP R1 POP R0 Return from Interrupt/Exception; ENDM