You are on page 1of 56

Chapter 13:

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

Application Code (test.c)

Processor independent Application Specific


implementations Configurations
•Scheduling policy
•Event flags OS_CFG.H
•Semaphores
•Mailboxes
•Event queues •Max # of tasks
•Task management •Max Queue length
•Time management
•Memory management •…

μC/OS-II port for processor specific codes


Software
Hardware
CPU Timer
3
Requirements
• C compiler
• Interrupt support and a timer
• Interrupt can be disabled and enabled by C
• Support hardware stack
• The processor can load and store the stack
pointer and other registers in memory (stack
pointer is stored in TCB)

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…

• The data type of a task’s stack and the status


register
– typedef unsigned int OS_STK
– typedef unsigned short OS_CPU_SR

7
OS_CPU.H
• Critical Method
– Type 1: enable and disable directly

– Type 2: save the interrupt status onto the stack

– Type 3: save the interrupt status into a local


variables

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

typedef unsigned short OS_CPU_SR /*define sie of CPU status register*/

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

code code code


(Task1) (Task2) (Task3)

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

(1) Mode Change Regs


(function call)

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

Interrupt Return Address Stack Growth


Regs
( 2 )

Processor Status Word

( 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 )

which register is used ( 3 ) Saved Processor Registers


'pdata'

to store pData Interrupt Return Address Stack Growth


( 2 )

Processor Status Word


( 1 )
Task start address

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'

HIGH MEMORY void OSTaskDelSelf() {


OSTaskDel(OS_PRIO_SELF);
}

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'

HIGH MEMORY OSTaskDel(OS_PRIO_SELF) {


/*…*/
/*…*/
}

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()

• It is called before unlinking the task from OS’s


internal linked list of active tasks.

• This function is called with interrupt disabled

32
OSTaskSwHook()
• Called by OSCtxSw and OSIntCtxSw

• Two variables is meaningful


– OSTCBCur //old task
– OSTCBHighRdy //new task

• This function is called with interrupt disabled

33
OSTaskStatHook()
• This function is called once every second by
OSTaskStat()

34
OSTimeTickHook()
• This function is called by OSTimeTick()

• OSTimeTick() is called before the tick start to


process in order to give your port or
application first claim to the tick.

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()

• OSInitHookBegin() is called immediately upon


entering OSInit().

• OSInitHookEnd() is called at the end of


OSInit().

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().

– You should initialize and tick interrupts in the first


task that executes following a call to 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.

• Because it is called from ISR, all registers are


properly saved.

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

• Ensure that the code compiles, assembles and


links

void main() {
OSInit();
OSStart();
}

51
Verifying of OSTaskStkInit and OSStartHighRdy

1. Disable the statistic task


OS_TASK_STAT_EN = 0
2. Step over the function OSInit() and then step
into the for OSStart()
3. Step into OSStartHighRdy
4. Switch to the “Idle” task?
– The register order  OSTaskStkInit
– “Return” to the idle thread  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 ()

• When the return from interrupt is executed,


the you should be in OS_TaskIdle.

54
Verifying of OSIntCtxSw() and OSTickISR()

• Installing of a timer interrupt handler

• Call OSTimeDly(1) to result in a context switch


to the idle thread by using 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

You might also like