Closed sukifeng closed 6 years ago
@aozima 查下这个问题,是否可能在一些极端情况下会出现这样的问题。
楼主的分析感觉有可能,需要仔细分析并验证。 如果问题确认,暂时把 更新FPCA 移动到 pendsv_exit之前应该就可以了。 楼主可以先这样修改测试一下!
IF {FPU} != "SoftVFP"
ORR lr, lr, #0x10 ; lr |= (1 << 4), clean FPCA.
CMP r3, #0 ; if(flag_r3 != 0)
BICNE lr, lr, #0x10 ; lr &= ~(1 << 4), set FPCA.
ENDIF
pendsv_exit
; restore interrupt
MSR PRIMASK, r2
ORR lr, lr, #0x04
BX lr
ENDP
同时可以把pendsv_exit按如下写两份,然后在后面下断点。 如果发生楼主所描述的场景,会有一定概率进入后面的pendsv_exit,这样就能佐证楼主的猜测。
IF {FPU} != "SoftVFP"
ORR lr, lr, #0x10 ; lr |= (1 << 4), clean FPCA.
CMP r3, #0 ; if(flag_r3 != 0)
BICNE lr, lr, #0x10 ; lr &= ~(1 << 4), set FPCA.
ENDIF
;pendsv_exit
; restore interrupt
MSR PRIMASK, r2
ORR lr, lr, #0x04
BX lr
pendsv_exit
; restore interrupt 在这里下断点
MSR PRIMASK, r2
ORR lr, lr, #0x04
BX lr
ENDP
@aozima 我目前就是按照下面这样修改的,和你说的一样. 测试1天了,还没有出现问题.
IF {FPU} != "SoftVFP"
ORR lr, lr, #0x10 ; lr |= (1 << 4), clean FPCA.
CMP r3, #0 ; if(flag_r3 != 0)
BICNE lr, lr, #0x10 ; lr &= ~(1 << 4), set FPCA.
ENDIF
pendsv_exit
; restore interrupt
MSR PRIMASK, r2
ORR lr, lr, #0x04
BX lr
ENDP
你说的验证方法我也试了,确实会进入pendsv_exit. 我昨天也验证了确实存在进入PendSV_handler后变量rt_thread_switch_interrupt_flag为0的情况. 从理论上分析也确实存在PendSV被打断的可能,只是时间窗口非常短,不容易碰到, 而我的应用恰恰中断非常多而且频繁。
在PendSV_Handler前面加延时,在shell里输入字符(会进入串口中断),就比较容易进入pendsv_exit了。
PendSV_Handler PROC
EXPORT PendSV_Handler
MOV R0, #0xF000
__nop
NOP
SUBS R0, R0, #1
CMP R0, #0
BGT __nop
; disable interrupt to protect context switch
MRS r2, PRIMASK
CPSID I
看来这个问题确认了,楼上的方法加大了时间窗口,提高了问题复现的概率。
开发环境:MDK5.22
如果PendSV触发后,在PendSV中断标志被清除到执行中断关闭语句这非常短的时间内,被其他中断打断,在该中断中释放信号量并进行任务调度,此时PendSV的中断标志再次置位,也就是说PendSV中断服务程序会执行2次,在第二次执行时变量rt_thread_switch_interrupt_flag为0,所以程序直接跳转到pendsv_exit。
由于此时寄存器R3的值不确定,PendSV退出时CONTROL.FPCA的值也就不确定,进而导致CPU出栈异常,例如压栈时压入了浮点寄存器,但出栈时未弹出,后续程序的堆栈错乱,PC值非法,进入Hardfault.