如何确定执行是在线程模式下发生还是异常处于活动状态? (ARMv7-A 架构)
Posted
技术标签:
【中文标题】如何确定执行是在线程模式下发生还是异常处于活动状态? (ARMv7-A 架构)【英文标题】:How can I determin if execution takes place in thread mode or if an exception is active? (ARMv7-A architecture) 【发布时间】:2021-12-26 15:47:39 【问题描述】:我在 ARM Cortex A9 CPU 上使用 FreeRTOS,我正在拼命地试图找出是否有可能确定处理器是在执行普通线程还是中断服务例程。它是在 V7-a 架构中实现的。
我发现了一些有希望的参考暗示 ICSR 寄存器(-> VECTACTIVE 位),但这仅存在于皮质 M 家族中。 A 家族中是否也有类似的名册?我试图读出当前处理器状态寄存器 (CPSR) 中的处理器模式,但在 ISR 期间读取时,我看到模式位指示的是超级用户模式,而不是 IRQ 或 FIQ 模式。
看起来很像无法确定处理器处于哪种状态,但我还是想问一下,也许我错过了什么......
处理器有一个pl390通用中断控制器。也许可以通过读取一些寄存器来确定是否触发了中断?
如果有人能给我一个线索,我会很高兴的!
编辑1: FreeRTOS 的 IRQ Handler 将处理器切换到 Supervisor 模式:
随后切换回系统模式:
我是否可以只检查处理器是否处于超级用户模式并假设这意味着执行发生在 ISR 中,或者是否存在内核可能切换到超级用户模式而不处于 ISR 的其他情况?
编辑2: 根据要求,我将首先通过解决了解当前执行上下文的问题来添加我想要实现的解决方案的总体背景描述。
我正在编写一组用于访问外围设备的 CortexA9 和 FreeRTOS 库。其中,我想为处理器外围的可用硬件定时器实现一个库。
为了保护对硬件的访问并避免多个任务同时尝试访问硬件资源,我将互斥信号量添加到计时器库实现中。 lib 函数在调用时所做的第一件事是尝试获取 Mutex。如果失败,函数返回错误,否则继续执行。
让我们关注启动计时器的函数:
static ret_val_e TmrStart(tmr_ctrl_t * pCtrl)
ret_val_e retVal = RET_ERR_DEF;
BaseType_t retVal_os = pdFAIL;
XTtcPs * pHwTmrInstance = (XTtcPs *) pCtrl->pHwTmrInstance;
//Check status of driver
if(pCtrl == NULL)
return RET_ERR_TMR_CTRL_REF;
else if(!pCtrl->bInitialized )
return RET_ERR_TMR_UNINITIALIZED;
else
retVal_os = xSemaphoreTake(pCtrl->osSemMux_Tmr, INSTANCE_BUSY_ACCESS_DELAY_TICKS);
if(retVal_os != pdPASS)
return RET_ERR_OS_SEM_MUX;
//This function starts the timer
XTtcPs_Start(pHwTmrInstance);
(...)
有时直接在 ISR 中启动计时器会很有帮助。出现的问题是,虽然函数的其余部分会支持它,但必须将 SemaphoreTake() 调用更改为 SemaphoreTakeFromISR() - 此外,从 ISR 调用时不支持等待滴答,以避免阻塞 ISR。
为了实现适用于两种执行模式(线程模式和 IRQ 模式)的代码,我们需要更改函数以首先检查执行状态,并在此基础上调用 SemaphoreTake() 或 SemaphoreTakeFromISR(),然后再继续访问硬件。
这就是我的问题的背景。正如在 cmets 中提到的,我不想通过添加一个必须由用户在每次调用时提供的参数来实现这一点,该参数告诉函数是从线程还是 ISR 调用的,因为我想让 API 保持苗条尽可能。
我可以采用 FreeRTOS 方法并实现 TmrStart() 函数的副本,其名称为 TmrStartFromISR(),其中包含对 FreeRTOS 系统资源的 ISR 特定调用。但我宁愿避免这种情况,因为复制我的所有函数会使代码整体更难维护。
因此,通过读取一些处理器寄存器来确定执行状态将是我能想到的唯一方法。但不幸的是,与 M3 不同,显然 A9 不能轻易提供这些信息。
我想到的另一种方法是在处理异常的 FreeRTOS 的汇编代码中设置一个全局变量。在 portSAVE_CONTEXT 中可以设置它,在 portRESTORE_CONTEXT 中可以重置它。 该解决方案的缺点是该库将无法与 FreeRTOS 的官方 A9 端口一起使用,这听起来也不好。此外,如果变量在被 lib 函数检查后立即更改,您可能会遇到竞争条件问题,但我想这也是直接从处理器寄存器读取状态时的问题......可能需要将此检查包含在一个关键部分中,以防止短时间内中断。
如果有人看到我没有想到的其他解决方案,请随时提出。
还请随时讨论我迄今为止提出的解决方案。 我只是想找到最好的方法。
谢谢!
【问题讨论】:
看看ARM架构手册。您将能够查询CPU状态 你的问题有点不清楚。 CPSR 的模式位 [4:0] 应该可以很好地识别模式。如果您使用的操作系统会导致用户代码以非特权方式运行,那么您应该期望这些位中有 0x10。大多数其他事情都表明特权。 0x12 和 0x11 分别表示 IRQ 和 FIQ。 我已经在看 armv7-a 架构参考手册了。它提供了一个包含用户模式的表格,我希望在 ISR 中看到用户模式变为 IRQ 或 FIQ。但它改为处于主管模式。 主管模式是一种例外模式。通常通过 SVC 调用输入(我认为它仅用于启动端口中的调度程序)。所以我认为只要你检查你没有处于用户或系统模式,你应该是好的(除非你处于故障模式)。 【参考方案1】:On a Cortex-A processor, when an interrupt handler is triggered,处理器进入 IRQ 模式,中断禁用。这反映在 CPSR 的状态字段中。 IRQ 模式不适合接收嵌套中断,因为如果发生第二个中断,第一个中断的返回地址将被覆盖。因此,如果一个中断处理程序需要重新启用中断,它必须首先切换到超级用户模式。
通常,操作系统的中断处理程序所做的第一件事就是切换到超级用户模式。当代码到达特定的驱动程序时,处理器处于超级用户模式。所以你观察到的行为是完全正常的。
FreeRTOS interrupt handler 是一个 C 函数。它在启用中断的情况下以超级用户模式运行。如果你想知道你的代码是否在中断处理程序的上下文中运行,千万不要直接调用中断处理程序函数,当它调用关心的辅助函数时,传递一个指示调用者是谁的变量。
void code_that_wants_to_know_who_called_it(int context)
if (context != 0)
// called from an interrupt handler
else
// called from outside an interrupt handler
void my_handler1(void)
code_that_wants_to_know_who_called_it(1);
void my_handler2(void)
code_that_wants_to_know_who_called_it(1);
int main(void)
Install_Interrupt(EVENT1, my_handler1);
Install_Interrupt(EVENT2, my_handler1);
code_that_wants_to_know_who_called_it(0);
【讨论】:
感谢您的详细回复。我查看了汇编程序中的 FreeRTOS IRQ 处理程序,找到了将处理器切换到主管模式的行!有关详细信息,请参阅我的问题的编辑。 关于您建议的解决方案的问题是,在用户调用的库函数中执行时,我需要知道执行模式。如果可能的话,我真的很想避免必须相信用户在 ISR 中提供这个上下文。因此,我非常喜欢能够自行确定当前执行模式的解决方案。 @Stone 我熟悉 arm 处理器模式,但不熟悉 FreeRTOS。通常系统模式仅在内核代码访问用户数据时使用很短的时间。但是,Cortex-A 上的 FreeRTOS 始终以特权模式运行,并使用系统模式从 SVC/IRQ/FIQ 模式中获得一组单独的寄存器。所以也许系统与主管模式会给你你想要的。我不知道,因为我对 FreeRTOS 的了解不够,而且您还没有准确解释您想要什么。 “当前为中断服务”是一个模棱两可的概念,you may be asking the wrong question。 好的,我会退后一步,尝试给出整个问题的更大图景。我只是不想一开始就搞砸整个问题,但也许我真的在尝试解决一个对我的第一个问题没有帮助的问题。请参考原问题中的Edit2。 @Stone 我不确定,因为在我写这篇文章时你还没有更新问题,但我怀疑这应该是一个不同的问题。以上是关于如何确定执行是在线程模式下发生还是异常处于活动状态? (ARMv7-A 架构)的主要内容,如果未能解决你的问题,请参考以下文章
如何避免此 PDO 异常:在其他无缓冲查询处于活动状态时无法执行查询