2、os_cpu_c.c
编写10个简单的c函数,其他OSTaskStkInit是必须的,其余9个必须定义但可以不加任何代码。
#ifndef SVCMODE
#define SVCMODE 0x13
#endif
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt)
{
OS_STK *stk;
opt = opt; /* 'opt' is not used, prevent warning */
stk = ptos; /* Load stack pointer */
*stk = (OS_STK)task; /* Entry Point */
*(--stk) = (OS_STK)task; /* lr */
*(--stk) = (INT32U)0; /* r12 */
*(--stk) = (INT32U)0; /* r11 */
*(--stk) = (INT32U)0; /* r10 */
*(--stk) = (INT32U)0; /* r9 */
*(--stk) = (INT32U)0; /* r8 */
*(--stk) = (INT32U)0; /* r7 */
*(--stk) = (INT32U)0; /* r6 */
*(--stk) = (INT32U)0; /* r5 */
*(--stk) = (INT32U)0; /* r4 */
*(--stk) = (INT32U)0; /* r3 */
*(--stk) = (INT32U)0; /* r2 */
*(--stk) = (INT32U)0; /* r1 */
*(--stk) = (INT32U)pdata;/* r0 : argument */
*(--stk) = (INT32U)(SVCMODE|0x40); /* PSR,fiq disabled */
*(--stk) = (INT32U)(SVCMODE|0x40); /* SPSR,fiq disabled */
return (stk);
}
s3c2410采用小端存储模式,堆栈向下生长(高-->低)
SVC模式运行,禁用fiq
3、os_cpu_a.S
主要编写4个汇编函数:
OSStartHighRdy();//使就绪态中优先级最高的任务运行
OSCtxSw();//任务切换函数
OSIntCtxSw();//中断状态下的任务切换函数
OSTickISR();//时钟节拍服务函数
#*****************************************************************************
# START MULTITASKING
# void OSStartHighRdy(void)
#
# Note : OSStartHighRdy() MUST:
# a) Call OSTaskSwHook() then,
# b) Set OSRunning to TRUE,
# c) Switch to the highest priority task.
#******************************************************************************
.text
#define SVCMODE 0x13
#define IRQMODE 0x12
.extern OSTaskSwHook
.extern OSRunning
.extern OSTCBHighRdy
.global OSStartHighRdy
OSStartHighRdy:
bl OSTaskSwHook
ldr r4, =OSRunning @ Indicate that multitasking has started
mov r5, #1
strb r5, [r4]
ldr r4,=OSTCBHighRdy @ Get highest priority task TCB address
ldr r4,[r4] @ get stack pointer
ldr sp,[r4] @ switch to the new stack
ldmfd sp!,{r4} @ pop new task s spsr
msr SPSR_cxsf,r4
ldmfd sp!,{r4} @ pop new task s psr
msr CPSR_cxsf,r4
ldmfd sp!,{r0-r12,lr,pc} @ pop new task s r0-r12,lr & pc
#******************************************************************************
# PERFORM A CONTEXT SWITCH (From task level)
# void OSCtxSw(void)
#
# Note(s): Upon entry,
# OSTCBCur points to the OS_TCB of the task to suspend
# OSTCBHighRdy points to the OS_TCB of the task to resume
#
#******************************************************************************
.extern OSTCBCur
.extern OSTaskSwHook
.extern OSTCBHighRdy
.extern OSPrioCur
.extern OSPrioHighRdy
.global OSCtxSw
OSCtxSw:
@ Special optimised code below:
stmfd sp!,{lr} @ push pc (lr should be pushed in place of PC)
stmfd sp!,{r0-r12,lr} @ push lr & register file
mrs r4,cpsr
stmfd sp!,{r4} @ push current psr
mrs r4,spsr
stmfd sp!,{r4} @ push current spsr
@ OSPrioCur = OSPrioHighRdy
ldr r4,=OSPrioCur
ldr r5,=OSPrioHighRdy
ldrb r6,[r5]
strb r6,[r4]
@ Get current task TCB address
ldr r4,=OSTCBCur
ldr r5,[r4]
str sp,[r5] @ store sp in preempted tasks s TCB
bl OSTaskSwHook @ call Task Switch Hook
@ Get highest priority task TCB address
ldr r6,=OSTCBHighRdy
ldr r6,[r6]
ldr sp,[r6] @ get new tasks stack pointer
@ OSTCBCur = OSTCBHighRdy
str r6,[r4] @ set new current task TCB address
ldmfd sp!,{r4} @ pop new tasks spsr
msr SPSR_cxsf,r4
ldmfd sp!,{r4} @ pop new tasks psr
msr CPSR_cxsf,r4
ldmfd sp!,{r0-r12,lr,pc} @ pop new tasks r0-r12,lr & pc
#***************************************************************************
# PERFORM A CONTEXT SWITCH (From an ISR)
# void OSIntCtxSw(void)
#
# Note(s): This function only flags a context switch to the ISR Handler
#
#***************************************************************************
.extern OSIntCtxSwFlag
.global OSIntCtxSw
OSIntCtxSw:
@ OSIntCtxSwFlag = True
ldr r0,=OSIntCtxSwFlag
mov r1,#1
str r1,[r0]
mov pc,lr
#**************************************************************************
# IRQ HANDLER
#
# This handles all the IRQs
# Note: FIQ Handler should be written similar to this
#
#**************************************************************************
.extern C_IRQHandler
.extern OSIntEnter
.extern OSIntExit
.extern OSIntCtxSwFlag
.extern OSTCBCur
.extern OSTaskSwHook
.extern OSTCBHighRdy
.extern OSPrioCur
.extern OSPrioHighRdy
.extern C_IRQHandler
.equ NOINT,0xc0
.global UCOS_IRQHandler
UCOS_IRQHandler:
stmfd sp!,{r0-r3,r12,lr}
bl OSIntEnter
bl C_IRQHandler
bl OSIntExit
ldr r0,=OSIntCtxSwFlag
ldr r1,[r0]
cmp r1,#1
beq _IntCtxSw
ldmfd sp!,{r0-r3,r12,lr}
mov pc,lr @subs pc,lr,#4
_IntCtxSw:
mov r1,#0
str r1,[r0] @ OSIntCtxSwFlag=0
ldmfd sp!,{r0-r3,r12,lr} @pop registers
stmfd sp!,{r0-r3} @push r0-r3 temporarily
mov r0,sp
add sp,sp,#16
msr cpsr_c,#SVCMODE|NOINT @ svc mode,disable interupts
mov r1,sp
mov r2,lr
msr cpsr_c,#IRQMODE|NOINT @irq mode
stmfd r1!,{r2} @ push old task's pc
stmfd r1!,{r4-r12,lr} @ push old task's lr,r12-r4
mov r4,r0 @ Special optimised code below
mov r6,r1
ldmfd r4!,{r0-r3}
stmfd r6!,{r0-r3} @ push old task's r3-r0
mrs r3,spsr
stmfd r6!,{r3} @ push old task's psr
stmfd r6!,{r3} @ push old task's spsr
add sp,sp,#4*14
msr cpsr_c,#SVCMODE|NOINT @ enter svc mode
mov sp,r6 @ store new sp
@ OSPrioCur = OSPrioHighRdy
ldr r4,=OSPrioCur
ldr r5,=OSPrioHighRdy
ldrb r5,[r5]
strb r5,[r4]
@ Get current task TCB address
ldr r4,=OSTCBCur
ldr r5,[r4]
str sp,[r5] @ store sp in preempted tasks's TCB
bl OSTaskSwHook @ call Task Switch Hook
@ Get highest priority task TCB address
ldr r6,=OSTCBHighRdy
ldr r6,[r6]
ldr sp,[r6] @ get new task's stack pointer
@ OSTCBCur = OSTCBHighRdy
str r6,[r4] @ set new current task TCB address
ldmfd sp!,{r4} @ pop new task's spsr
msr SPSR_cxsf,r4
ldmfd sp!,{r4} @ pop new task's psr
msr CPSR_cxsf,r4
ldmfd sp!,{r0-r12,lr,pc} @ pop new task's r0-r12,lr & pc
#**************************************************************************
# CRITICAL SECTION METHOD 3 FUNCTIONS
#
# Description: Disable/Enable interrupts by preserving the state of interrupts. Generally speaking you
# would store the state of the interrupt disable flag in the local variable 'cpu_sr' and then
# disable interrupts. cpu_sr is allocated in all of uC/OS-II s functions that need to
# disable interrupts. You would restore the interrupt disable state by copying back cpu_sr
# into the CPU s status register.
#
# OS_CPU_SR OSCPUSaveSR()
# Arguments : none
#
# Returns : OS_CPU_SR
#
# OSCPURestoreSR(OS_CPU_SR cpu_sr)
# Arguments : OS_CPU_SR
#
# Returns : none
#
#***************************************************************************
.global OSCPUSaveSR
OSCPUSaveSR:
mrs r0,CPSR
orr r1,r0,#NOINT
msr CPSR_c,r1
mov pc,lr
.global OSCPURestoreSR
OSCPURestoreSR:
msr CPSR_c,r0
mov pc,lr
我们主要谈一下时钟节拍中断和中断ISR中的任务切换。
s3c2410有5个定时器,其中定时器4没有输出,常用作系统时钟,linux系统即使用定时器4作为自己的时钟节拍。
void startTimer4()//PCLK=50MHz
{
/* prescaler for Timer 4 is 16 */
TCFG0 = 0x0f00;
/* load value for 10 ms timeout */
TCNTB4 = 15625;
/* manual update of Timer 4 */
TCON = 0x600000;
/* auto load, start Timer 4 */
TCON = 0x500000;
}
//开启定时器4中断
void startTimeTick()
{
INTMOD = 0; // all interupts are IRQ
INTMSK &= ~(1<<14);//start timer4 interrupt
}
//中断服务程序
void C_IRQHandler()
{
if(INTOFFSET==14)//定时器4中断
OSTimeTick();
SRCPND=1< INTPND=INTPND;
}
s3c2410的异常向量表:
.text
.global _start
_start:
b Reset
ldr pc,=HandleUndef
ldr pc,=HandleSWI
ldr pc,=HandlePrefetchAbort
ldr pc,=HandleDataAbort
ldr pc,=HandleNotUsed
ldr pc,=HandleIRQ
ldr pc,=HandleFIQ
.extern UCOS_IRQHandler
HandleIRQ:
sub lr,lr,#4 @返回地址
stmdb sp!,{r0-r12,lr} @压栈
ldr lr,=int_return @中断服务程序返回地址
ldr pc,=UCOS_IRQHandler @转去执行中断服务程序
int_return: @中断服务程序返回后
ldmia sp!,{r0-r12,pc}^ @返回被中断的程序,^表示恢复cpsr
中断服务程序可能不会返回,这是就必须调整堆栈了。
下面分析UCOS_IRQHandler
UCOS_IRQHandler:
stmfd sp!,{r0-r3,r12,lr} @压栈
bl OSIntEnter @记录中断嵌套
bl C_IRQHandler @执行ISR程序
bl OSIntExit @退出中断嵌套,若嵌套全部退出且有更高级的任务就绪,
置OSIntCtxSwFlag为1(由OSIntCtxSw实现)
ldr r0,=OSIntCtxSwFlag
ldr r1,[r0]
cmp r1,#1
beq _IntCtxSw @如果要切换转到_IntCtxSw
ldmfd sp!,{r0-r3,r12,lr} @没有切换,弹出堆栈
mov pc,lr @返回到HandleIRQ的int_return处
_IntCtxSw: @任务切换
mov r1,#0
str r1,[r0] @ 清OSIntCtxSwFlag=0
ldmfd sp!,{r0-r3,r12,lr} @pop registers
stmfd sp!,{r0-r3} @push r0-r3 temporarily
mov r0,sp @使用r0记录r3被压入的位置
add sp,sp,#16 @返回sp至未压栈前
msr cpsr_c,#SVCMODE|NOINT @ svc mode,disable interupts
mov r1,sp @SVC模式下,被中断任务的sp->r1和lr->r2
mov r2,lr
msr cpsr_c,#IRQMODE|NOINT @irq mode,返回irq模式
#向被中断任务的栈中压入CPU寄存器值
stmfd r1!,{r2} @ push old task's pc
stmfd r1!,{r4-r12,lr} @ push old task's lr,r12-r4
mov r4,r0 @ 压入r0-r3后的sp位置->r4
mov r6,r1 @ 原任务的sp
ldmfd r4!,{r0-r3} @ 弹出r0-r3
stmfd r6!,{r0-r3} @ push old task's r3-r0
mrs r3,spsr
stmfd r6!,{r3} @ push old task's cpsr
stmfd r6!,{r3} @ push old task's spsr
add sp,sp,#4*14 @丢弃IRQ中断时压栈的r0-r12,lr共14个栈空间的内容
msr cpsr_c,#SVCMODE|NOINT @ enter svc mode
mov sp,r6 @ store new sp
@ OSPrioCur = OSPrioHighRdy
ldr r4,=OSPrioCur
ldr r5,=OSPrioHighRdy
ldrb r5,[r5]
strb r5,[r4]
@ Get current task TCB address
ldr r4,=OSTCBCur
ldr r5,[r4]
str sp,[r5] @ store sp in preempted tasks's TCB
bl OSTaskSwHook @ call Task Switch Hook
@ Get highest priority task TCB address
ldr r6,=OSTCBHighRdy
ldr r6,[r6]
ldr sp,[r6] @ get new task's stack pointer
@ OSTCBCur = OSTCBHighRdy
str r6,[r4] @ set new current task TCB address
ldmfd sp!,{r4} @ pop new task's spsr
msr SPSR_cxsf,r4
ldmfd sp!,{r4} @ pop new task's psr
msr CPSR_cxsf,r4
ldmfd sp!,{r0-r12,lr,pc} @ pop new task's r0-r12,lr & pc
4、bsp文件start.S
完成一些基本的初始化操作,类似于uboot中的start.S
注意设置各模式下的栈空间。