天天看点

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

__swi(0x83) void TaskIsTHUMB(INT8U prio);      

比如在程序运行到调用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 }^             ;运行新任务