UCOS-II移植ARM的读书笔记(12.20)续

 
之前刚开始的时候是直接看移植代码,后来看到后面实在看不下去了,转过头回去看了一个星期的内核结构,以前也看过一遍内核结构,但是有点晕晕的,现在重新看了一次清楚多了,相信回过头来看移植部分也应该更清楚了。
现在先来掌握一下关于软件中断swi的内容,这是我比较发晕的源泉
 
软中断:
中断不返回形式:void _swi(swi_num)  swi_name(arguments)
返回一个结果到R0中  int _swi(swi_num)  swi_name(arguments);最多可以返回四个结果R0-R3到一个结构struct type{ int a,b,c,d}中 type(返回类型) _value_in_regs(返回多个结果的修饰符) _swi(swi_num) swi_name(arguments);
 
在ARM中实现软中断的方法我在blog里面搜了很多文章也没有看到讲的通俗一点的,还是自己看ARM的移植代码吧
首先定义了一堆软中断的中断号,其中0和1的中断服务子程序是用汇编编写的,其他的都是在c语言编写的中断服务子程序SWI_Exception中。
__swi(0x00) void OS_TASK_SW(void);              /*  任务级任务切换函数          */
__swi(0x01) void _OSStartHighRdy(void);         /*  运行优先级最高的任务        */
__swi(0x02) void OS_ENTER_CRITICAL(void);       /*  关中断                      */
__swi(0x03) void OS_EXIT_CRITICAL(void);        /*  开中断                      */
__swi(0x40) void *GetOSFunctionAddr(int Index); /*  获取系统服务函数入口        */
__swi(0x41) void *GetUsrFunctionAddr(int Index);/*  获取自定义服务函数入口      */
__swi(0x42) void OSISRBegin(void);              /*  中断开始处理                */
__swi(0x43) int  OSISRNeedSwap(void);           /*  判断中断是否需要切换        */
__swi(0x80) void ChangeToSYSMode(void);         /*  任务切换到系统模式          */
__swi(0x81) void ChangeToUSRMode(void);         /*  任务切换到用户模式          */
__swi(0x82) void TaskIsARM(INT8U prio);         /*  任务代码是ARM代码           */
__swi(0x83) void TaskIsTHUMB(INT8U prio);       /*  任务代码是THUMB             */
比如在程序运行到调用OS_TASK_SW(void)函数时,就产生软件中断,然后就进入中断服务子程序,按照什么指令走呢?恩,就按照下面这个代码,这个代码是将软件中断异常处理程序挂接到内核的作用的,是在启动代码中实现的:
LDR   PC,SWI_Addr
 
