为什么我的程序在 GCC 下不进入处理程序模式?
Why does my program not enter handler mode under GCC?
我有这段代码可以在 KEIL 下运行,但不能在 GCC 下运行,我不知道为什么会这样。它测试了一些 RTX OS 功能。
处理程序:
void GenWait_IRQHandler (void) {
switch (ISR_ExNum) {
case 0: Stat_Isr = osDelay (10); break;
#if (osFeatureWait)
case 1: Stat_Isr = osWait (10); break;
#endif
}
}
上面的代码是通过从main设置pending IRQ进入的:
...
ISR_ExNum = 0; /* Test: osDelay */
NVIC_SetPendingIRQ((IRQn_Type)SWI_HANDLER);
ASSERT_TRUE (Stat_Isr == osErrorISR);
...
问题是这个 ASSERT_TRUE()
失败了,因为 Stat_Isr
不等于 osErrorISR
,它应该是因为调用 osDelay()
不允许从处理程序模式:
osStatus osDelay (uint32_t millisec) {
if (__get_IPSR() != 0U) {
return osErrorISR; // Not allowed in ISR
}
return __svcDelay(millisec);
}
正如我所说,在 KEIL 下编译时它工作正常,但在 GCC 下编译时它失败了。看起来 IPSR 在进入处理程序时没有更新,osDelay()
不知道它应该 return 错误。知道为什么会这样吗?
SWI_Handler
是软件处理程序,我在里面调用了GenWait_IRQHandler()
。
编辑:
这是作为 RTX 验证从 KEIL Packs 获得的实现,我只是试图让它在我正在使用的芯片上工作。所以即使我从 ISR 调用函数它也应该工作。
此外,正如我在评论中所写:
(来自 www.keil.com):
Interrupt Service Routines (ISR) can call some CMSIS-RTOS functions. When a CMSIS-RTOS function cannot be called from ISR context, it rejects the invocation.
然后:
Functions that cannot be called from an ISR are verifying the interrupt status and return, in case they are called from an ISR context, the status code osErrorISR
. In some implementations, this condition might be caught using the HARD FAULT vector.
编辑 2:
将优化从 -O3 减少到 -O1 修复了问题,但我仍然不知道为什么要这样优化以及如何轻松地防止编译器这样做。我知道最简单的答案是添加几个 "volatile's" 但我认为在这种情况下这不是那么简单。
感谢@kkrambo 的正确线索。问题出在指令顺序上。向 Stat_Isr
添加 volatiles 是不够的,但添加内存屏障对我来说很有效:
...
ISR_ExNum = 0; /* Test: osDelay */
NVIC_SetPendingIRQ((IRQn_Type)SWI_HANDLER);
__DMB();
ASSERT_TRUE (Stat_Isr == osErrorISR);
...
因为代码优化使得ISR在ASSERT_TRUE (Stat_Isr == osErrorISR);
之后被调用。我想现在很清楚了。
我有这段代码可以在 KEIL 下运行,但不能在 GCC 下运行,我不知道为什么会这样。它测试了一些 RTX OS 功能。
处理程序:
void GenWait_IRQHandler (void) {
switch (ISR_ExNum) {
case 0: Stat_Isr = osDelay (10); break;
#if (osFeatureWait)
case 1: Stat_Isr = osWait (10); break;
#endif
}
}
上面的代码是通过从main设置pending IRQ进入的:
...
ISR_ExNum = 0; /* Test: osDelay */
NVIC_SetPendingIRQ((IRQn_Type)SWI_HANDLER);
ASSERT_TRUE (Stat_Isr == osErrorISR);
...
问题是这个 ASSERT_TRUE()
失败了,因为 Stat_Isr
不等于 osErrorISR
,它应该是因为调用 osDelay()
不允许从处理程序模式:
osStatus osDelay (uint32_t millisec) {
if (__get_IPSR() != 0U) {
return osErrorISR; // Not allowed in ISR
}
return __svcDelay(millisec);
}
正如我所说,在 KEIL 下编译时它工作正常,但在 GCC 下编译时它失败了。看起来 IPSR 在进入处理程序时没有更新,osDelay()
不知道它应该 return 错误。知道为什么会这样吗?
SWI_Handler
是软件处理程序,我在里面调用了GenWait_IRQHandler()
。
编辑:
这是作为 RTX 验证从 KEIL Packs 获得的实现,我只是试图让它在我正在使用的芯片上工作。所以即使我从 ISR 调用函数它也应该工作。
此外,正如我在评论中所写:
(来自 www.keil.com):
Interrupt Service Routines (ISR) can call some CMSIS-RTOS functions. When a CMSIS-RTOS function cannot be called from ISR context, it rejects the invocation.
然后:
Functions that cannot be called from an ISR are verifying the interrupt status and return, in case they are called from an ISR context, the status code
osErrorISR
. In some implementations, this condition might be caught using the HARD FAULT vector.
编辑 2:
将优化从 -O3 减少到 -O1 修复了问题,但我仍然不知道为什么要这样优化以及如何轻松地防止编译器这样做。我知道最简单的答案是添加几个 "volatile's" 但我认为在这种情况下这不是那么简单。
感谢@kkrambo 的正确线索。问题出在指令顺序上。向 Stat_Isr
添加 volatiles 是不够的,但添加内存屏障对我来说很有效:
...
ISR_ExNum = 0; /* Test: osDelay */
NVIC_SetPendingIRQ((IRQn_Type)SWI_HANDLER);
__DMB();
ASSERT_TRUE (Stat_Isr == osErrorISR);
...
因为代码优化使得ISR在ASSERT_TRUE (Stat_Isr == osErrorISR);
之后被调用。我想现在很清楚了。