之前刚开始的时候是直接看移植代码,后来看到后面实在看不下去了,转过头回去看了一个星期的内核结构,以前也看过一遍内核结构,但是有点晕晕的,现在重新看了一次清楚多了,相信回过头来看移植部分也应该更清楚了。
现在先来掌握一下关于软件中断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 }^ ;运行新任务