SWI_Addr   DCD     SoftwareInterrupt
因此当产生软中断之后PC就跳到了SoftwareInterrupt,这时就算真正进入了软件异常中断处理部分了,然后就是执行下面的汇编代码
SoftwareInterrupt
        LDR     SP, StackSvc            ; 重新设置堆栈指针
        STMFD   SP!, {R0-R3, R12, LR}
        MOV     R1, SP                  ; R1指向参数存储位置
        MRS     R3, SPSR
        TST     R3, #T_bit              ; 中断前是否是Thumb状态,判断SPSR的T位是不是为零
        LDRNEH  R0, [LR,#-2]            ; 不为零即THUMB指令集: 取得Thumb状态SWI号
        BICNE   R0, R0, #0xff00          ;在THUMB指令集中软中断功能号为8位,所以取低八位即为功能号
        LDREQ   R0, [LR,#-4]            ; 为零即ARM指令集: 取得arm状态SWI号
        BICEQ   R0, R0, #0xFF000000         ;在ARM指令集中软中断功能号为24位,所以取低6位即为功能号
                                        ; r0 = SWI号,R1指向参数存储位置
        CMP     R0, #1
        LDRLO   PC, =OSIntCtxSw      ;功能号为0到OSIntCtxSw执行中断任务切换函数
        LDREQ   PC, =__OSStartHighRdy   ; SWI 0x01为第一次任务切换
        BL      SWI_Exception           ;否则进入c编写的中断服务函数
       
        LDMFD   SP!, {R0-R3, R12, PC}^
       
StackSvc           DCD     (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)
怎么进入c编写的中断服务子程序SWI_Exception呢?通过下面的申明
IMPORT  SWI_Exception               ;软中断异常处理程序,表示将c程序中的该函数挂接到此段汇编代码中
同样的道理
EXPORT  __OSStartHighRdy            
EXPORT  OSIntCtxSw                  ;中断退出时的入口,参见startup.s中的IRQ_Handler
EXPORT  SoftwareInterrupt           ;软中断入口
上面的申明是将该段汇编代码挂接到外面,因此在外部可以直接调用函数名
 
继续看OS_CPU_A.S的其他部分代码,就是两个软件异常中断处理函数OSIntCtxSw和OSStarHighRdy
OSIntCtxSw代码是中断服务子程序使得更高优先级的任务进入就绪状态后,中断返回后需要切换到该任务时调用的,这是被切换的任务的CPU寄存器的值已经在响应中断后存入了堆栈中,因此,这里不需要重复保存了直接切换任务即可,具体过程看代码
OSIntCtxSw
                                                    ;下面为保存任务环境
                                                    ;当响应软件异常中断后进入了系统模式,在上面的代码中我们可以看到,进入系统模式时保存的堆栈结构从顶到底依次是:R0,R1,R2,R3,R12,LR,而在用户模式中任务的堆栈结构应该是:OsEnterSum,CPSR,RO-12,LR,PC,所以在进行软件中断任务切换之前先要保存原来任务的堆栈结构。
        LDR     R2, [SP, #20]                       ;获取PC
        LDR     R12, [SP, #16]                      ;获取R12
        MRS     R0, CPSR
        MSR     CPSR_c, #(NoInt | SYS32Mode)
        MOV     R1, LR
        STMFD   SP!, {R1-R2}                        ;保存LR,PC
        STMFD   SP!, {R4-R12}                       ;保存R4-R12
        MSR     CPSR_c, R0
        LDMFD   SP!, {R4-R7}                        ;获取R0-R3
        ADD     SP, SP, #8                          ;出栈R12,PC
       
        MSR     CPSR_c, #(NoInt | SYS32Mode)
        STMFD   SP!, {R4-R7}                        ;保存R0-R3
       
        LDR     R1, =OsEnterSum                     ;获取OsEnterSum
        LDR     R2, [R1]
        STMFD   SP!, {R2, R3}                       ;保存CPSR,OsEnterSum
                                                    ;保存当前任务堆栈指针到当前任务的TCB
        LDR     R1, =OSTCBCur
        LDR     R1, [R1]
        STR     SP, [R1]
        BL      OSTaskSwHook                        ;调用钩子函数
                                                    ;OSPrioCur <= OSPrioHighRdy
        LDR     R4, =OSPrioCur
        LDR     R5, =OSPrioHighRdy
        LDRB    R6, [R5]
        STRB    R6, [R4]
                                                    ;OSTCBCur <= OSTCBHighRdy
        LDR     R6, =OSTCBHighRdy
        LDR     R6, [R6]
        LDR     R4, =OSTCBCur
        STR     R6, [R4]
OSIntCtxSw_1
                                                    ;获取新任务堆栈指针
        LDR     R4, [R6]
        ADD     SP, R4, #68                         ;17寄存器CPSR,OsEnterSum,R0-R12,LR,SP
        LDR     LR, [SP, #-8]
        MSR     CPSR_c, #(NoInt | SVC32Mode)        ;进入管理模式
        MOV     SP, R4                              ;设置堆栈指针
        LDMFD   SP!, {R4, R5}                       ;CPSR,OsEnterSum
                                                    ;恢复新任务的OsEnterSum
        LDR     R3, =OsEnterSum
        STR     R4, [R3]
   
        MSR     SPSR_cxsf, R5                       ;恢复CPSR
        LDMFD   SP!, {R0-R12, LR, PC }^             ;运行新任务

你可能感兴趣的:(嵌入式之路)