在SOC3210i上测试过的UCOS2的中断嵌套
ucos2任务切换,是通过系统时钟中断进行任务调度来实现的。所以系统时钟中断在中断服务程序中就有区别于其它中断的特殊之处。在这里,对于中断嵌套而言,主要分为两类:系统时钟中断和其它普通中断。
对于这两类,在中断嵌套的处理里,如果不考虑中断优先级的话,有如果三种情形:
1、系统时钟中断服务程序处理中发生普通中断嵌套
2、普通中断服务程序处理中发生系统时钟中断嵌套
3、普通中断服务程序处理中发生普通中断嵌套
需要注意的是,系统时钟中断可能会产生任务切换,所以得特别处理,实现如下:
BSS
/* 32 bit align */
ALIGN(2)
.globl exc_stack_low
exc_stack_low:
.space _EXC_STKSIZE
.globl exc_stack_hi
exc_stack_hi:
ALIGN(4) /* 16 bytes align */
exc_context:
.space 8
ALIGN(4)
exc_sp_vetor:
.space 1024
LEAF(UCOS_INTHandler)
MFC0(k0,C0_EPC) #读取中断返回地址
STORE_REG_RET(k0) #保存中断现场
la k1,exc_context #保存状态寄存器和原因寄存器
MFC0(t0,C0_STATUS)
sw t0, 0(k1)
MFC0(t0,C0_CAUSE)
sw t0, 4(k1)
la k0,OSIntNesting #将当前堆栈指针sp压入SP堆栈
lw k0,0(k0) #根据中断嵌套层数保存各层中断的堆栈指针
sll k0,2
la t0,exc_sp_vetor
addu t0,k0
sw sp,0(t0)
bne k0,0,nest #如果当前状态为嵌套,跳转到nest
nop
la sp,exc_stack_hi #改变中断执行的堆栈为exc_stack_hi
la gp,_gp #指定gp指针
nest:
jal OSIntEnter #进入中断,主要是计算中断嵌套层数
nop
jal rt_interrupt_dispatch #执行中断服务程序,传入参数为exc_context地址
move a0,k1
jal OSIntExit #出中断
nop
la k0,OSIntNesting #根据中断嵌套层数,获取堆栈指针,并由k1保存
lw k0,0(k0)
sll k0,2
la t0,exc_sp_vetor
addu t0,k0
lw k1,0(t0)
la t0,OSIntCtxSwFlag #如果需要进行任务切换,跳转到_IntCtxSw
lw t1,0(t0)
beq t1,1,_IntCtxSw
nop
move sp,k1 #sp指针指向新堆栈
RESTORE_REG_ERET() #出栈,恢复中断现场
_IntCtxSw:
sw zero,0(t0) #清空任务切换标志位
la t0,OSPrioCur #相当于OSPrioCur = OSPrioHghRdy
la t1,OSPrioHighRdy
lb t1,0(t1)
sb t1,0(t0)
jal OSTaskSwHook #调用任务切换钩子函数
nop
la t2,OSTCBHighRdy
lw t2,0(t2) #获取最高优先级任务的堆栈
la t0,OSTCBCur
lw a0,0(t0)
la t1,exc_sp_vetor
lw a1,0(t1)
sw a1,0(a0) #保存当前任务的堆栈指针
sw t2,0(t0) #相当于OSTCBCur = OSTCBHighRdy
beq k0,0,nonest
nop
move sp,k1
RESTORE_REG_ERET() #出栈,恢复中断现场
nonest:
lw sp,0(t2)
RESTORE_REG_ERET() #出栈,恢复中断现场
END(UCOS_INTHandler)
分析代码的执行过程如下:
1、当产生中断后,首先读取中断返回地址并将其同其它寄存器一起压栈,并读取状态寄存器和原因寄存器;
2、将本次中断产生前的堆栈压入堆栈指针堆栈,也就是把该堆栈指针保存到exc_sp_vetor;
3、如果本次中断是嵌套中断,跳转到5;
4、将堆栈指针从任务堆栈改变为中断堆栈,并指定全局指针gp;
5、执行OSIntEnter,OSIntNesting自加1,表示进行一次中断嵌套;
6、执行中断服务程序,传入参数为指向状态寄存器和原因寄存器的指针;
7、执行OSIntExit,OSIntNesting自减1;
8、从堆栈指针堆栈中读取出中断后的堆栈指针;
9、判断OSIntCtxSwFlag是否为1,如果为1,表示要进行任务切换,跳转到11;
10、把堆栈指针赋值给sp指针,出中断,恢复中断现场;
11、清零OSIntCtxSwFlag标志;
12、调用任务切换钩子函数;
13、将当前任务堆栈指针保存到当前任务的TCB中;
14、获取最高优先级任务的TCB,并将该TCB指向的任务堆栈保存到exc_sp_vetor的第一个元素,这样在最后一个中断返回时,就进行了任务切换;
15、如果本次中断是嵌套中断,从exc_sp_vetor中找到堆栈指针,返回中断。
16、如果本次中断是非嵌套中断,从OSTCBHighRdy获取最高优先级任务的堆栈,返回中断。
另外:
对于MIPS中断嵌套的实现,还需要在中断服务程序中打开中断嵌套,并且屏蔽该中断及比该中断优先级低的中断。