uCOS-II在ARM上的移植(2)

2325阅读 0评论2010-07-07 天海一线
分类:嵌入式

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
注意设置各模式下的栈空间。
 
上一篇:linux手机平台
下一篇:uCOS-II在ARM上的移植(3)