Professional Documents
Culture Documents
Porting μC/OS-II
1
Outline
• Requirements
• Hardware
• Software
• Tasks of Porting µC/OS-II
• OS_CPU_C.H
• OS_CPU_C.C
• OS_CPU_A.ASM
• Testing a port
2
μC/OS-II hardware/software architecture
4
Porting Tasks of µC/OS-II
• Setting the value of 2 #define constants (OS_CPU.H)
– OS_CRITICAL_METHOD
– OS_STK_GROWTH
• Declaring 11 data types (OS_CPU.H)
– OS_STK
• Declaring 1~3 #define macros (OS_CPU.H)
– Critical section
• Writing 10 simple functions in C (OS_CPU_C.C)
– Hooks
– OSTaskStkInit
• Writing 4 assembly language functions (OS_CPU_A.ASM)
– Context switch (starting, ctx by ISR, ctx by AP)
– TickISR procedures
5
Development Tools
• A C compiler that generates reentrant code
(each function has its stack space)
• A linker which is used to combine object files
– Resolve references within these modules
• A locator which allow you to place the code
and data anywhere in the memory map of the
target processor
6
OS_CPU.H
• Compiler-Specific Data Type
– BOOLEAN, INT8U, INT8S…
7
OS_CPU.H
• Critical Method
– Type 1: enable and disable directly
8
OS_CPU.H
#if OS_CRITICAL_METHOD == 1
#define OS_ENTER_CRITICAL() asm CLI
#define OS_EXIT_CRITICAL() asm STI
#endif
#if OS_CRITICAL_METHOD == 2
#define OS_ENTER_CRITICAL() asm {PUSHF; CLI}
#define OS_EXIT_CRITICAL() asm POPF
#endif
#if OS_CRITICAL_METHOD == 3
#define OS_ENTER_CRITICAL() (cpu_sr = OSCPUSaveSR())
#define OS_EXIT_CRITICAL() (OSCPURestoreSR(cpu_sr))
#endif
X86 port
9
OS_CPU.H
• OS_TASK_SW()
– a macro that is invoked when µC/OS-II switches from a
low-priority task to the highest-priority task.
• OS_TASK_SW() is called from user program.
– OSIntExit() is called from ISR
• In this procedure (OS_Task_SW())…
– Throw a software trap
– The interrupt handler should vector to the assembly
language function OSCtxSw()
X86 port
#define OS_TASK_SW() asm INT uCOS
10
OS_CPU.H
11
OS_CPU_C.C
• OSTaskStkInit()
• OSTaskCreateHook()
• OSTaskDelHook()
• OSTaskSwHook()
• OSTaskIdleHook()
• OSTaskStatHook()
• OSTimeTickHook()
• OSInitHookBegin()
12
OSTaskStkInit()
• This function is called by OSTaskCreate() and
OSTAskCreateExt() to initialize the stack frame
of a task as an interrupt has just occurred and
all the processor registers have been pushed
onto that stack.
13
main()
main2()
fun1() main3()
os_start()
main1()
code code code
(Task1) (Task2) (Task3)
xxx
ooo
zzz Regs Regs Regs
kkk
ggg
fff PSW PSW PSW
qqqqq PC PC PC
Stack
Ret. adrs Ret. adrs
Stack Ret. adrs
Stack
Stack pdata
(Task3)
(Task1)
pdata pdata
(Task3)
(main)
startHighRdy
sp
tcb = getHPT()
asm {sp = tcb->sp}
pc
asm {popa}
asm {iret}
14
main2()
main1()
xxx main3()
xxx
xxx
Regs Regs
local
PSW PSW
variables
PC PC
Ret. adrs Ret. adrs
Stack Ret. adrs
Stack
pdata pdata
(Task3) pdata
(Task3)
startHighRdy
sp
Regs tcb = getHPT()
asm {sp = tcb->sp}
pc
PSW asm {popa}
PC asm {iret}
15
Context switch
TCB TCB Stack Stack
(task2) (task1) (task1) (task2)
OSTCBStkPtr OSTCBStkPtr
main2() {
myFunction() {
xxx Regs
OSMBoxPost()
yyy
zzz
} PSW
PC
PSW
OSMboxPost() {
PC
//...
OS_Sched() Ret. adrs Ret. adrs
OSPrioHighRdy= //Get pointer to HPT ready to run pdata pdata
INT 0x80 //OS_TASK_SW
//... (2) Exception
} (software interrupt)
(3)
(4)
PUSH
POP PSW
PSW
PUSH
POP PC+4
PC+4
//(4) ISR (interrupt service routine)
PUSHA
OSTCBCur->OSTCBStkPtr = SP
SP = OSTCBHighRdy->OSTCBStkPtr
POPA
16
IRET
Quiz
registers
PSW & PC
Ret. addr
pdata 17
Pseudo code for OSTaskStkInit()
registers
PSW & PC
Ret. addr
pdata 18
OSTaskStkInit()
• Under μC/OS-II, a task looks like a C function
with one argument.
– Push the argument onto the stack
– Pass the argument in one or more registers
19
OSTaskStkInit()-
pdata passed to the stack
LOW MEMORY
Stack Pointer
( 4 )
( 3 )
Saved Processor Registers
( 1 )
Task start address
PSW & PC
'pdata'
HIGH MEMORY
Ret. adrs
pdata
20
BC45
5dc6
0022
0080
5d7A
0031
21
Ret.
ADRS
bp
22
OSCTXSW
23
OSTaskStkInit()-
pdata passed to the stack
LOW MEMORY
Stack Pointer
( 4 )
( 3 )
Saved Processor Registers
( 2 )
Interrupt Return Address Stack Growth
Processor Status Word
( 1 )
Task start address
'pdata'
HIGH MEMORY
24
OSTaskStkInit() –
passed in register
• Because the compiler
passed arguments to a
function in registers, LOW MEMORY
Stack Pointer
we need to find out ( 4 )
HIGH MEMORY
25
Process Termination
26
Stack layout
LOW MEMORY
Stack Pointer
( 4 )
( 3 )
Saved Processor Registers
( 2 )
Interrupt Return Address Stack Growth
Processor Status Word
( 1 )
@OSTaskDelSelf
Task start address
'pdata'
27
Stack layout
LOW MEMORY
Stack Pointer
( 4 )
( 3 )
Saved Processor Registers
( 2 )
Interrupt Return Address Stack Growth
Processor Status Word
( 1 )
@OSTaskDel
Task start address
'pdata'
28
Stack layout
LOW MEMORY
Stack Pointer
( 4 )
( 3 )
Saved Processor Registers
( 2 )
Interrupt Return Address Stack Growth
Processor Status Word
( 1 )
@OSTaskDel
Task start address
'pdata'
0xFF
HIGH MEMORY OSTaskDel(OS_PRIO_SELF) {
/*…*/
/*…*/
}
29
Hook Functions
• If (OS_CPU_HOOK_EN == 1)
– Hook functions are in OS_CPU_C.C
• If (OS_CPU_HOOK_EN == 0)
– In other files
30
OSTaskCreateHook() &
OSTaskInitHook()
• These functions are called when…
– After: OS setting up most of OS_TCB
– Before the OS_TCB is linked to the active task
chain and before the task is made ready to run
• Interrupt has been enabled
• OSTaskInitHook is called immediately before
OSTaskCreateHook
31
OSTaskDelHook()
• Called by OSTaskDel()
32
OSTaskSwHook()
• Called by OSCtxSw and OSIntCtxSw
33
OSTaskStatHook()
• This function is called once every second by
OSTaskStat()
34
OSTimeTickHook()
• This function is called by OSTimeTick()
35
OSTaskIdleHook()
• We can bring the processor to the power
saving mode by placing “stop” instruction here.
void OSTaskIdleHook(void) {
asm(“STOP”);
}
36
OSInitHookBegin()/ OSInitHookEnd()
37
OS_CPU_A.ASM
• OSStartHighRdy()
• OSCtxSw()
• OSIntCtxSw()
• OSTickISR()
38
OSStartHighRdy()
• This function is called by OSStart() to start the
highest priority task ready-to-run.
• OSStartHighRdy() assumes that OSTCBHighRdy()
points to the TCB of the task with the highest priority.
– OSTCBHighRdy() is set by OSStart()
• The function only does half a context switch
– Restoring the registers of the highest priority task
– Not saving the register of the previous task
39
Pseudocode
1. Call OSTaskSwHook
2. Set OSRunning = true
3. Get the stack pointer
stack pointer = OSTCBHighRdy -> OSTCBStkPtr;
4. Restore all processor registers from the task’s
stack
5. Execute “Return from interrupt”
40
OSCtxSw()
• A task level context switch is accomplished by issuing
a software interrupt instruction.
• The sequence of events that leads µC/OS-II to vector
to OSCtxSw() is as follows:
– The current task calls a system call which causes a higher
priority task ready to run. At the end of the service call, the
OS calls OSSched().
– OSSched() loads the address of the highest priority task
into OSTCBHighRdy and then executes the software
interrupt or trap instruction by invoking the macro
OS_TASK_SW()
#define OS_TASK_SW() asm INT uCOS 41
Pseudocode
The machine has saved the return address and status word
void OSCtxSw(void)
{
Save processor registers;
Save the current task’s stack pointer into\\
the current task’s OS_TCB:
OSTCBCur->OSTCBStkPtr = Stack pointer;
Call user definable OSTaskSwHook();
OSTCBCur = OSTCBHighRdy;
OSPrioCur = OSPrioHighRdy;
Get the stack pointer of the task to resume:
Stack pointer = OSTCBHighRdy->OSTCBStkPtr;
Restore all processor registers from the new task’s stack;
Execute a return from interrupt instruction;
}
42
OSTickISR()
• You MUST enable ticker interrupts AFTER
multitasking has started, i.e. after calling
OSStart().
43
Pseudocode
void OSTickISR(void)
{
Save processor registers;
Call OSIntEnter() or increment OSIntNesting;
if (OSIntNesting == 1)
OSTCBCur->OSTCBStkPtr = stack pointer
Call OSTimeTick();
Call OSIntExit();
Restore processor registers;
Execute a return from interrupt instruction;
}
44
OSIntExit()
void OSIntExit (void) {
if (OSIntNesting == 0) {
if (OSLockNesting == 0) {
OSIntExitY = OSUnMapTbl[OSRdyGrp];
OSPrioHighRdy = (INT8U)((OSIntExitY << 3) +
OSUnMapTbl[OSRdyTbl[OSIntExitY]]);
if (OSPrioHighRdy != OSPrioCur) {
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
OSIntCtxSw();
}
}
}
OS_EXIT_CRITICAL();
}
}
45
OSIntCtxSw()
• This function is called by OSIntExit() to
perform context switch from ISR.
46
Pseudocode
void OSIntCtxSw(void)
{
Call user-definable OSTaskSwHook();
OSTCBCur = OSTCBHighRdy;
OSPrioCur = OSPrioHighRdy;
Get the stack pointer of the task to resume:
Stack pointer = OSTCBHighRdy->OSTCBStkPtr;
Restore all processor registers from the new task’s stack;
Execute a “return from interrupt” instruction;
}
47
Pseudocode – an Old Version
void OSIntCtxSw(void)
{
Adjust the stack pointer to remove calls to:
OSIntExit(),
OSIntCtxSw() and possibly the push of the processor status word;
Save the current task’s stack pointer into the current task’s OS_TCB:
OSTCBCur->OSTCBStkPtr = Stack pointer;
Call user definable OSTaskSwHook();
OSTCBCur = OSTCBHighRdy;
OSPrioCur = OSPrioHighRdy;
Get the stack pointer of the task to resume:
Stack pointer = OSTCBHighRdy->OSTCBStkPtr;
Restore all processor registers from the new task’s stack;
Execute a return from interrupt instruction;
}
48
49
Testing of a Port
• Steps
– Ensure that the code compiles, assembles and
links
– Verify OSTaskStkInit and OSStartHighRdy
– Verify OSCtxSw
– Verify OSIntCtxSw and OSTickISR
50
Code Compiling, Assembling and Linking
void main() {
OSInit();
OSStart();
}
51
Verifying of OSTaskStkInit and OSStartHighRdy
52
Verifying of OSCtxSw()
void main() {
OSInit();
OSTaskCreate(TestTask, NULL, stack, 0);
OSStart();
}
Void TestTask(void *pData) {
while(1) {
OSTimeDly(1);
}
}
53
Verifying of OSCtxSw()
• OSTimeDly() OS_Sched() OSCtxSw ()
54
Verifying of OSIntCtxSw() and OSTickISR()
55
Verifying of OSIntCtxSw() and OSTickISR()
void main(void) {
OSInit();
install the clock tick ISR;
OSTaskCreate(TestTask);
OSStart();
}
Void TestTask(void *pData) {
disable interrupt
initialize the clock tick interrupt;
enable interrupt
while (1) {
OSTimeDly(1);
printf(“%d”, OSTimeGet());
}
}
56